- Home /
The question is answered, I solved it with help.
Powerup only spawning twice (MissingReferenceException Error)
So in my game I have an object that the powerup Spawns on top of (I intend for you to have to activate it to get the powerup but for now you just walk over it, but that's not the problem), I have it so on Start a powerup spawns, then five seconds after the powerup has worn off a new one spawn, that first cycle works fine, but after I pick up the new one, instead of spawning in the new one I get this error:
MissingReferenceException: The object of type 'Powerup' has been destroyed but you are still trying to >access it. Your script should either check if it is null or you should not destroy the object. Powerup.Remove () (at Assets/Scripts/Powerup/Powerup.cs:30) Powerup.m_0 () (at Assets/Scripts/Powerup/Powerup.cs:11) PowerupController+c_Iterator0.MoveNext () (at Assets/Scripts/Powerup/PowerupController.cs:37) UnityEngine.SetupCoroutine.InvokeMoveNext (IEnumerator enumerator, IntPtr returnValueAddress) (at C:/buildslave/unity/build/Runtime/Export/Coroutines.cs:17)
PowerupWellController Class:
public class PowerupWellController : MonoBehaviour {
public Color color;
public GameObject powerup;
private bool powerupSpawned;
private bool powerupSpawning;
private string powerupName;
// Use this for initialization
void Start () {
powerupName = powerup.name.Substring (8, powerup.name.Length - 8);
SpawnPowerup ();
color = powerup.GetComponent<SpriteRenderer> ().color;
}
// Update is called once per frame
void Update () {
if(transform.childCount == 0){
powerupSpawned = false;
}
if(!powerupSpawned && !powerupSpawning){
powerupSpawning = true;
Debug.Log ("Spawning Powerup!");
StartCoroutine (PowerupController.CallAfterTime (SpawnPowerup, 5f));
}
}
void OnTriggerStay2D (Collider2D col){
if(col.gameObject.name == "Player"){
if(col.gameObject.GetComponent<PlayerController>().activate){
col.gameObject.GetComponent<SpriteRenderer> ().color = color;
}
}
}
private void SpawnPowerup(){
PowerupController.SpawnPowerup (powerupName, new Vector2(gameObject.transform.position.x, gameObject.transform.position.y + 0.75f), this.transform);
powerupSpawned = true;
powerupSpawning = false;
}
}
Powerup Class:
public class Powerup : MonoBehaviour {
public Effect effect;
public void OnPickup(){
effect.action ();
effect.endAction += delegate{ Remove(); };
StartCoroutine (PowerupController.CallAfterTime(effect.endAction, effect.length));
}
void OnTriggerEnter2D(Collider2D c){
if(c.transform.tag == "Player"){
OnPickup ();
Disable ();
}
}
private void Disable(){
GetComponent<Collider2D> ().enabled = false;
GetComponent<SpriteRenderer> ().enabled = false;
}
private void Remove(){
Debug.Log ("Remove() Called!");
Destroy (gameObject);
}
}
The Remove function in the Powerup class does get called, it just doesn't seem to destroy the second powerup after it wears off like it's supposed to.
Answer by sparkzbarca · Dec 30, 2017 at 06:03 PM
so whats happening here and we can kind of see this based on the error thrown is this error is specifically saying your trying to destroy an already destroyed object.
so what i can tell you is the second attempt at calling removing is in fact still calling the first powerups code/script.
my guess is the problem is something to do with this line and the code in here.
PowerupController.SpawnPowerup (powerupName, new Vector2(gameObject.transform.position.x, gameObject.transform.position.y + 0.75f), this.transform)
somehow your not really creating a new powerup (i.e. using new keyword) but your somehow storing a reference to the old.
so your trying to destroy the same object twice instead of two seperate objects is the problem if that helps. This is generally caused by you somewhere saying something like
powerupreference = powerup
powerup.destory()
and not then going and after you destroy doing something like
poweruprefrence = null; or making sure when you pick up the second powerup you change the reference.
somewhere you keeping and storing the reference to the first object and never getting rid of it so at some point your calling
powerup.remove() and the powerup variable that your using is still the old one and hasn't been updated to be the second powerup.
just as a note by the way, this is pretty much all in the error it threw, i know it's a bit odd to understand but it is there.
object of type 'Powerup' has been destroyed but you are still trying to >access it.
this tells us your trying to do something to an already destroyed object. The question is what object and what are you trying to do. That is answered in the next line where it lists the line and class.
Powerup.Remove () (at Assets/Scripts/Powerup/Powerup.cs:30)
this tells us Powerup.Remove() is the kind of line having an issue,
the at tells us it's line 30 in powerup.cs
if we check.
we can see according to your code line 26 is powerup.destory() but it's safe to assume you just didn't copy paste a few using system.collections; etc at the top and those are the top 4 missing lines.
You're right, I didn't copy the top, it is refering to Destroy(gameObject); on line 26. So basically, I need to reset the reference to the object to be set to the second powerup? But how do I do that? Aren't I calling the Remove function of that specific object? I don't see any area where I could be storing this and it not be replacing automatically, except maybe in this script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
public class PowerupController : $$anonymous$$onoBehaviour {
const string powerupsPath = "Prefabs/Powerups/Powerup_";
public PlayerController playerController;
public static Dictionary<string, Effect> powerups = new Dictionary<string,Effect> () {
{"$$anonymous$$ovementSpeed",
new Effect (5f, delegate {
PlayerController.Double$$anonymous$$oveSpeed ();
}, delegate {
PlayerController.Reset$$anonymous$$oveSpeed ();
})
} //Add comma when adding a new Powerup!
};
// Use this for initialization
void Start () {
}
public static void SpawnPowerup(string name, Vector2 position, Transform parent){
GameObject powerup = Instantiate (Resources.Load<GameObject>(powerupsPath + name), position, Quaternion.identity, parent) as GameObject;
powerup.transform.localScale *= 6.5f;
Powerup pu = powerup.AddComponent<Powerup> ();
pu.effect = powerups [name];
}
public static IEnumerator CallAfterTime(UnityAction function, float seconds){
yield return new WaitForSeconds (seconds);
function ();
}
}
this is probably the stored reference
Powerup pu = powerup.AddComponent ();
also looking at this.
effect.endAction += delegate{ Remove(); };
StartCoroutine (PowerupController.CallAfterTime(effect.endAction, effect.length));
the second time you start coroutine.
because effect.endaction is +=
i think the second time you call it it will be that
effect.endaction = 0 at first then effect.endaction = remove()
then the second time its
effect.endaction = remove();remove();
possibly.
resulting in the second remove action being invalid.
possibly. if you debug.log in remove and get 3 calls when you only expect two thats the case.
if you see two it's the first object called twice.
Answer by richardhenry1399 · Dec 30, 2017 at 08:45 PM
I fixed it, basically what I did was instead of calling the Remove() function and destroying that specific gameObject, I made a new function in my WellController that cleared all child objects of that well.
Thank you for the help sparkzbarca!