How do I correctly pass a self reference of one object to another? [duplicate] - c#

This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 4 months ago.
I have a PlayerMovement class and an EnemyMovement class. My enemy moves only when the player moves.
When an enemy is created, it should get added to a list of enemies in the PlayerMovement object.
This is what I've got:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyMovement : MonoBehaviour
{
public PlayerMovement player;
void Awake()
{
player.Subscribe(this);
}
public void Move()
{
print("Enemy moved!");
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
public static ArrayList enemies;
void Start()
{
enemies = new ArrayList();
}
void Message(Direction direction)
{
foreach(EnemyMovement enemy in enemies)
{
enemy.Move();
}
}
public void Subscribe(EnemyMovement enemy)
{
enemies.Add(enemy);
}
}
However, Unity gives me the error
NullReferenceException: Object reference not set to an instance of an object.
How do I correctly pass a self reference of one object to another?

Where I’m relying on myself, or another user, to enter references through the Inspector, I generally like to add checks to the code. For example:
public class EnemyMovement : MonoBehaviour
{
public PlayerMovement player;
void Awake()
{
if (player == null)
{
Debug.LogError ( “PlayerMovement is null.” );
return;
}
player.Subscribe(this);
}
// ..
}
And then if I know I want an initialised item from the start, I will generally do so in the definition, like so:
public class PlayerMovement : MonoBehaviour
{
public static ArrayList enemies = new();
// ..
}
That way you stand a much smaller chance of getting a NullRegerence exception.

Related

Can't access variable from another script

Assets\Scripts\Wood.cs(32,9): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement
Well I'm trying to take the bool hasTorch and put it in the script Wood.cs to know if the player has a torch or not.
(I'm new so it's probably easy to fix, I just don't know :c)
Script 1 (Chest.cs):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Chest : MonoBehaviour
{
public Sprite openChest;
public GameObject chest1;
public GameObject chestBox;
public GameObject torch;
public bool hasTorch = false;
public GameObject darkness;
public GameObject chatBox;
private void OnTriggerEnter2D(Collider2D other)
{
chest1.GetComponent<SpriteRenderer>().sprite = openChest;
torch.SetActive(true);
chatBox.SetActive(true);
darkness.SetActive(false);
chestBox.SetActive(false);
hasTorch = true;
}
public void Close()
{
chatBox.SetActive(false);
}
}
Script 1 (Wood.cs):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Wood : MonoBehaviour
{
public GameObject chestScript;
public Chest script;
public GameObject chatBox;
private void OnTriggerEnter2D(Collider2D other)
{
if (script.hasTorch == true)
{
chatBox.SetActive(true);
}
if (script.hasTorch == true)
{
chatBox.SetActive(true);
}
}
public void Close()
{
chatBox.SetActive(false);
}
void Start(){
chestScript.GetComponentInChildren<Chest>().hasTorch;
}
}
This line does not do anything (not a valid statement, as the error suggests):
chestScript.GetComponentInChildren<Chest>().hasTorch;
you could Log it or set it to true/false like this (a valid assignment):
chestScript.GetComponentInChildren<Chest>().hasTorch = false;
You have created a variable of type Chest but have not told Unity which Chest instance you want to access. Imagine you had a couple of GameObjects, all with this script attached, each with a different value for hasTorch. Unity wouldn't know which instance you have in mind, that is why you have to specifically assign the value.
All you have to do is to add this line into the Start() method:
script = someKindOfaGameObject.GetComponent<Chest>();
From now on you should be able to access all the public variables in the Chest script using script.variable syntax.

is there a way to find if an object in an array has been destroyed? [duplicate]

I'm creating a game and i want to show a panel when the player is dead
I've tried different approaches but none seems to do what I want
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class DeadOrAlive : MonoBehaviour
{
public GameObject Player;
public GameObject deadPanel;
void Update()
{
if (!GameObject.FindWithTag("Player"))
{
deadPanel.SetActive(true);
}
}
}
To check if a object has been destroyed, you should use MonoBehavior's OnDestroy like so:
// Attach this script to the player object
public class DeadOrAlive : MonoBehaviour
{
public GameObject deadPanel;
void OnDestroy()
{
deadPanel.SetActive(true);
}
}
You can also instead of destroying the player object, set it to active/inactive, but to check if the player is dead or alive this way, you will need a separate object which checks the active state:
//Attach this to a object which isn't a child of the player, maybe a dummy object called "PlayerMonitor" which is always active
public class DeadOrAlive : MonoBehaviour
{
public GameObject deadPanel;
void Update()
{
if (!GameObject.FindWithTag("Player"))
{
deadPanel.SetActive(true);
}
}
}
Haven't used unity in a while and forgot how weird it could get.
Thanks to #VincentBree this is how I did it
void Update()
{
if (!Player.activeSelf)
{
deadPanel.SetActive(true);
}
}

Is there a way to check if a GameObject has been destroyed?

I'm creating a game and i want to show a panel when the player is dead
I've tried different approaches but none seems to do what I want
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class DeadOrAlive : MonoBehaviour
{
public GameObject Player;
public GameObject deadPanel;
void Update()
{
if (!GameObject.FindWithTag("Player"))
{
deadPanel.SetActive(true);
}
}
}
To check if a object has been destroyed, you should use MonoBehavior's OnDestroy like so:
// Attach this script to the player object
public class DeadOrAlive : MonoBehaviour
{
public GameObject deadPanel;
void OnDestroy()
{
deadPanel.SetActive(true);
}
}
You can also instead of destroying the player object, set it to active/inactive, but to check if the player is dead or alive this way, you will need a separate object which checks the active state:
//Attach this to a object which isn't a child of the player, maybe a dummy object called "PlayerMonitor" which is always active
public class DeadOrAlive : MonoBehaviour
{
public GameObject deadPanel;
void Update()
{
if (!GameObject.FindWithTag("Player"))
{
deadPanel.SetActive(true);
}
}
}
Haven't used unity in a while and forgot how weird it could get.
Thanks to #VincentBree this is how I did it
void Update()
{
if (!Player.activeSelf)
{
deadPanel.SetActive(true);
}
}

How could i fix an error (cs0120) in unity?

In unity, I'm trying to make a press of a button increase the speed of the player. However, each time I run it. It gives me:
Error CS0120: An object reference is required for the non-static
field, method, or property 'PlayerController.speed'
I've already tried changing order of the code, so what could I do?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Upgrader1 : MonoBehaviour
{
void Start()
{
GameObject Player = GameObject.Find("Player");
PlayerController PlayerController = Player.GetComponent<PlayerController>();
}
public void Upgrade1()
{
PlayerController.speed++;
}
}
public class Upgrader1 : MonoBehaviour
{
PlayerController PlayerController; //It should be member variable
void Start()
{
GameObject Player = GameObject.Find("Player");
PlayerController = Player.GetComponent<PlayerController>();
}
public void Upgrade1()
{
PlayerController.speed++;
}
}
it's always a good thing to use proper naming conventions.
PlayerController _PlayerController;
void Start() {
GameObject Player = GameObject.Find("Player");
_PlayerController = Player.GetComponent<PlayerController>();
}
public void UpgradeSpeed() // I changed the name according to its functionality
{
_PlayerController.speed++;
}
With this, you won't put PlayerController class reference again by mistake.

Events in scriptable objects

Is it possible to use UnityEvents and/or UnityActions in scriptable objects?
My attempt looks like this:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
[CreateAssetMenu(menuName ="Dog", fileName ="New Dog")]
public class Dog : ScriptableObject
{
public string Id;
public string Text;
public UnityEvent WhenFinished;
public GameObject Go;
}
Is it possible to make a scriptable object hold a reference to a GO in the hierarchy or have an event point to a listener in the hierarchy. Is there anyway a scriptable object can communicate with the logic in the hierarchy?
Yes and no.
You can get an SO to GO reference and the reverse, but only during runtime.
When outside runtime, you can only get a SO to GO reference, an SO created in the editor outside of runtime can't reference any game object (it wouldn't know from which scene to get the GO).
Following is the code showing how to achieve this.
Scriptable Object Code
using System;
using UnityEngine;
[CreateAssetMenu(menuName = "Dog", fileName = "New Dog")]
public class Dog : ScriptableObject {
public GameObject Go;
public static event Action<string> ScriptableToGameobjectEvent;
private void Awake() {
EventScript.GameobjectToScriptableEvent += InstanceCreated;
if (Application.isPlaying) {
ScriptableToGameobjectEvent("Dog Created");
}
}
private void InstanceCreated(GameObject go) {
Go = go;
Debug.Log(Go.name);
}
}
Game Object Code
using UnityEngine;
using System;
public class EventScript : MonoBehaviour {
public static event Action<GameObject> GameobjectToScriptableEvent;
public ScriptableObject myScriptable;
private void Awake() {
Dog.ScriptableToGameobjectEvent += DogCreated;
}
private void Start() {
myScriptable = ScriptableObject.CreateInstance<Dog>();
}
private void DogCreated(string str) {
Debug.Log(str);
GameobjectToScriptableEvent(gameObject);
}
}

Categories