My SendMessage is problematic [SOLVED]

Hi all,

I have a script attached to my enemy object (at the moment it's nothing but a cube, so no parent/child issues), which, among other things, calls this function when it dies:

var levelController : Scoreboard; // was var levelController : GameObject;
var scoreWorth = 5;

function YouBeDead()
{
    levelController.AddScore(scoreWorth); // was levelController.SendMessage("AddScore", scoreWorth);
    Instantiate(explosion, transform.position, Random.rotation);
    Destroy(gameObject);
}

(levelController is a GameObject var, and scoreWorth is an int var) My issue is with the levelController line. It sends a message to my Level Controller script, telling it to add x amount of points onto my player's score:

var totalScore : int;

function AddScore(scoreWorth : int)
{
    totalScore += scoreWorth;
    print (totalScore);
}

function OnGUI () 
{
    GUI.Label (Rect (0,0,100,50), "Score: " + totalScore);
}

The strange part is, it calls the function fine, but it doesn't send the value of scoreWorth. As a test, I put the line `print(totalScore);` in the AddScore function, and every time I kill an enemy it does print, but it prints 0, so obviously the value isn't travelling with the message.

As always, it's probably something exceedingly simple, but I'm just not seeing it, and am starting to get cranky with it :P

Thanks for your help!

Kristof

Have you defined "scoreWorth" as a var in your levelController script? if so, this class-wide definition may be conflicting with the "local" definition that you are trying to set up in the function's parameters. The correct way to do this is to not have a var declared with the same name as your function parameter names.

However, since you already have a variable as a reference to your levelController gameobject, you could go one step further and make that a reference to the script on that gameobject by changing the variable's type to the name of the script. Eg, instead of:

var levelController : GameObject;

You could have:

var levelController : LevelController;

(Assuming your script is named with an initial captial, which is the convention in Unity dev)

This means your variable is now set to receive an object whose type is "LevelController" - the type that you created when you made a script with that exact name.

Once you have this set up, if you drag your levelController gameobject into that slot, the reference will now be to the script instance on that object, rather than the object itself. This means you no longer have to use "SendMessage" to transmit messages, and can instead call the function directly, for example, instead of:

levelController.SendMessage ("AddScore", scoreWorth);

You can have:

levelController.AddScore(scoreWorth);

The benefits of working in this way are many, but to name a few:

  • It's shorter and more readable
  • It executes faster, and so is more optmial at runtime
  • Certain common types of errors are checked at compile-time (typos or incorrect params)

Hope this helps!

** ---- EDIT ---- **

Apparently it didn't so here's another thing to try:

If your enemies are being dynamically instanced, then having a reference to the LevelController prefab sounds wrong - they ought to have a reference to the instance of that prefab in the scene.

I understand the problem is that you can't drag in these references because when in edit mode, the enemies don't exist in the scene in order to be able to drag references into them.

There are two ways that spring to mind to solve this:

  1. Have the script that instantiates the enemies "tell" the new instances about the LevelController reference. Or...

  2. Have the enemy instances "find" the LevelController reference in their own start functions.

To implement number 1, you would have a reference to the LevelController object in your "EnemySpawner" script (or whatever it's called - the important thing is that this presumably is on a gameobject somewhere in your scene).

In this script, it creates a new enemy instance, and then immediately after, in the same function, it passes the LevelController reference it has to the enemy, like so:

var levelController : LevelController;

// "Enemy" is the name of the script on your enemy prefab
var enemyPrefab : Enemy;   

function SpawnEnemy() {
    var newEnemy = Instantiate(enemyPrefab, newPos, newRot);

    // now the levelController var on the new enemy is assigned the
    //  same reference that this script currently holds:
    newEnemy.levelController = levelController;
}

Alternatively (number 2) - this is one of the situations where using one of the many "Find" functions is a good idea. You could find the levelController by the name of the gameobject, by its tag, or by the type of script attached.

Seeing as you know there is only one of this kind of object in your scene (or there should be, right?) you can make your enemys find this object using a single line of code in their Start function:

// this var is "private" because it doesn't need assigning externally
private var levelController : LevelController; 

function Start() {
    levelController = GameObject.FindObjectOfType(LevelController);
}