Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Cant start a coroutine to spawn destroy a game object

Discussion in 'Scripting' started by AndrewMac26, Dec 9, 2015.

  1. AndrewMac26

    AndrewMac26

    Joined:
    Nov 24, 2015
    Posts:
    14
    So im trying to start a coroutine in another script but im getting this error:
    Assets/Scripts/playerScript.cs(93,64): error CS0103: The name `PlaySoundAndRemove' does not exist in the current context


    Code (CSharp):
    1. if (Input.GetButtonDown ("Fire2"))
    2.                     {
    3.                         Instantiate (grenadePrefab, GameObject.Find("grenadeSpawn").transform.position, Quaternion.identity);
    4.                         StartCoroutine(PlaySoundAndRemove());
    5.                         grenadeNum--;
    6.                     }
    7.             }
    this is where im trying to start the coroutine in playerScript^


    And this is the coroutine itself in grenadeScript:
    Code (CSharp):
    1. public IEnumerator PlaySoundAndRemove()
    2.     {
    3.         audio.clip = explosionSound;
    4.         audio.Play ();
    5.         new WaitForSeconds (audio.clip.length);
    6.         Instantiate (explosion, GameObject.Find("grenadeSpawn").transform.position, Quaternion.identity);
    7.         yield return new WaitForSeconds (1.0f);
    8.         Destroy(gameObject);
    9.     }
    I was using this for help and what Ive posted above is following this solution but its not working
    http://answers.unity3d.com/questions/782957/how-to-start-coroutine-from-another-script.html

    Any Ideas?
     
  2. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,744
    You're trying to call it on the object that's doing the spawning, but the function is on the spawned object. If you want to do that, you should make sure that you StartCoroutine AND run the coroutine function on the object:
    Code (csharp):
    1. GameObject spawned = Instantiate (grenadePrefab, GameObject.Find("grenadeSpawn").transform.position, Quaternion.identity);
    2. grenadeScript gren = spawned.GetComponent<grenadeScript>();
    3. grenadeScript.StartCoroutine(grenadeScript.PlaySoundAndRemove() );
    However, you're probably better off calling the coroutine from grenadeScript's own Start() function instead.
     
  3. AndrewMac26

    AndrewMac26

    Joined:
    Nov 24, 2015
    Posts:
    14
    Yep the start function works much better however I now have the problem that gameobject.find cant find the grenade prefab so it can instantiate the explosion in that location
     
  4. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,744
    The solution to this one is easy: don't use GameObject.Find. (I mean, don't use it ever; it's never the best option.) I'd have to have more information about the structure of your game to say for sure which technique to use instead, but mostly likely either A) Link it via the inspector instead as a public Transform; or B) put your spawning script on the spawner itself. If you need help with either of those don't hesitate to ask, I'm always more than happy to help eliminate GameObject.Find from projects. ;)
     
  5. AndrewMac26

    AndrewMac26

    Joined:
    Nov 24, 2015
    Posts:
    14
    Thanks so much, it works now when I link it like so:
    Code (CSharp):
    1. GameObject bullet = (GameObject) Instantiate (explosionPrefab,transform.position, Quaternion.identity);
    what exactly is the problem with GameObject.Find, Ive seen it mentioned in other threads but they never really go into detail
     
  6. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,744
    1) GameObject.Find is sloooooow. It has to look through every single object in the scene until it finds the one with the matching name.

    2) GameObject.Find doesn't guarantee that its results have what you're looking for. Just because an object is named "Player" doesn't mean that GetComponent<Player> will return anything usable.

    2a) For this code to be properly safeguarded against null references, you have to call GameObject.Find, check that object against null, then call GetComponent (which is also not super fast), then check against that for null, and finally you can begin working.

    3) GameObject.Find gives you false negatives if the name of the object has changed in any way - including, most annoyingly, the "(Clone)" that gets tacked onto the end when it is instantiated.

    4) GameObject.Find gives you false positives. Maybe right now there's only one "Player"-named object in your scene, but it's possible that a month from now you'll add another one, and you'll have to rewrite all your code when you do.

    4a) It finds objects throughout the scene, with no way to narrow your scope to something more sensible.

    5) GameObject.Find requires you to hardcode the names into your scripts. This is a variant on "code coupling" - your script will work only on this scene and this scene requires that code. It reduces script versatility.

    6) If you typo a name in GameObject.Find, it will give you a runtime error, not a compile time error. Compile time errors are preferred, because it lets you know instantly, before you even begin playtesting, and you're not required to playtest every situation your game can come across in order to find all the errors.

    SOME ALTERNATIVES:
    (which one of these to use depends on the situation, and this is not an exhaustive list. However, I will bet you money that you will never come across a situation outside of editor or debugging code where GameObject.Find is not inferior to at least one of the techniques below.)

    1) Assignment of public variables in the inspector.

    2) FindObject(s)OfType<ScriptName>() will find a reference to a component(s) of the given type. It's still slow, but it at least has a predictable return type and won't be screwed up by a slightly changed name.

    3) FindWithTag. The tag system a number of issues of its own (e.g. only one tag allowed per GameObject), and it still has problems 2 and 5 above, but it is at least fast. (Unity maintains a lookup table of tags -> GameObjects just to make this fast.)

    4) Singletons. If there's going to be one single globally-accessible something in your scene, a singleton is the right call - its speed is instant, its reliability can be rock-solid, it survives name changes (and can be written to even survive the object not existing initially!), and on and on. In fact, the main problem with singletons is that they're too convenient, so people start getting in the habit of using them when it's not appropriate to.

    4a) Managed lists. Like a singleton, but allows more than one of them. Also very fast and reliable.

    5) transform.Find. It has most of the same issues as GameObject.Find, but all of them to a lesser degree. It's not as slow because it's only looking under one transform; it's not as likely to find false positives for the same reason; and so on.

    6) Snagging references opportunistically via methods like OnCollisionEnter or by raycasting.

    7) GetComponentsInChildren<ScriptName>(). Like FindObjectsOfType and transform.Find had a baby. Useful when you have a general reference to a parent and want all of the things underneath it that fit certain criteria.
     
    Last edited: Dec 10, 2015