I am making a multiplayer game in which the characters can change scenes. I have items scattered in different scenes which the player can pick up and add to his inventory. The items have an 'AddItem' script attached to them. If the player is in the first scene and picks up the item, it is added to the inventory, however when the player changes the scene and picks up an item in that scene (which is using same script), it throws null reference error. Here is the script:
public class AddItem : MonoBehaviour
{
public Item item;
DialogueConditions dc;
private GameObject player;
private void Start()
{
player = GameObject.Find("Engineer");
dc = player.GetComponent<DialogueConditions>();
}
void pickUp()
{
Debug.Log("Picking up : " + item.name);
HasBriefDoc();
bool itemPickedUp = Inventory.instance.Add(item);
if(itemPickedUp == true)
{
Destroy(gameObject);
}
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
pickUp();
}
}
public void HasBriefDoc()
{
if (item == null)
{
dc.hasBrief = false;
}
else if (item.name == "Brief Doc")
{
dc.hasBrief = true;
}
else
{
dc.hasBrief = false; //throws null reference
}
}
}
After debugging, I found out that after scene change, the object 'dc' is assigned with null reference in the Start() method. I also tried initializing it with FindObjectOfType<DialogueConditions>(); and adding the start code to the Awake() method, but the problem still persists. The only way it is working is when I drop the 'dc' object and directly use static variables, which is something I do not prefer.
To anyone who is facing this issue, my problem was occurring due to wrong execution order of functions. The 'AddItem' script was running before my player had even instantiated, on which this DialogueConditions script was attached. That is why it was returning null reference. I changed the order of execution by instantiating my player in the Awake() method, as it will then run before the Start() method of AddItem, hence the null reference exception was removed.
Related
I'm currently working on a script for a basic pause menu. The script is passed two GameObjects through the inspector, each being a different menu, and alternates between setting each object as enabled while setting the other as disabled. The code had worked fine before I moved it to a different script while restructuring my codebase. The script still executes properly, but each time I perform the operation Unity throws an UnassignedReferenceException, telling me to set the reference to the HUD in the inspector. The reference is clearly set and the operation is being performed, but the error is still thrown. I have checked for other instances of the script, and none are present.
Here is the full script in question:
public class GameMenuHandler : MonoBehaviour
{
private bool paused;
[SerializeField] private GameObject HUD;
[SerializeField] private GameObject pauseMenu;
void Start()
{
SubscribeMethodsToEvents();
}
public void RestartLevel()
{
String scene_name = SceneManager.GetActiveScene().name;
SceneManager.LoadScene(scene_name);
}
public void ExitToMainMenu()
{
SceneManager.LoadScene("MainMenu");
}
public void TogglePauseMenu()
{
paused = !paused;
if (paused)
{
HUD.SetActive(false);
pauseMenu.SetActive(true);
Time.timeScale = 0f;
}
else
{
HUD.SetActive(true);
pauseMenu.SetActive(false);
Time.timeScale = 1f;
}
}
void SubscribeMethodsToEvents()
{
GameEvents.current.onEscapeKeyPress += TogglePauseMenu;
}
void UnsubscribeMethodsFromEvents()
{
GameEvents.current.onEscapeKeyPress -= TogglePauseMenu;
}
void OnDestroy()
{
UnsubscribeMethodsFromEvents();
}
}
Here is the inspector view of the script:
Here is the console after the operation had been performed (successfully) numerous times:
One of the best ways to avoid this error is to set the condition != Null. Try this:
if (HUD) HUD.SetActive(false);
if (HUD) HUD.SetActive(true);
I have multiple enemies set up in a level, all using the same behaviour and animator scripts. When I hit or kill one of them, all of them get hit or killed. I need them to function as separate instances.
I have tried referencing only an instance of the script:
private GoblinBehaviour goblin;
goblin = GetComponent<GoblinBehaviour>();
goblin.IncrementHits(1);
But that doesn't work. An error arises that says that script can't be accessed with an instance and needs a type instead.
code for hit detection script:
public class RangedDetection : MonoBehaviour
{
private GoblinBehaviour goblin;
void OnTriggerEnter(Collider other)
{
//on colliding destroy rocks after its life time
Destroy(gameObject, rockLifeTime);
if (other.gameObject.tag.Equals("Enemy"))
//if (other.tag == "Enemy")
{
Destroy(other.gameObject);
}
else if (other.gameObject.tag.Equals("Goblin"))//goblin should play
death animation
{
goblin = GetComponent<GoblinBehaviour>();
goblin.IncrementHits(1);
GetComponent<BoxCollider>().enabled = false; //Removing hit
collider so it only hits target once.
}
}
}
Simplified Code for goblin script:
public class GoblinBehaviour : MonoBehaviour
{
Transform player;
public static bool isDead;
public static bool isPunching;
public static bool isHit;
public GameObject particles;
public Animator anim;
public void IncrementHits(int hitCount)
{
isHit = true;
hits += hitCount;
if (hits >= 2) isDead = true;
}
void Die()
{
Instantiate(particles, transform.position, transform.rotation);
Destroy(gameObject);
}
void updateAnim()
{
anim.SetBool("Punch_b", isPunching);
anim.SetBool("hit", isHit);
}
}
Things should animate and act separately, I'm not sure how to reference only the current instance of the script.
While your code is incomplete and the problem cannot be said for sure, it looks like you are using statics improperly.
Static properties are instance analagous. In other words, all of your goblin instances share any static properties (isPunching, isHit, isDead, etc.). Making these values static allows you to reference them directly without having to obtain the instance you're affecting, but results in you updating all goblins at once.
Your solution will involve removing the static modifier from your GoblinBehaviour properties unless the properties are meant to be shared across all instances.
I just want reload a scene after GameOver. I have searched on the net but it didn't give me a clear reason and solution. Can someone point me the list of reason for this. I have posted below code for my scene reload.
Time.timeScale = 1;
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
Then my code for GameManager
public class GameManager : MonoBehaviour
{
private void Awake()
{
if (instance == null)
{
instance = this;
}
DontDestroyOnLoad(this);
}
}
Kindly inform me if you need additional info. Thanks
Hi from what I saw on your code you might be missing some stuff on your sigleton code for the game manager, for example not having multiple instances for the game manager (each time you reload a scene you're making a new one and not destroying it on load).
withouth seeing all your scene I can't find out the why but I did some simple code to reload a scene and keep one scene manager only.
using UnityEngine;
using UnityEngine.SceneManagement;
public class GMScript : MonoBehaviour
{
private static GMScript instance;
private void Awake()
{
if (instance == null || instance == this)
{
instance = this;
}
else
{
Destroy(this.gameObject);
}
DontDestroyOnLoad(this);
Debug.Log("Scene reloaded");
}
public void ReloadScene()
{
Time.timeScale = 1;
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
}
private void Update()
{
if(Input.GetKeyDown(KeyCode.Space))
{
ReloadScene();
}
}
}
Also make sure the scene is added to project settings, I'm using space key for reloading but you can put out any method fits your needs.
Also make sure all your objects that will need access of the gamemanager update their reference once the scene loads(preferably on start method, as we use awake for the singleton handle), this can be done using a find method like GameObject.FindWithTag.
Found the problem I have this code
public class GameManager : MonoBehaviour
{
private void Awake()
{
if (instance == null)
{
instance = this;
}
DontDestroyOnLoad(this);
}
void Start()
{
shapeSpawnerGO = GameObject.Find("SpawnShapesObj");
scoreGO = GameObject.Find("ScoreText");
lifeGo = GameObject.Find("LifeText");
}
}
Then after reload scene the reference for this 3 becomes null it has something to do DontDestroyOnLoad(this); which means the GameManager script won't be destroyed after reload so when the game reloads the GameManager stays while it's reference get's destroyed. I'll check for efficient solution on this one
I want simply to destroy a deactivated instance of a Quad prefab (hp bar) , am able to destroy activated ones with :
private GameObject correspondingHpBar;
private string correspondingHpBarName;
void Start()
{
correspondingHpBarName = "hpBar1"
}
void Update()
{
correspondingHpBar = GameObject.Find (correspondingHpBarName);
if (shipHp <= 0)
{
Destroy (correspondingHpBar);
Destroy (gameObject);
}
}
This doesn't work with the deactivated objects, i googled hard but failed to find an answer.
Deactivated object don't have their Start or Update method called (nor any coroutine for that matter). In fact when an object is deactivated it is like its own time is frozen.
What you could do is create a method that does the destruction and find a way to call it from another script (for example a kind of controller that keeps reference to all HP bars in your scene).
The following is some pseudo-code (didn't check if it compiles, but you should adapt it anyway):
// in script for HP bar
public Boolean TryDestroy()
{
if (shipHp <= 0)
{
Destroy (correspondingHpBar);
Destroy (gameObject);
return true;
}
return false;
}
// in another script
private List<HPBar> _allHPBars;
void Awake()
{
_allHPBars = new List<HPBar>(FindObjectsOfType(typeof(HPBar)));
}
void Update()
{
var destroyedHPBars = new List<HPBar>();
foreach (var hpBar in _allHPBars)
{
if (hpBar.TryDestroy())
{
destroyedHPBars .Add(hpBar);
}
}
foreach (var destroyedBar in destroyedHPBars)
{
_allHPBars.Remove(destroyedBar);
}
}
In Unity 5, what is "clean" way to manage dynamically created game objects?
I've written a component (MonoBehavior) that creates/destroys several GameObjects. The objects are loaded as part of custom customization system that selects portions of character - hair/clothes, etc. Meaning they're visible to player, visible in editor, but are not supposed to be editable in editor. The objects being loaded are meshes with skeletons.
The script behaves in this fashion:
Loads GameObjects from Resources (exact object is determined in script, they are not prefabs)
Attaches them to some portion of the scene (not necessarily to its own node)
Deletes when destroyed.
Deletion:
protected void unloadLastResource(){
if (lastResourceInstance){
if (Application.isEditor){
GameObject.DestroyImmediate(lastResourceInstance);
}
else
GameObject.Destroy(lastResourceInstance);
Resources.UnloadUnusedAssets();
lastResourceInstance = null;
}
}
Creation:
GameObject target = getEffectiveTargetNode();
Object resource = Resources.Load(newResourceName);
instance = Instantiate(resource) as GameObject;
instance.hideFlags = HideFlags.HideAndDontSave;
instance.transform.parent = target.transform;
instance.transform.localPosition = Vector3.zero;
instance.transform.localRotation = Quaternion.identity;
instance.transform.localScale = Vector3.one;
Destruction handler:
void OnDestroy(){
unloadLastResource();
}
That seems to work fine in editor, but when I switch from gaming mode back to edito mode, I get a lot of warnings:
Destroying object multiple times. Don't use DestroyImmediate on the same object in OnDisable or OnDestroy.
UnityEngine.Object:DestroyImmediate(Object)
And I get bunch of new object trees (the ones that are supposed to be deleted - trees originate from object that was loaded along with original "resources" and was attached) at the top level of scene hieararchy.
So, how do I cleanly handle dynamically created gameobjects?
I need to know which flags I need to set, and which steps I should do to ensure that object does not "leak" into scene and is properly destroyed when I delete component that created it.
Advice?
Full base class used by "ResourceLoader"
public class BaseResourceLoader : MonoBehaviour {
public GameObject targetNode = null;
protected GameObject lastTargetNode{
get{return lastTargetNodeInternal;}
}
private GameObject lastTargetNodeInternal = null;
protected bool targetNodeChanged(){
return targetNode != lastTargetNode;
}
protected string lastResourceName{
get{return lastResourceNameInternal;}
}
private string lastResourceNameInternal = "";
//private Object lastResource;
private GameObject lastResourceInstance;
protected GameObject getEffectiveTargetNode(){
if (targetNode == null)
return this.gameObject;
return targetNode;
}
public void reloadResource(){
loadNewResource(lastResourceNameInternal, true);
}
protected void unloadLastResource(){
if (lastResourceInstance){
if (Application.isEditor){
GameObject.DestroyImmediate(lastResourceInstance);
}
else
GameObject.Destroy(lastResourceInstance);
Resources.UnloadUnusedAssets();
lastResourceInstance = null;
}
lastResourceNameInternal = "";
}
protected void loadNewResource(string newResourceName, bool forceReload){
if ((newResourceName == lastResourceNameInternal) && !forceReload)
return;
GameObject instance = null;
if (newResourceName != ""){
GameObject target = getEffectiveTargetNode();
Object resource = Resources.Load(newResourceName);
instance = Instantiate(resource) as GameObject;
instance.hideFlags = HideFlags.HideAndDontSave;
instance.transform.parent = target.transform;
instance.transform.localPosition = Vector3.zero;
instance.transform.localRotation = Quaternion.identity;
instance.transform.localScale = Vector3.one;
}
unloadLastResource ();
lastResourceInstance = instance;
lastResourceNameInternal = newResourceName;
lastTargetNodeInternal = targetNode;
}
void OnDestroy(){
unloadLastResource();
}
}
I've figured it out.
The problem is caused by this line:
instance.hideFlags = HideFlags.HideAndDontSave;
In hierarchy, this flag affects only ONE object and does not affect object's children. As a result, if you set this flag for the root object in hierarchy and scene gets saved, root will be destroyed, but its children will get serialized (meaning they'll appear in inspector upon scene reload) AND you'll get tons of warnings about destroying same object many times.
To deal with the problem, it is necessary to walk through the entire hierarchy and set the flag to all objects in hierarchy. Which fixes the problem and eliminates all errors.
void makeHierarchyHidden(GameObject obj){
obj.gameObject.hideFlags = HideFlags.HideAndDontSave;
foreach(Transform child in obj.transform){
makeHierarchyHidden(child.gameObject);
}
}
.....
makeHierarchyHidden (newObject);
.....
It is unclear if this only happens when loading non-prefabs from disk, or just in general case with all hierarchical objects.
The error message means that your code would create an infinite recursion, because you are destorying an object from within OnDestroy() which would - again - call OnDestroy() on this object and so forth...
Let me share this piece of code which I use as the base class for all my Behaviors:
using UnityEngine;
using System.Collections.Generic;
namespace de.softfun.drawntogether {
public class EnhancedBehavior : MonoBehaviour {
private List<GameObject> linkedObjects = new List<GameObject>();
protected void destroy(params GameObject[] objects) {
foreach (GameObject o in objects) {
try {
Destroy(o);
} catch {
continue;
}
}
}
public void LinkObjects(params GameObject[] objects) {
foreach (GameObject o in objects) {
linkedObjects.Add(o);
}
}
void OnDestroy() {
foreach (GameObject o in linkedObjects) {
destroy(o);
}
}
protected T instantiate<T>(T prefab, bool addLink = true) where T : Object {
T o = (T)Instantiate(prefab);
if (addLink && (o is GameObject)) {
linkedObjects.add(o);
}
return o;
}
}
}
The trick here is that I collect a List of GameObjects which are bound to this MonoBehaviour and should be destroyed if the 'parent' gets destroyed. When using instantiate of this class, the created object gets automatically added to the List, unless addLink is set to false in this method.
Maybe you can use this or something similar.