0
\$\begingroup\$

I have been struggling with this NullReferenceExpection error.

When I don't add anything and just use this version of the code. The game adds a tower, upgrades a tower and takes money for it.

It worked all fine until I tried to add a menu that opens up and then you can choose a tower type you can buy.

So after I add all of this I get a NullReferenceExpection at return tower == null && gameManager.Gold >= cost; and at gameManager.Gold -= tower.GetComponent<TowerData>().CurrentLevel.cost;. The game also doesn't take any money from purchases and you can spawn near infinite amounts of towers on one spot.

//tower = (GameObject)

theTowerMenu.gameObject.SetActive (false);

transform.position = new Vector3(openSpot.transform.position.x, openSpot.transform.position.y, transform.position.z

If i take 'tower = (GameObject)' out of comments I get "error CS0030: Cannot convert type void' toUnityEngine.GameObject'".

The current script:

using UnityEngine;
using System.Collections;

public class PlaceTower : MonoBehaviour {

public GameObject towerPrefab;
private GameObject tower;

public GameObject openSpot;

private GameManagerBehavior gameManager;

public GameObject theTowerMenu;

// Use this for initialization
void Start () {
    gameManager = GameObject.Find ("GameManager").GetComponent<GameManagerBehavior>();
}

// Update is called once per frame
void Update () {

}

private bool canPlaceTower() {
    int cost = towerPrefab.GetComponent<TowerData> ().levels[0].cost;
    return tower == null && gameManager.Gold >= cost;
}

void OnMouseUp () {
    if (canPlaceTower ()) {
        //tower = (GameObject)
        theTowerMenu.gameObject.SetActive (false);
        transform.position = new Vector3(openSpot.transform.position.x, openSpot.transform.position.y, transform.position.z);
        Instantiate (towerPrefab, transform.position, Quaternion.identity);

        AudioSource audioSource = gameObject.GetComponent<AudioSource> ();
        audioSource.PlayOneShot (audioSource.clip);

        gameManager.Gold -= tower.GetComponent<TowerData>().CurrentLevel.cost;
    } else if (canUpgradeTower ()) {
        tower.GetComponent<TowerData> ().increaseLevel ();
        AudioSource audioSource = gameObject.GetComponent<AudioSource> ();
        audioSource.PlayOneShot (audioSource.clip);
        gameManager.Gold -= tower.GetComponent<TowerData> ().CurrentLevel.cost;
    }

}

private bool canUpgradeTower() {
    if (tower != null) {
        TowerData towerData = tower.GetComponent<TowerData> ();
        TowerLevel nextLevel = towerData.getNextLevel ();
        if (nextLevel != null) {
            return gameManager.Gold >= nextLevel.cost;
        }
    }
    return false;
}
}

Please help. I've been stuck with this for a long time.

\$\endgroup\$
  • 1
    \$\begingroup\$ I'm not sure about the logic but you have declared tower as a private and I cant see anywhere where you are setting it. \$\endgroup\$ – Uri Popov May 13 '16 at 7:53
3
\$\begingroup\$

You are creating a tower prefab with Instantiate (towerPrefab, transform.position, Quaternion.identity);, but you don't do anything with the GameObject it returns. The result is that your newly created tower is just an independent GameObject in your scene with no relation at all to your PlaceTower game object which created it.

It seems like you want the newly created tower assigned to the private GameObject tower; variable so you can reference it at a later point in the script:

tower = Instantiate (towerPrefab, transform.position, Quaternion.identity) as GameObject;

You might also want to make the tower a child-object of the PlaceTower object by adding the line tower.transform.SetParent(transform) below the previous one.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ I'm sorry I fell asleep. Wow. Thank you very much! I have a question: Why should I set the tower as a child-object of PlaceTower? \$\endgroup\$ – SleepySleeper May 13 '16 at 13:52
  • 1
    \$\begingroup\$ @SleepySleeper Just feels right, IMO. The game-mechanical effect is that it moves the Tower when the PlaceTower is moved and it destroys the Tower when the PlaceTower is destroyed. Reagarding your workflow in the editor: it makes the tower appear as a child of the place in the scene graph while in play mode, so it's easier for you as a developer to spot which tower belongs to which place. \$\endgroup\$ – Philipp May 13 '16 at 13:56
  • \$\begingroup\$ I see. Thank you for answering. I really appreciate it. I'm still kind of new to C# and Unity. \$\endgroup\$ – SleepySleeper May 13 '16 at 14:05
2
\$\begingroup\$

With your current logic you have no choice but to run into a null reference exception.
You never assign anything to tower. The check canPlaceTower is only returning true if tower is null but in the code of the if statement you use tower: tower.GetComponent<TowerData>().CurrentLevel.cost;.
That can't work. If you want to create a new tower instance you have to create from one a prefab or something else. One option would be to do it like this:

//instead of this:
//tower = (GameObject)
//use this:
tower = MonoBehaviour.Instantiate(Resources.Load("Path/To/towerPrefab")) as GameObject;
//Set position and rotation next.

There are other options like using the existing towerPrefab:

tower = (GameObject)Instantiate(towerPrefab, transform.position, Quaternion.identity);

Which looks like what you are looking for according to your line Instantiate (towerPrefab, transform.position, Quaternion.identity);where you miss the assignment to tower.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ I want to instantiate a copy of the object stored in towerPrefab to create a tower, and store it in tower so I can control it in the game. Your line created 5 more errors. Thanks anyway. \$\endgroup\$ – SleepySleeper May 13 '16 at 8:05
  • 1
    \$\begingroup\$ What does Convert.ToString do here? You already have a string literal here, so what are you converting it to? \$\endgroup\$ – Philipp May 13 '16 at 8:07
  • 2
    \$\begingroup\$ @SleepySleeper Please help us helping you and tell us what new errors you get with that line. \$\endgroup\$ – Philipp May 13 '16 at 8:09
  • 1
    \$\begingroup\$ @Philip: My mistake, I copied that line from my own project and there I needed that. So I removed it here. Thanks for the hint! \$\endgroup\$ – Skalli May 13 '16 at 8:12
  • \$\begingroup\$ I'm sorry, but he updated his comment and I used the old line and I got a lot of errors. After I used the new and current one this happened: I couldn't even place a tower after clicking the area where I can place towers, and clicking the tower. \$\endgroup\$ – SleepySleeper May 13 '16 at 13:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.