I'm not really sure how to describe it exactly so let me show you what is going on.
I have a PlayerControls script which looks like this (note: I stripped everything except for the necessities).
namespace Player.Controls {
internal class PlayerControls: MonoBehaviour {
public bool IsClimbing { get; private set; } = false;
public bool IsGrounded { get; private set; } = false;
}
}
These variables are set in this class depending if the player is climbing/touching the ground. This script resides on the "Player" GameObject in the scene.
I have another script called PlayerControllerwhich looks like this
using Player.Controls;
public class PlayerController: Singleton<PlayerController> {
internal PlayerStats stats = new PlayerStats();
//PlayerStats nested class (see below)
}
The Singleton class only checks if the generic type is null, if it is, it will use FindObjectOfType to get an instance. This script also resides on the "Player" GameObject.
Inside the PlayerController class, I have a nested class called PlayerStats. It looks like this
internal class PlayerStats : PlayerControls {
public new bool IsClimbing { get { return base.IsClimbing; } }
public new bool IsGrounded { get { return base.IsGrounded; } }
}
Notice this nested class in inheriting from PlayerControls.
The idea is that the PlayerControls class in inaccessible to all other classes except for PlayerController, and any information I want to obtain regarding the player can be obtained by getting the player's instance (via the singleton) and accessing the PlayerStats variable.
For example, assuming the variable inside Singleton which holds the instance is called Instance, one could do PlayerController.Instance.stats.IsClimbing; Everything works as expected, except for one thing.
In the Awake method of the PlayerController class, I do this
private void Awake() {
Debug.LogFormat("In PlayerController Awake(). Is PlayerController.stats null? {0}",
(stats.Equals(null) ? "Yes" : "No"));
Debug.LogFormat("IsClimbing : {0}", stats.IsClimbing);
}
In the output window, it prints
In PlayerController Awake(). Is PlayerController.stats null? Yes
IsClimbing : False
If I also put the same IsClimbing debug in the Update() method, the value is correct for when I start climbing.
So, finally, my question, how can I access the variables of the PlayerStats class with the stats variable if stats is null? I thought it may have been somehow calling straight to the PlayerControls properties, so I changed their names, removed the new inside of PlayerStats and even put a debug statement inside one of the properties inside PlayerStats, and it definitely gets called. For example,public bool IsClimbing { get { Debug.Log("Called IsClimbing inside PlayerStats."); return base.Climbing; } }
If it is getting called and working properly, how can it be null? I asked my professor and he doesn't seem to know why either. What is really going on here?
Edit:
As requested, the Singleton class:
public abstract class Singleton<T>: MonoBehaviour where T : MonoBehaviour {
private static T instance;
public static T Instance {
get {
if(instance == null) {
instance = FindObjectOfType<T>();
}
return instance;
}
}
}
Here is an image of the console output.
Digging around on the Unity forums it appears that the Equals method has been overridden (on Object which MonoBehaviour eventually derives from) which is why comparing a MonoBehaviour to null is not giving you what you might expect. The answer I link to suggests code like this is more appropriate:
stats == null || stats.Equals(null)
Related
I'm a total beginner at Unity and I don't understand how to use a variable in two different scripts.
For example, let's say that I have a script called "player" and another called "logic".
In the player script, I create the boolean "IsAlive", and when I collide with something, I die :
public class PlayerScript : MonoBehaviour
{
public bool IsAlive = True;
}
private void OnCollisionEnter2D(Collision2D collision)
{
IsAlive = False;
}
Now, I want to do something in the "Logic" script and have to check if the player is alive or not before. How do I do that ? I tried something like :
public class LogicScript : MonoBehaviour
{
public PlayerScript PlayerScript_logic;
private bool IsAlive_logic = PlayerScript_logic.IsAlive;
}
so that I could use "IsAlive_logic" which would be the same as "IsAlive".
But that is apparently not how if works.
If somebody could help me, please, I'm so lost.
Simply instead of IsAlive_logic in all places use PlayerScript_logic.IsAlive. Despite the fact that you can't access a non-constant field (PlayerScript_logic) when declaring your other fields - it is bad practice to store (and maintain) the same value in multiple places anyway ;)
Alternatively if you really for some reason want/need to you could have a property
private bool IsAlive_logic => PlayerScript_logic.IsAlive;
// or also
//private bool IsAlive_logic { get => PlayerScript_logic.IsAlive; }
// or also
//private bool IsAlive_logic { get { return PlayerScript_logic.IsAlive; } }
which basically simply returns PlayerScript_logic.IsAlive everytime you access it. In general this adds some tiny overhead though and it would be better to go through the PlayerScript_logic.IsAlive directly...
I have a TextMeshPro Input Field but my various attempts at getting the Text component are producing null reference exceptions. The Input Field is called Name. I reference this object when the player clicks OK after submitting their name.
Here is the GetName script:
public class GetName : MonoBehaviour
{
GameObject Name;
// These two are left over from previous attempts.
public TextMeshProUGUI player_name;
public TMP_InputField player_inputField;
private string monicker;
// Integer function should be less bother than a bool when called from another script.
public int IsNameEmpty()
{
monicker = Name.GetComponent<TMP_InputField>().text.ToString();
// Program never gets this far.
The OK function in the other script is:
public class WelcomeButtons : MonoBehaviour
{
public GetName getName;
void TaskOnClick6()
{
Debug.Log("You have clicked the OK button!");
int isName = getName.IsNameEmpty(); // Causes null reference exception.
// Program never gets this far.
A simple method to get the text from the input field
public class GetName: MonoBehaviour
{
public TMP_InputField name;
public void TaskOnClick()
{
if(name =="")
{
Debug.log("NO Name Found");
}
else
{
Debug.log("NAME: "+name);
}
}
Yes, if you attached the script above to an empty GameObject, then your script is missing the Link to the GameObject containing the TMP_InputField-Component. You can fix this in two simple ways, just decide what fits best for you:
a) Attach the GetName-script to the same GameObject that also contains the TMP_InputField-Component. Replace the Line "Name.GetComponent<TMP_InputField>().text.ToString();" with "GetComponent<TMP_InputField>().text.ToString();".
b) Leave the GetName-script on the empty GameObject. Make the "GameObject Name;" line public by changing it to "public GameObject Name". Go to the Unity-Editor. When selecting the GameObject containing the GetName-script, you should see the Name-Property and an empty Field next to it in the Inspector. Drag&Drop the GameObject containing the TMP_InputField-Component into it.
So, why did your code not work before? Well, the GetName-script needs some kind of reference to your TMP_InputField-component, which was missing. You tried this by getting it from the Name-Property, but never assigned it any value.
Thanks for all the advice I've received. I now have a working version using the OnClick() approach:
public class GetName : MonoBehaviour
{
public TMP_InputField user_inputField; // User-entered name.
public void SaveName()
{
Globals.player = user_inputField.text.ToString(); // Saved for later.
Debug.Log("Globals.player is " + Globals.player);
}
public void ResetName()
{
user_inputField.text = "";
Globals.player = "";
}
}
just two lines will work :
using UnityEngine.UI;
and
public TMPro.TMP_InputField Name;
I'm creating an enemy (from EnemyCreator1 class) with both EnemyMove1 and MarkusEnemy scripts (EnemyMove1 is a parent class to MarkusEnemy class). In EnemyCreator1 class I set value mainState of the script EnemyMove1 to "CHASE", but when I'm trying to access it from that class it says that mainState is "IDLE" (Please read my coments below because there are more explanations about what am I trying to achieve)
public class EnemyMove1 : MonoBehaviour
{
public enum mainStates { IDLE, CHASE }
public mainStates mainState;
void Update()
{
Debug.Log(mainState); //mainstate == IDLE, but should be CHASE
}
}
public class EnemyCreator1 : MonoBehaviour
{
[SerializeField] private GameObject enemyPrefab;
public void CreateEnemyAndSetItsStateToChase()
{
GameObject enemy = Instantiate(enemyPrefab);
enemy.GetComponent<EnemyMove1>().mainState = EnemyMove1.mainStates.CHASE;
}
}
public class MarkusEnemy : EnemyMove1
{
void Update()
{
EnemyMove enemyMoveScript = GetComponent<EnemyMove>();
Debug.Log(enemyMoveScript.mainState); //mainstate == CHASE
}
}
From the above code it looks like you are inheriting from a different base class EnemyMove, not EnemyMove1.
Thank you guys for helping me, after searching for the information about base classes I decided that it is impossible to access directly it's variables from another objects' scripts so I just simply call methods with variables as arguments (I put variables in round brackets of the method)
In Unity, I have
public enum inven {Food,Scissors,Nothing};
public inven held;
How can I access the enum and, more importantly, the information contained in the held variable, from another script.
I tried the Singleton Method:
public class Singleton : MonoBehaviour {
public static Singleton access;
public enum inven {Nothing, Scissors, Food};
public inven held;
void Awake () {
access = (access==null) ? this : access;
}
}
to make global variables, accessed by
Singleton.inven.food //or
Singleton.access.held //respectively
However, that returned "Null reference exception: Object reference not set to an instance of an object."
I also tried using this:
public class Accessor : MonoBehaviour {
void Start() {
GameObject HeldItem = GameObject.Find("Story"); //where story is the Gameobject containing the script of the enum and variable
TextController textcontroller = Story.GetComponent<Textcontroller>(); //Where TextController is the sript containing the enum and variable
}
}
accessed by TextController.held etc, returned that it needed an object reference. What is the proper way of doing this?
Here's how I do my singletons. In my case, it's called SharedData.
using UnityEngine;
using System.Collections;
public class Booter : MonoBehaviour
{
void Start ()
{
Application.targetFrameRate = 30;
GameObject obj = GameObject.Find("Globals");
if(obj == null)
{
obj = new GameObject("Globals");
GameObject.DontDestroyOnLoad(obj);
SharedData sharedData = obj.AddComponent<SharedData>();
sharedData.Initialize();
}
}
}
This script is attached to an object in the first scene that loads, and it is set in the script execution order to go first. It creates a GameObject to attach the SharedData component to, then tells the engine not to delete that GameObject when new levels are loaded.
Then to access it, I do this:
public class Interface : MonoBehaviour
{
private SharedData m_sharedData;
public void Initialize()
{
GameObject globalObj = GameObject.Find("Globals");
m_sharedData = globalObj.GetComponent<SharedData>();
m_sharedData.LoadData(); // This is where I use something on the singleton.
}
}
In order to get variable(s), function(s) in another class, I have known 2 ways of doing this. First, is to use Get Component to the Script that we want to get the variable(s), function(s) into. Second, is to use Instance of the Script itself.
So I have made the following code:
First case: Get Component to the Script itself
public class Manager : MonoBehaviour
{
private AnotherManager _anotherManager;
private void Awake()
{
_anotherManager = GameObject.Find("Managers").GetComponent<AnotherManager>();
}
private void Start()
{
_anotherManager.myIntVariable = 10;
_anotherManager.MyFunction();
}
}
public class AnotherManager : MonoBehaviour
{
public int myIntVariable;
public void MyFunction()
{
}
}
Second case: Use Instance of the Script itself
public class Manager : MonoBehaviour
{
private void Start()
{
AnotherManager.instance.myIntVariable = 10;
AnotherManager.instance.MyFunction();
}
}
public class AnotherManager : MonoBehaviour
{
public static AnotherManager instance;
public int myIntVariable;
private void Awake()
{
instance = this;
}
public void MyFunction()
{
}
}
My question is: Is there any difference between those cases? In terms of good practice of coding for programmer or performance or it is just a matter of programmer's perspective or whatever else?
Thanks
The second example is the what is known as the Singleton Pattern and should be used very sparingly.
I try to never use the first approach either where you find the gameobject and hope it exists.
You can expose a field for the Unity Inspector so that you can wire it up the same as you can expose any other variable
public AnotherManager AnotherManager;
Alternatively, if you hate using public all over the place like that, like me, you can also indicate to Unity that you wish to expose this variable in the inspector with the SerializeField attribute
[SerializeField]
private AnotherManager anotherManager;
With both of these methods, you can then drag an an object that has the AnotherManager component attached into the field in the inspector.
If instantiated objects need access to this, you will need to wire it up when it is instantiated.
If you need help attaching it in unity I can attach some screenshots.