- Home /
Follower script stops working after death of target. How to re-assign the target at respawn?
Hi everyone!
I am new to Unity and to coding in general and I am already having a lot of fun making my little game!
I followed a (great) Youtube tutorial for a side view platformer and I was trying to add something more personal with a light following my player. For that I made a Follower script (below) which works fine until the player dies and respawns, at which point the light doesn't follow the player anymore and I get an error message saying:
Your script should either check if it is null or you should not destroy the object. Follower.Update () (at Assets/Scripts/Follower.cs:10) using UnityEngine; using System.Collections;MissingReferenceException: The object of type 'GameObject' has been destroyed but you are still trying to access it.
public class Follower : MonoBehaviour { public GameObject targetToFollow;
public Vector3 offset;
void Update() { targetToFollow = GameObject.FindWithTag(targetToFollow.tag); transform.position = targetToFollow.transform.position + offset; } } To kill the player I use this function: public void Die(){ Destroy (gameObject); } And to respawn the player: private void SpawnPlayer(Vector3 spawnPos) { currentPlayer = Instantiate(player, spawnPos, Quaternion.identity) as GameObject; cam.SetTarget(currentPlayer.transform); } After searching online for a bit I couldn't figure a way to fix it which is why I am putting my hopes in your crafty hands :) - So first of all is my approach valid or should I revise everything? - Secondly could you help me finding a solution to the problem or point me towards a source where I can learn how to do what I am trying to achieve? Thanks in advance and cheers!
Don't use FindWithTag is Update method. It is considered bad practice, cos it is slow, and that can create problems once you have more objects on the scene. This is the way I would do it. Have a single gameobject responsible for creating Followers and Targets. Call it something like Spawner and create a script Spawner.cs on it. You need it to keep reference to both Followers and Targets so that you can introduce new Targets to the Follower. Let's say that your follower have its code in Follower.cs and Target has its in Target.cs. You can spawn (Instantiate) follower in it or just add a public GameObject property in your script to make a placeholder for a Follower from your scene (drag and drop it to that field in Inspector). Either way, you need to have that reference to the Follower in your Spawner. Put your SpawnPlayer method in Spawner, too. When you create a new Player (after Instantiate method) you then have a reference to a Player, and you need to pass it to the Follower. Then use Follower.GetComponent().targetToFollow = currentPlayer. This way you put the reference to the Target into Follower's script. One last this you need to add, and this is probably what you wanted to know in the first place, is protection from using the empty reference in your Update method so that it does not work until you fill in that reference. So put this "if (targetToFollow)" before "transform.position = targetToFollow.transform.position + offset;". And loose that FindWithTag part.
Let me just add that this is a solution to the programmatic problem you were having. There is a more simpler way to achieve this behavior with the light following the target than through code. If your light will always be hooked to the player, you can simply parent a light to a player in Hierarchy view. Just drag'n'drop it on Player, and than adjust it's position in Transform component.
I started by testing what seems to be the easiest solution to this specific problem and used the parenting option (thanks for the tip, this will be very useful!). It does follow the player but I'm having another issue now. The problem I have is that I change the facing (left - right) of my character based on the input direction and the light ends up on the other side when I turn to face the other direction. In other words it is attached to the left side of my character and it's only visible when going left. Is there a good way to have the position of the parented object relative for x and y but absolute for z?
Answer by LMan · Mar 06, 2014 at 08:37 PM
What if you didn't destroy the player? Just disabled the gameobject, re-positioned, and reactivate it?
Otherwise, you're going to need to put a check for if that player variable is null. Then you could have the player script or the spawner script or whatever reassign that variable when the player object is instantiated.
This sounds like a good option too, thanks! Is it the most common way to deal with player death in games? In which way is it better or worse than destroying the GameObject?
Well I don't know about most common- but it's handy for when you don't want to re-connect all the references. It's especially good here because the game doesn't end when the player dies- everything else keeps running. If you have things that depend on the player script being there, it's just easier to keep it around. It's also good for mobile platforms where instantiating/destroying objects can be expensive computationally due to the time spent loading something new into the memory, and time spent collecting "garbage" of objects you destroyed.
A benefit of destroying would be that it does free up memory for other tasks, after garbage collection. Plus when you instantiate, all your variables start at their defaults so you wouldn't have to reset them by hand. Disabling would also be pointless if the gameobject you disabled never got re-enabled and just sat around taking up memory. Oh, and you can't do gameobject.find on disabled objects I believe- but if you had a reference to it before you disabled it, the reference stays intact.
Actually it is common way to do some "deaths" in games. This technique is a popular program$$anonymous$$g pattern called Object pool. It is often used for creating and destroying bullets. You usually get lousy performance if you instantiate many bullets when they are fired and destroy them as they hit something. It's better to create a bunch of them at once and only once. This bunch is called a pool (a List of bullets). You take some bullets from the pool (make them active) and return them back deactivated (to the list) once they are not needed so that you can reuse them again (reset position and reactivate).
Ok I understand, that makes sense. What terms should I search for if I want to learn more about the technic? Thanks a lot!
Answer by wibble82 · Mar 06, 2014 at 08:38 PM
Pretty easy if your player has a specific script attached (maybe a Player script). There's an error on line 10 of your follower - each frame you're saying 'find the target by asking the target what its tag is and search for it'. That'll have no effect whatsoever initially, until your target dies and it'll start giving you null reference errors.
How about in your update:
//if I don't have a target, try and find one by searching for a game object with your player script attached.
if(targetToFollow == null)
targetToFollow = FindObjectOfType<Player>();
//if I STILL don't have a target there's nothing I can do so bail!
if(targetToFollow == null)
return;
Note: That assumes your player has a 'Player' script attached.
You could use FindWithTag instead of FindObjectOfType, but you'll have to write in the correct tag, not try and read it from the target object.
I wouldn't worry too much about the performance of the Find functions for now. It's bad practice to call them every frame (they aren't cheap) but it'll be fine if you just do it when you need to find a new target.
More efficient alternatives involve more complex systems where you maintain some kind of 'spawner' that knows about what's going on in the game so provides a fast way of finding stuff. But why take a complex solution to simple problem :)
Thanks for your answer and for pointing out the mistake on line 10. I managed to fix my code thanks to that! I understand this isn't the most optimal way to find the player but I have so much to learn that I'll stick with the simple solution for now and optimize it when I know more. Thanks again!
Answer by mdelisle · Mar 06, 2014 at 09:34 PM
Wow thank you so much for all the answers, it looks like there is a great community here :) I'll test the different solution and get back to you. Thanks again!
Your answer
Follow this Question
Related Questions
Dragg 2D object? 1 Answer
When i double Tap single Tap function also Executing.. 1 Answer
Adding animation from other model on character 0 Answers
Best way to store large quantities of information 2 Answers
Quaternion.LookRotation over time? 0 Answers