Cached Links to Scripts becoming NULL during play

I’m having a bit of trouble with my methods of linking, in that they are losing their connection after start… :sweat_smile:

I have three scripts that are all talking to each other:

LevelStatus, PlayerStatus, TargetController.

They all cache each others link at function Start().

They all pass the Debug.Log check so they are (theoretically) linked and cached at Start().

When I call from TargetController.js a function in PlayerStatus.js that checks a variable in LevelStatus.js, I get a null exception “no link to LevelStatus”.

I can force a link by refinding LevelStatus within the function, but that’s inefficient. I feel that I’ve made the link and cached it in function Start() and shouldn’t have to do it again.

I’ve bugged the sheets off the folks in the IRC channel, and there’s been no light in the wood shed so far.

Ultimately what I’m doing is:

Here are the relevant bits of code:

LevelStatus.js contains some level based variables. This script is overkill right now, but design-wise should be where this code is for futureproofing.

LevelStatus.js


var setLives : int;
var setMaxFuel : int;
var setHealth : int;

//var playerStatus : PlayerStatus; // don't need the link in this script right now

function Start () {
	Debug.Log("LevelStatus.js Starting");
}

function OnDisable ()
{
	Debug.Log("LevelStatus.js STOPPED");
}

function Reset () {
	setLives = 5;
	setMaxFuel = 1000;
	setHealth =100;
}

(The Debug.Log is from troubleshooting…)

PlayerStatus.js is where I keep most of the immediate variables and functions related to the player state.

PlayerStatus.js


static var LTOFuel : float;
static var LTOHealth : float;

private var shipController : ShipController;
private var levelStatus : LevelStatus;

//var shipController : ShipController;
//var levelStatus : LevelStatus;


function Start () {
	shipController = FindObjectOfType(ShipController);
	if (!shipController)
		Debug.Log("Player Status Start: No link to Spaceship Controller");
		
	levelStatus = FindObjectOfType(LevelStatus);
	if (levelStatus == null)
		Debug.Log("Player Status Start: No link to Level Status");
		
	LTOFuel = levelStatus.setMaxFuel;
		Debug.Log("LTOFuel: " + LTOFuel);
		Debug.Log("levelStatus.setMaxFuel: " + levelStatus.setMaxFuel);
	LTOHealth = levelStatus.setHealth;
	LTOLives = levelStatus.setLives;
}

function ReFuel (reFuelValue : int) {
	LTOFuel += reFuelValue;
	
	levelStatus = FindObjectOfType(LevelStatus);
	if (levelStatus == null)
		Debug.Log("Player Status Refuel: No link to Level Status");
	
	if (levelStatus) {
		if (LTOFuel > levelStatus.setMaxFuel) {
			LTOFuel = levelStatus.setMaxFuel;
		}
	}
}

function Repair (repairlValue : int) {
	LTOHealth += repairlValue;
	
	levelStatus = FindObjectOfType(LevelStatus);
	if (levelStatus == null)
		Debug.Log("Player Status Repair: No link to Level Status");
	
	if (levelStatus) {
		if (LTOHealth > levelStatus.setHealth) {
			LTOHealth = levelStatus.setHealth;
		}
	}
}

TargetController makes function calls to PlayerStatus depending upon several tests.

TargetController.js


private var playerStatus : PlayerStatus;
private var levelStatus : LevelStatus;
private var announcement : GUIAnnouncement;
private var refuelRation : int;
private var repairRation : int;

function Start () {
	
	playerStatus = FindObjectOfType(PlayerStatus);
	if (!playerStatus)
		Debug.Log("LZC: No link to Player Status");
		
	levelStatus = FindObjectOfType(LevelStatus);
	if (!levelStatus)
		Debug.Log("LZC: No link to Level Status");
		
	announcement = FindObjectOfType(GUIAnnouncement);
	if (!announcement)
		Debug.Log("LZC: No link to GUI Announcement");

}

//SNIP! removed lots of code, including the tests to check target success and if tests are true, call the function "TargetSuccess" below:

function TargetSuccess () {
	
//	announce the status
	announcement.textMessage = "Success";
	yield WaitForSeconds (1);
	announcement.textMessage = "Success\n\nYou did it!";

//	do the work
	RefuelShip ();
	RepairShip ();

//	reset the announcement
	yield WaitForSeconds (3);
	announcement.textMessage = "";
}

function RefuelShip () {
	playerStatus.ReFuel(refuelRation);
}

function RepairShip () {
	playerStatus.Repair(repairRation);
}

The odd thing about this problem is it’s intermittent and crops up unexpectedly.

I’ve just done some additional work to the game, and not changed any of the relevant code related to two other functions called in this (tangle?) of communication. They are OnDeath () and OnReset (), two often used and must have functions! It’s not like I could have overlooked them.

Not listed here, but my ship controller checks in with player status, and player status checks back in with ship controller, and now all of a sudden (and I can’t figger out why) I’ve lost connection between the player status and ship controller.

Again - I can force it to reconnect with a FindObjectOfType in the now disconnected function, but I shouldn’t have to.

Any ideas why a cached link, linked on Start(), would lose that connection during game play?

COULD this be something in the way the game engine handles things? I forced the connections with a FindObjectOfType just before the otherScript.Function() call, and it worked. As they are attached to the same object, I also tried a GetComponent before the otherScript.Function(), and IT worked…

THEN I restarted Unity - then I quoted these forced links out again, and it’s back to working like it should… The only difference I can see is that I’ve restarted Unity. Could Unity hold something that would break a connection?

The current symptom seems that it loses connection if there is a ApplicationLoadLevel() involved. I only have 1 playable level, but I have a StartUp and GameOver screen. If I start a new game, use all 5 lives, the game loads GameOver.unity (index2), which has a button to play again,which loads Start Menu.unity (index 0), which has a play button which loads Level 1.unity (index 1).

The behaviour oddly seems to be:

:shock:

So - if I load the game from Unity, it fails on the SECOND try.

The bookend levels only have gui scripts attached to the main camera, and nothing else.

Why would initializing these scripts directly in Level 1 cause me to lose connection between them when it’s RELOADED later?

I’ve lost my mind.

SOLUTION?

I’ll let the smarter people out there decide.

Some additional information:

This error was stably intermittent. There would be a sudden change of behavior, but once the behavior changed, it would be consistent until another unfathomable event would precipitate another change of behavior.

This behavior was always a loss of connection between the scripts that had previously cached links. However, which functions would be effected would change.

Now - “what I think it is”…

Looking at the above code…

I had some variables named with legacy names: LTOHealth, LTOLives, etc… LTO originally stood for “Level Test Object”. I’ve had them on my list to clean up for some time - to make them currentHealth, currentLives, etc…

And I did, precipitating the change of behavior during this thread. I was staring at them thinking all I’ve done is changed your label/name and I’ve completely cleaned up all the connections… HOW COULD THE BEHAVIOR CHANGE? I’ve tactically done no real change at all!

Now - the crux?

Looking at the code in LevelStatus.js, I have

var setLives : int;
var setMaxFuel: int;
var setHealth : int;.

Seems fine? Well these variables set:
LTOLives, LTOFuel LTOHealth in function Start() of PlayerStatus

function Start () {
//      <snip>
   LTOFuel = levelStatus.setMaxFuel;
   LTOHealth = levelStatus.setHealth;
   LTOLives = levelStatus.setLives;
}

Note that in the beginning:

static var LTOFuel : float; 
static var LTOHealth : float;

Whups! :sweat_smile:

Setting these could be an int. I assign health and fuel as a whole number - but it’s being set against a float. :shock:

Now the odd things was this worked… kindov.

But by changing the mere NAME of the variable, it caused different behavior. Working for one function, but not another! :shock:

You’d’a’thunk?

I’d’ve expected it would just not have worked at all.

I didn’t have the stamina to change them all back and see if the behavior reverted in a repeatable manner, but somehow when I went thru and cleaned up my names, it changed which functions were losing communication.

I’ve now set those original values in LevelStatus to float:

var setLives : float;
var setMaxFuel: float;
var setHealth : float;

and everything works fine now…

This may also be a factor of NOT having a controller script and having confusions on loading (Awake, Start, etc) where the linking and caching take place.

I just had a spate of NullRef’s that seem to be a load order issue - where I changed one line of code and two different scripts stopped talking with broken links that should have been cached on start(). Most likely the values had not been set because of the start order.