0
\$\begingroup\$

I am currently working on a 3d game in unity in C#. I've been following a series of tutorials on youtube (burgzerg arcade) for quite a while now, and i've came across the following error. This error only occurs when i instantiate prefabs to my game scene, opposed to dragging and dropping them into the scene hierarchy. In the tutorial, we work on many things including a targeting script and mob generation. The purpose of the targeting script, is to (when pressing tab), checks to see if there are any mobs (prefabs / game objects) within a certain distance, if they are, it adds them to a list, from which sorts them in order from closest to furthest. When the mob generation script instantiates the prefabs, it instantiates them as a child to the spawn points that i made (spawn point game objects), meaning that when the targeting script searches for the game objects, it doesn't recognize that the mobs are there (because they are childed to the spawn points). So what im asking is how can i make it so that when i press tab, it adds the instantiated children to the list? Here is the code for the mob regen script and targeting script. This is the error i get after i press tab using the instantiated mobs. (ps: when i manually drag the prefabs onto the game scene, opposed to instantiating, it works just the way i want because they aren't instantiated as a child).

This is the error i get when i press tab using the mob regen script:

Parameter name: index
System.Collections.Generic.List`1[UnityEngine.Transform].get_Item (Int32 index) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.Collections.Generic/List.cs:633)
Targetting.TargetEnemy () (at Assets/Scripts/Targetting.cs:64)
Targetting.Update () (at Assets/Scripts/Targetting.cs:116)

The reason i think i am getting this error is because when the mob gen script generates mobs, it instantiates them as a child to the parent spawn points, so when the targetting script looks for tags with 'enemy', it can't find them ( so it doesn't add them to the list) because the cloned mobs are children to the spawn points. I am trying to find a work around but haven't found anything. Could someone please give me a hand to find a solution to my problem? I've tried things like searching for the children, checking for the child count ect ect. but still can't find a way.

Oh also, the spawn points are just empty game objects that i placed around the map.

Code for targetting.cs:

using UnityEngine;
using System.Collections.Generic;
using System.Collections;

public class Targetting : MonoBehaviour 
{
    public List<Transform> targets;
    public Transform selectedTarget;

    private Transform myTransform;

    // Use this for initialization
    void Start () 
    {
        targets = new List<Transform> ();
        selectedTarget = null;
        myTransform = transform;

        AddAllEnemies();

    }


    public void AddAllEnemies()
    {
        GameObject[] go = GameObject.FindGameObjectsWithTag ("Enemy");

        foreach (GameObject enemy in go) 
        {

            Debug.Log ("Adding enemies to list....");
            AddTarget (enemy.transform);
        }

    }

    public void AddTarget(Transform enemy)
    {
        Debug.Log ("Added enemy to the list");
        targets.Add (enemy);
    }

    private void SortTargetByDistance()
    {
        targets.Sort (delegate(Transform t1, Transform t2) 
                      {  return Vector3.Distance (t1.position, myTransform.position).CompareTo(Vector3.Distance(t2.position, myTransform.position));
                       });
    }


    private void TargetEnemy()
    {
        if(selectedTarget == null)
        {
            SortTargetByDistance();
            selectedTarget = targets[0]; //so the list is empty? well at first it is thats why its null, so it isnt adding them to list? doesn't seem like that, how can you output stuff? is there a comma
        }
        else
        {
            int index = targets.IndexOf (selectedTarget);

            if(index < targets.Count - 1)
            {
                index++;
            }
            else
            {
                index = 0;
            }
            DeselectTarget();
            selectedTarget = targets[index];

        }
            SelectTarget();
    }

    private void SelectTarget()
    {
        Transform name = selectedTarget.FindChild ("Name");

        if(name == null)
        {
            Debug.LogError("Could not find the Name on " + selectedTarget.name);
            return;
        }

        name.GetComponent<TextMesh> ().text = selectedTarget.GetComponent<Mob>().Name;
        name.GetComponent<MeshRenderer> ().enabled = true;
        selectedTarget.GetComponent<Mob> ().DisplayHealth();

        Messenger.Broadcast<bool> ("show mob vitalbars", true);

    }

    private void DeselectTarget()
    {
        selectedTarget.FindChild("Name").GetComponent<MeshRenderer>().enabled = false;

        selectedTarget = null;
        Messenger.Broadcast<bool> ("show mob vitalbars", false);
    }

    // Update is called once per frame
    void Update () 
    {
        if(Input.GetKeyDown (KeyCode.Tab))
        {
            TargetEnemy();
        }
    }
}

Code for mob regen script:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class MobGenerator : MonoBehaviour 
{

    public enum State
    {
        Idle,
        Initialize,
        Setup,
        SpawnMob


    }

    public GameObject[] mobPrefabs;  // an array to hold all of the prefabs of mobs we want to spawn
    public GameObject[] spawnPoints;  // an array will hold a reference to all of the spawn points in game

    public State state;  // our local variable that holds our current state

    void Awake()
    {
        state = MobGenerator.State.Initialize;


    }

    // USE THIS FOR INITIALIZATION  
    IEnumerator Start()
    {
        while(true)
        {
            switch(state)
            {
            case State.Initialize:
                Initialize();
                break;

            case State.Setup:
                Setup();
                break;

            case State.SpawnMob:
                SpawnMob();
                break;

            }
            yield return 0;
        }
    }

    // make sure that eeverything is initialized before we go on to the next step
    private void Initialize()
    {
        Debug.Log("*** We are in the initialize function ***");

        if (!CheckForMobPrefabs())
            return;

        if (!CheckForSpawnPoints ())
            return;

        state = MobGenerator.State.Setup;
    }

    // make sure that everything is setup before we continue
    private void Setup()
    {
        Debug.Log("*** We are in the setup function ***");

        state = MobGenerator.State.SpawnMob;

    }

    // spawn a mob if we have an open spawn point
    private void SpawnMob()
    {
        Debug.Log ("*** Spawn Mob ***");

        GameObject[] gos = AvailableSpawnPoints ();

        for (int cnt = 0; cnt < gos.Length; cnt++) 
        {
            GameObject go = Instantiate (mobPrefabs [Random.Range (0, mobPrefabs.Length)],
                                         gos [cnt].transform.position, 
                                         Quaternion.identity) as GameObject;

            go.transform.parent = gos [cnt].transform;
            Debug.Log ("there are " + go.transform.childCount.ToString () + " children");

            state = MobGenerator.State.Idle;
        }
    }

    // check to see that we have at least one mob prefab to spawn
    private bool CheckForMobPrefabs()
    {
        if (mobPrefabs.Length > 0)
            return true;
        else
            return false;
    }

    // check to see if we have at least one spawnpoint to spawn mobs at
    private bool CheckForSpawnPoints()
    {
        if (spawnPoints.Length > 0)
            return true;
        else
            return false;

    }

    // generate a list of available spawn points that do not have any mobs childed to it
    private GameObject[] AvailableSpawnPoints()
    {
        List<GameObject> gos = new List<GameObject> ();

        for(int cnt = 0; cnt < spawnPoints.Length; cnt++)
        {
            if(spawnPoints[cnt].transform.childCount == 0)
            {

                Debug.Log ("*** Spawn point available ***");
                gos.Add (spawnPoints[cnt]);

            }
        }
        return gos.ToArray ();
    }
}

So as you can see, I am aware of the problem but I am not sure how to go about fixing the problem. Please let me know how or point me in the right direction.

\$\endgroup\$
1
\$\begingroup\$

This line

GameObject[] go = GameObject.FindGameObjectsWithTag ("Enemy");

Looks for all GameObjects with the "Enemy" tag. It should find them no matter of their parent.

My guess is that your Mobs prefabs do not have the "Enemy" tag applied.

\$\endgroup\$
  • \$\begingroup\$ Nice catch, was about to answer the same! \$\endgroup\$ – Pip Mar 5 '15 at 16:23
  • \$\begingroup\$ I have double checked and all my tags are set to what they should be, including the prefabs. \$\endgroup\$ – devon t Mar 5 '15 at 18:07
  • \$\begingroup\$ The error you're getting seems to be out of sync with the Targetting.cs you've posted. The error shows up at line 116 but your file only has 111 lines. \$\endgroup\$ – Mihai-Andrei Dinculescu Mar 5 '15 at 18:28
  • \$\begingroup\$ The reason the error line is out of sync is because i had comments at the top of my code that i excluded from posting. \$\endgroup\$ – devon t Mar 7 '15 at 18:29
  • \$\begingroup\$ I am just wondering if there is any way i can contact you and thoroughly describe/show you my error. \$\endgroup\$ – devon t Mar 7 '15 at 18:30
0
\$\begingroup\$

I can see several things wrong with your code. Your problems come from the way you're trying to observe your dynamically generated mobs.

First, in Targetting.cs, you only call AddAllEnemies once in the Start method. You're assuming that the mobs will be spawned first. But if MobGenerator.cs is started second, that would explain your problem: the enemies in the mob don't even exist yet.

The list of targets needs to be constantly updated as more mobs are spawned. So you need to either:

  • Run AddAllEnemies in the first line of Targetting.Update to completely overwrite the previous list of targets.

    // Update is called once per frame
    void Update () 
    {
        AddAllEnemies();
        if(Input.GetKeyDown (KeyCode.Tab))
        {
            TargetEnemy();
        }
    }
    
  • Write an UpdateTargets method to add targets from GameObject.FindGameObjectsWithTag ("Enemy") that are not present in the current list of targets. Use it in Targetting.Update instead.

    public void UpdateTargets()
    {
        GameObject[] go = GameObject.FindGameObjectsWithTag ("Enemy");
    
        foreach (GameObject enemy in go) 
        {
            if (targets.Contains(enemy.transform)) continue;
            Debug.Log ("Adding enemies to list....");
            AddTarget (enemy.transform);
        }
    }
    

This way should work no matter which of the two scripts runs first. Just a side note, but since you're dealing with enemies that can be removed from the scene, you should also write some code to remove null references from the list of targets. It'll help protect your game from null pointer exceptions.

\$\endgroup\$

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.