I am making a maze game and the keys which are needed to be collected to complete it wont appear again if the game restarts, I get the following error;
MissingReferenceException: The object of type 'MazeDirectives' 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.
I am just disabling the MazeKey object, not destroying it, can anyone help? Below is my code;
MazeKey.cs
using UnityEngine;
using System.Collections;
public class MazeKey : MonoBehaviour
{
void OnTriggerEnter2D(Collider2D other)
{
transform.parent.SendMessage("OnKeyFound", SendMessageOptions.DontRequireReceiver);
gameObject.SetActive(false);
}
}
MazeDirectives.cs
MazeGoal mazeGoal;
MazeKey mazeKey;
void StartDirectives()
{
mazeGoal = Instantiate(mazeGoalPrefab, MazeGenerator.instance.mazeGoalPosition, Quaternion.identity) as MazeGoal;
mazeGoal.transform.SetParent(transform);
mazeKeyPositions = MazeGenerator.instance.GetRandomFloorPositions(keysToFind);
for (int i = 0; i < mazeKeyPositions.Count; i++)
{
MazeKey mazeKey = Instantiate(mazeKeyPrefab, mazeKeyPositions[i], Quaternion.identity) as MazeKey;
mazeKey.transform.SetParent(transform);
}
}
To restart the game I use the code below;
void OnTriggerEnter2D(Collider2D other)
{
if (other.tag == "Player")
{
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
gameObject.SetActive(true);
}
}
MazeGoal.cs
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class MazeGoal : MonoBehaviour
{
public Sprite closedGoalSprite;
public Sprite openedGoalSprite;
void Start()
{
GetComponentInChildren<SpriteRenderer>().sprite = closedGoalSprite;
}
public void OpenGoal()
{
GetComponentInChildren<SpriteRenderer>().sprite = openedGoalSprite;
}
void OnTriggerEnter2D()
{
transform.parent.SendMessage("OnGoalReached", SendMessageOptions.DontRequireReceiver);
}
Explenation
The exception you get is not talking about the MazeKey object but rather the MazeDirectives component.
Unfortunately you hit the most important information in the comments:
private void Awake()
{
MazeGenerator.OnMazeReady += StartDirectives;
}
so OnMazeReady seems to be static and not instanced so it will not be destroyed when a new Scene is loaded but keeps intact bloating into the new Scene!
When you call
MazeGenerator.OnMazeReady += StartDirectives;
you add the call to the StartDirectives method of an instance of MazeDirectives as listener to that static event.
Now when you reload the Scene all GameObjects and thereby their instances of components are destroyed
=> so is the instance of MazeGenerator ... BUT the static event OnMazeReady is not destroyed!
so after the next Awake call you now have two listeners
The one from the "second"/new loaded Scene
Still the "old" one you added the first time
But since the instance of MazeDirectives you added the first listener for is destroyed when the scene is reload and a new instance generated, you get that exception
MissingReferenceException: The object of type 'MazeDirectives' 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.
when the method tries to access the transform value of the destroyed instance.
Solution 1a
So you should remove the listener when you destroy the instance
private void OnDestroy()
{
MazeGenerator.OnMazeReady -= StartDirectives;
}
Solution 1b
or overwrite it with only exactly one listener at a time
private void Awake()
{
MazeGenerator.OnMazeReady = StartDirectives;
}
this second aproach obviously is only useful when there is no other instance or class listening to that event. The question is how much sense does it make to use an event than? And I would than anyway remove it if not needed just to be sure
private void OnDestroy()
{
MazeGenerator.OnMazeReady = null;
}
Solution 2
I would prefere this solution.
Don't make MazeGenerator.OnMazeReady static at all. Since anyway I see that you are using a Singleton pattern e.g. in
MazeGenerator.instance.mazeGoalPosition
you could instead just make OnMazeReady Non-static and instead use it the same way:
private void Awake()
{
MazeGenerator.instance.OnMazeReady += startDirectives;
}
so it will be destroyed together with that instance of MazeGenerator.
General note
I would always remove all listeners I ever added as soon as possible to avoid exactly the issue you have.
You could additionally remove it e.g. already inside of StartDirectives to make sure the method is executed only once even if the same Scene "accidentely" invoked OnMazeReady twice.
Hint: I said additionally since it is always save/possible to remove a listener even if it wasn't added before and you should allways leave the one in OnDestroy in case the StartDirectives is never called before the object is destroyed.
Update:
This answer is wrong at first place. The exception is complaining about accessing the MazeDirectives's transform, not mazeGoal object.
But the comments below did give some useful info. So I'm keeping this post for references.
For complete solution, see here.
From the line mazeGoal.transform.SetParent(transform); throws the exception:
MissingReferenceException: The object of type 'MazeDirectives' 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.
From here:
The load of a new Scene destroys all current Scene objects.
The mazeGoal has been destroyed when you called the
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
to restart the game.
And from MonoBehaviour.Awake(),
Awake is called only once during the lifetime of the script instance.
Since you only assign the mazeGoal variable inside StartDirectives function which been called in Awake, after loading the same scene again, the actual object of mazeGoal has been destroyed.
If you want to reuse the same object when loading a new scene, you can use DontDestroyOnLoad to keep the mazeGoal object.
Or you can move the StartDirectives to Start function which will be called every time the gameobject is created and reinitialize your mazeGoal.
Related
I have a prefab object called Beam, which contains several things but one is an object that when an instance of it is collided with and triggered, should destroy itself.
Currently, I have the script that generates all of the instances on a variable called Beams. Shown here:
When that runs, it creates clones within it. Seen here:
You will also see in the last image, the Beam prefab that contains the Cookie in it. That cookie is where I have a script that says, if I hit it, destroy. That code looks like this:
...
public class Collectibles : MonoBehaviour
{
GameManager game;
// Start is called before the first frame update
void Start()
{
game = FindObjectOfType<GameManager> ();
}
...
void OnTriggerEnter2D(Collider2D other) {
if(other.tag == "Player"){
string coinType = "Cookie";
game.AddCollectible(coinType);
Destroy(gameObject);
}
}
}
Currently, when I run into a cookie, it runs Destroy(gameObject) and destroys ALL instances of the cookie (one per each Clone).
This code lives on Cookie, not on Beams. Is that correct? Should I have the code somewhere else? I also tried Destroy(this) but that doesn't do what I thought it would do (just the instance).
Is it possible that from where I was calling Destroy, the script doesn't have access to the instances, or am I missing something? Thank you in advance!
If I understand your question, you want that when the "player" collides with an instance of "beam" only destroy the instance of Cookie (or the gameobject that contains the script), in this case it would do so with the tag:
public GameObject[] arrayofcookie;
public int destroyedinstances=1;
//this int will tell how many instances you want to be destroyed (from the last instantiated to the first)
//for this example the last instance will be deleted
public void destroyCookie()
{
arrayofcookie= GameObject.FindGameObjectsWithTag("Cookie");
for (int i = 0; i < destroyedinstances; i++)
{
Destroy(arrayofcookie[i].gameObject);
}
}
You call this method in the cookie script, in the collider or if you prefer with an invoke method after N seconds.
I do not think I have understood your question very well, but in these problems I prefer to use the label and it also depends on the nature of your game
Using Unity, I'm working on a game where all Gameobjects with a certain tag vanish/reappear fairly regularly (every 10 seconds on average). I use GameObject.FindGameObjectsWithTag() to create a Gameobject[] through which I enumerate every time that the objects need to be made visible/invisible. I cannot call it once, on Start, as new Gameobjects are created while playing. I thought that it would be worse to access and change the Gameobject[] every time something got created/destroyed. Is there a better way to handle this. I know how bad of an impact on performance the GameObject.Find methods make...
Yes, there is a better way to do this. Have a script with List variable that can store GameObjects. Making it a singleton is better but not necessary.
This singleton script should be attached to an empty GameObject:
public class GameObjectManager : MonoBehaviour
{
//Holds instance of GameObjectManager
public static GameObjectManager instance = null;
public List<GameObject> allObjects = new List<GameObject>();
void Awake()
{
//If this script does not exit already, use this current instance
if (instance == null)
instance = this;
//If this script already exit, DESTROY this current instance
else if (instance != this)
Destroy(gameObject);
}
}
Now, write a script that registers itself to the List in the GameObjectManager script. It should register itself in the Awake function and un-register itself in the OnDestroy function.
This script must be attached to each prefab you want to add to the List. Simply do that from the Editor:
public class GameObjectAutoAdd : MonoBehaviour
{
void Awake()
{
//Add this GameObject to List when it is created
GameObjectManager.instance.allObjects.Add(gameObject);
}
void OnDestroy()
{
//Remove this GameObject from the List when it is about to be destroyed
GameObjectManager.instance.allObjects.Remove(gameObject);
}
}
If the GameObjects are not prefabs but GameObjects created through code, simply attach the GameObjectAutoAdd script to them once they are created:
GameObject obj = new GameObject("Player");
obj.AddComponent<GameObjectAutoAdd>();
You can now access your GameObjects in the List with GameObjectManager.instance.allObjects[n]; where n is the index number and you don't have to use any of the Find functions to find the GameObject anymore.
It's true that calling to GameObject.Find consumes a lot of recourses.
The point should be to save the result and use it always but I understand that you can't do that.
Other option is to have all this gameobjects as a child of one gameobject let's call it handlerGameobject. It will have a script that doesn't use GameObeject.Find it could getChild or transform.Find that uses less resources.
Hope it helps!
I am trying to make my player hit an object, destroying the object and triggering an animation, but everything I try causes an error. I am relatively new at c# so the answer may be obvious but I need help. How can I set it up so that the collision will cause the object to disappear and the player to play an animation? Here is the script I am currently trying.
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class succ : MonoBehaviour
{
public float speed = .15f;
public static float jumpSpeed = 170f;
void Start()
{
GetComponent<ConstantForce2D>().enabled = false;
GameObject.Find("goal");
}
public bool animation_bool;
private object coll;
private object other;
void Update()
{
OnCollisionStay2D(Collision2D coll);
{
if (coll.gameObject.tag == "succ") ;
{
animation_bool = true;
GetComponent<Animator>().SetBool("succ", animation_bool);
GetComponent<ConstantForce2D>().enabled = true;
Destroy(other.object);
}
}
}
private void Destroy(object gameObject)
{
throw new NotImplementedException();
}
private void OnCollisionStay2D(Collision2D collision2D, object coll)
{
throw new NotImplementedException();
}
}
There are a few things I can see that are wrong, but I'll start by answering your question.
I suggest you change your MonoBehaviour method OnCollisionStay2D to OnCollisionEnter2D. OnCollisionStay2D is "sent each frame where a collider on another object is touching this object's collider". OnCollisionEnter2D is "sent when an incoming collider makes contact with this object's collider".
I believe you are looking for the latter since you only want to trigger this once during the collision. You are also destroying the other object, making it impossible to call OnCollisionStay2D anymore even if you wanted to do so.
You should also remove your Update method. I honestly do not understand what you are trying to achieve there now. All of the OnCollision methods get called automatically; you do not have to call them yourself.
Then you can use the Awake and OnCollisionEnter2D methods as follows
public class Succ : MonoBehaviour
{
private Animator animator;
private void Awake()
{
// You can already get a reference to the Animator on Awake
// This way you do not have to do it on every collision
animator = GetComponent<Animator>();
}
// Use OnCollisionEnter2D instead since the code
// needs to be excecuted only once during the collision
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.CompareTag("succ")
{
// Assuming that you only want to trigger an animation once
// to reflect attacking or colliding, you could use SetTrigger
// instead. Otherwise you need to use SetBool again to set it
// back to false. You should then change the Animator parameter
// accordingly, from a bool to a trigger.
animator.SetTrigger("succ");
Destroy(collision.gameObject);
}
}
}
Apart from this, I have a few things I would like to comment on:
I am not sure what you are trying to achieve by setting your ConstantForce2D component to false on Start and then setting it to true on collision.
You seem to be using GameObject.Find on Start. GameObject.Find is something that should be very rarely used. It can be extremely expensive, especially if your Scene has a lot of GameObjects in it; this is because it simply goes through the Hiearchy, comparing the parameter string to names of GameObjects until it either finds a match or runs out of GameObjects.
Moreover, you are using GameObject.Find on Start to look for a GameObject, but then you do not store that anywhere, making the whole finding process completely pointless.
Overall, I recommend you to take a look at all of the different learning resources offered by Unity themselves. Your question is about fairly basic functionality that is certainly covered during all of the different tutorials.
I am making a game where I need to spawn a player in the start of the game. Then I will get transform.position of that player but I dont know how to do that. Here is my code:
GameObject playerball;
Transform spawpoint;
bool spaw
private void Start(){
spaw = false;
}
private void Update()
{
if(!spaw)
{
Instantiate(playerball, spawpoint.position, spawpoint.rotation);
spaw=true;
}
//????????????
}
As you see, I have no idea how to get access to that playerball. That playerball is the prefab that holds the control script, and this script has 'Transform public Camera' but in the prefab 'public Transform Camera ' is 'None(Transform)'. So I need that, when I have instantiated the playerball, I can control it, that mean I can set which is 'public Transform camera'. How can I do that?
Instantiate returns a reference to the object you have just instantiated. You just have to keep that reference, and to work on it for your own needs:
GameObject instance = Instantiate(playerball, spawpoint.position, spawpoint.rotation);
Also, you are missing a semicolon on this line:
bool spaw
Whole code would then give:
GameObject playerball;
Transform spawpoint;
bool spaw;
GameObject playerballObject;
private void Start(){
spaw = false;
}
private void Update()
{
if(!spaw)
{
playerballObject = GameObject.Instantiate(playerball, spawpoint.position, spawpoint.rotation);
spaw=true;
// Do any first time modification you need on your object.
}
// Use your reference there to update the state of your object.
}
Alternative script, making a few changes that Isuka did not, for efficiency, clean-code, and good practice.
public GameObject playerballPrefab;
public Transform spawpoint;
private GameObject playerballInstance;
private void Update()
{
if(playerballInstance == null)
{
playerballInstance = Instantiate(playerballPrefab, spawpoint.position, spawpoint.rotation);
// Other spawn logic / modification
}
//do stuff with playerballInstance every frame
}
Notice that we no longer need the bool spawn variable (er, spaw which is misspelled) as we can check to see if the playerballInstance is null, and if it is, spawn it. Any time it is null (e.g. if it is destroyed by any means) we will spawn a new one (which I renamed to playerballPrefab just so that the variable's name indicates that this object is not in the scene hierarchy; purely optional), just as if spawn was reset to false by an external script, but without any potential conflicts (e.g. setting spawn to false without destroying the instance and vice versa).
Additionally we hold that reference in perpetuity so that the ball can be acted upon continuously, rather than for the single frame it was instantiated. Isuka edited their answer to include this change.
playerballInstance can be set as either public (because we wish an outside script to modify the value) or private (because no other script should be touching it). The prefab and transform are declared public because presumably they are defined in the editor / inspector (and private fields are not listed and not serializable).
Start method is no longer needed because we no longer have a spawn value to make sure is set as we want (as well as not needed originally, as the default value for a bool is false and the fact that we could declare bool spawn = false without the Start() method anyway).
Marking Start() and Update() as private is also not strictly necessary, but not wrong. Similarly marking it public is not necessary or wrong. I personally either leave it off (inheriting its protection level from MonoBehaviour) or mark it public (as all MonoBehaviours have this method declared). It makes no difference to the engine what protection level is used, as it is invoked through voodoo.
My player object has 4 children objects, called Pawn 1 through 4. When I click on one of them, it becomes selected. When a Pawn is selected, it should glow. Now, the trouble is that in order for glowing to happen properly, each Pawn has to know if it is selected at the moment, or not. I did that part by attaching a
public class PlayerController : MonoBehaviour {
public GameObject selectedObject;
}
to the Player object, and a script to each Pawn object that, among other things, does this
void Update()
{
if (transform.parent.gameObject.GetComponent<PlayerController>().selectedObject ==
gameObject)
{
Glow();
}
}
I can't help but to think that there has to be a better way to do this, as performing a GetComponent on every Update, on every Pawn, for every player seems incredibly wasteful.
Is there a way to get a reference to the selectedObject in Start(), so it keeps getting updated without manually getting it the whole time?
Is there a way to get a reference to the selectedObject in Start(), so
it keeps getting updated without manually getting it the whole time?
Cache PlayerController in the Start function.
private PlayerController playerController;
void Start()
{
playerController = transform.parent.gameObject.GetComponent<PlayerController>();
}
void Update()
{
if (playerController.selectedObject ==
gameObject)
{
Glow();
}
}
Why not have the Pawn handle the click interaction and store whether or not it is selected? Then you'd have something like:
if(IsSelected)
Glow();