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...
Related
I have two classes: Menu_Buttons, in which there are definitions for methods executed on clicking different buttons in the menu, and PauseMenu, which defines what happens when the Menu key is pressed during the game.
Menu_Buttons:
public class Menu_Buttons : MonoBehaviour
{
public void Menu_NewGameClick()
{
SceneManager.LoadScene(1);
}
public void Menu_ContinueClick()
{
Debug.Log("This will continue the game from the last save");
}
public void Menu_LoadGameClick()
{
SceneManager.LoadScene(1);
Debug.Log("Another menu will show to choose which save to load");
}
public void Menu_SaveGameClick()
{
SaveItem();
Debug.Log("This will save the game");
}
public void Menu_OptionsClick()
{
Debug.Log("This will show the game options");
}
public void Menu_QuitClick()
{
Application.Quit();
Debug.Log("The Game should quit now");
}
}
PauseMenu:
public class PauseMenu : MonoBehaviour
{
//private bool isPauseMenuOpened = false;
public GameObject pauseMenu;
void Update()
{
if (Input.GetKeyDown(KeyCode.B))
{
if (pauseMenu.activeSelf) { ClosePauseMenu(); }
else { OpenPauseMenu(); }
}
}
public void OpenPauseMenu()
{
pauseMenu.SetActive(true);
Cursor.visible = true;
Cursor.lockState = CursorLockMode.Confined;
//isPauseMenuOpened = true;
Time.timeScale = 0f;
}
public void ClosePauseMenu()
{
pauseMenu.SetActive(false);
Cursor.visible = false;
Cursor.lockState = CursorLockMode.Locked;
//isPauseMenuOpened = false;
Time.timeScale = 1f;
}
}
I wanted to add another method called Menu_ResumeClick, which would resume the game from the Pause Menu. Of course, I could just create this method in the PauseMenu script and then everything is fine. It looks like this:
public void Menu_ResumeClick()
{
ClosePauseMenu();
}
But since I would like to keep things organised, I thought it would be better to put this method in the Menu_Buttons script along with all the other similar methods. So I tried this:
public void Menu_ResumeClick()
{
PauseMenu.ClosePauseMenu();
}
And then problems begin... I get an error: an object reference is required for the non-static field method or property. Then if I change any of those classes to static, I get errors saying: cannot declare instance members in a static class. Not to mention that static classes canot inherit from MonoBehaviour. Maybe I would be able to solve those problems somehow, but the thing is I don't want to change the whole code just because I would rather have a method in another class. It's just for keeping things organised, nothing more.
I have to admit that I'm a bit frustrated by how these things work. I can easily put the Menu_ResumeClick() method in the PauseMenu class, but in the future it may be difficult to keep track of things if I have various methods scattered around different scripts. The most reasonable solution is to put every menu button in the Menu_Buttons class and then access them from there, but it poses problems that I described. Actually this is not the first time when I'm having problems with accessing methods or variables from other classes - there always seem to be some difficulties. It seems the best way to write code would be to just have a single class for the whole game because then I would be able to access absolutely anything easily - but again the problem would be with keeping things organised.
So, the question is: can I easily use methods (or variables) from other classes, without changing the whole code for this purpose? In other words can I somehow just call a method from another class like this: className.MethodName(); or set a variable from another class like this: className.varName = 2; without making everything static, etc.?
And a bonus question: If it's not possible, then how should I structure my code? Should I try to squeeze as many things as possible into a single class to be able to access them easily, should I make classes static, whenever it's possible, etc.?
In PauseMenu, you can add a field for the Menu_Buttons, which Unity can serialize, so you can pull the GameObject (what contains the Menu_Buttons) in the inspector, and you can call its public methods (and access its public members) from PauseMenu.
public Menu_Buttons MenuButtons;
// or (I much more like this version, keeping things as encapsulated as possible)
[SerializeField] private Menu_Buttons _menuButtons;
private void Resume() => _menuButtons.Menu_ResumeClick();
Edit based on comments:
Both script can have references to each other. As both logically related, I wouldn't separate them, because with the references, we couple them anyway.
Example:
public class Menu_Buttons : MonoBehaviour
{
[SerializeField] private PauseMenu _pauseMenu;
public void Menu_ResumeClick() => _pauseMenu.ClosePauseMenu();
// ...
}
public class PauseMenu : MonoBehaviour
{
[SerializeField] private Menu_Buttons _menuButtons;
// ...
public void ClosePauseMenu()
{
// ...
}
}
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)
I need to stop/resume the rotation of a fan when a certain key is pressed, so I wrote this C# code:
public bool rotationFlag = false;
void Update()
{
if(rotationFlag)
{
fan.transform.Rotate(Vector3.up, rotationAngle);
}
}
public void commuteFan()
{
rotationFlag = !rotationFlag;
}
cummuteFan() method is called when I press the button.
the variable starts on false and the fan correctly doesn't ratate, when I hit the button, the method is called, the variable values changes and the starts to rotate, but if I want to stop it, it doesn't work anymore. debug tells me that the value is correctly changed to false in commuteFan() but update() continues to read the old value (true) and the fan doesn't stop.
[SOLVED] I don't know why, but I created a class with only the bool along with getter/setter methods in it and it works know
So you had your variable as public, which means that any other part of the project could easily edit it. In your case you were changing it via the editor.
To avoid this kind of problems in the future try using protection.
public class Test : MonoBehaviour
{
private bool isRotating = false;
public void setIsRotating(bool status)
{
isRotating = status;
}
public bool getIsRotating()
{
return isRotating;
}
}
Coding like this will save you a lot of headaches later on.
In Unity3D I've got a script that adds the variable 'eaten' as a component.
using UnityEngine;
[AddComponentMenu("My game/IsEaten")]
public class IsEaten : MonoBehaviour
{
public bool Eaten;
}
Yay! I can then add another script to access 'Eaten'
using UnityEngine;
using System.Collections;
public class Test : MonoBehaviour
{
private Eaten someScript;
// Use this for initialization
void Start ()
{
someScript = GetComponent<IsEaten>();
bool temp = someScript.Eaten;
print(temp); // false
}
}
Which works fine. What do I have to do to access the variable with dot notation from another script? ie
if (myCube.eaten == true)
{
// do something
}
You know, in Unity one does rarely create the whole script to add a single property to some object. The common approach is to think of scripts as 'components' (which they are, in fact). Let me explain this, a component is a single piece of code that add certain functionality to your GameObject, like ability to animate, or to behave the laws of physics. So, maybe, it would be better to reform your IsEaten class to form a true component, like Pickup (I'm assuming that you need Eaten property for the pickup of some sort) that will have functionality to be eaten by a player, or something.
// You will actually need to attach a collider (and check the 'IsTrigger' checkbox on it) to your GameObject
// Then this method will be called whenewer some game object with a collider (e. g. player, npc's, monsters) will enter the trigger of this object
void OnTriggerEnter(Collider other)
{
// Check whether this object was eaten before and if the actor entering our trigger is actually the player
if (!Eaten && other.tag == "Player")
{
// Make shure that we will not get eaten twice
Eaten = true;
// Apply some effect to the player that has devoured us
other.GetComponent<Player>().AddHp(25);
}
}
Other than that, I'm personally thinking, that getting out of your way to simply enable a little sweeter syntax is not worth the hassle, but, if you provide some insight of what are you actually trying to implement, I may try to help you with it :)
One way to do it might be using Get/Set:
using UnityEngine;
using System.Collections;
public class Test : MonoBehaviour
{
private Eaten someScript;
// start new!
public bool eaten
{
get
{
return someScript.Eaten;
}
set
{
someScript.Eaten = value;
}
}
// end new!
// Use this for initialization
void Start ()
{
someScript = GetComponent<IsEaten>();
bool temp = someScript.Eaten;
print(temp); // false
}
}
Then you can access the Test class property:
Test t = GetComponent<Test>();
t.eaten = true;
ath.
I'm trying to add a Quest-object to a Person. It succeeds for one and gives a nullreferenceexception for the other, what am I doing wrong here?
P.S. The player and requestor are set in the Unity inspector.
public class GameCreator : MonoBehaviour {
private Quest quest;
public Player player;
public Requestor requestor;
void Start() {
quest = createQuest();
requestor.thisPerson.SetQuest(quest); //this is the problem
player.thisPerson.SetQuest(quest);
}
}
public class Player : MonoBehaviour {
public Person thisPerson;
void Start() {
thisPerson = new Person("Name");
}
}
public class Requestor: MonoBehaviour {
public Person thisPerson;
void Start() {
thisPerson = new Person("Name");
}
}
public class Person {
public Quest quest;
void SetQuest(Quest quest) {
this.quest = quest;
}
}
Any suggestions why this is going wrong?
Move your variable initialization in to Awake(), see the documentation for the following (paraphrased):
Awake is used to initialize any variables or game state before the
game starts.... and use Start to pass any information back and forth.
The way your GameCreator.Start() is written you are reliant on the arbitrary order in which Unity calls your scripts. GameCreator could be the first object called, in which case none of your other scripts have initialized their values.
Other possible errors:
You don't explicitly instantiate requestor, I'm going to assume this was done in Unity's Inspector.
You didn't include `createQuest()' which could be returning null.
As Jordak said, your Start methods can run in any possible order, so you can't rely on Start of some component in the other. You have several ways to address this issue:
You can move the basic initialization code to Awake(). However, this only allows you two levels of initialization, and can be insufficient in the future.
You can adjust script priority in the project settings. However, this is not really C# way, as this makes your code rely on logic that is not obvious from it.
Instead of initializing thisPerson field in the class initialization, create a public property to access it. (Public fields are bad practice in C# anyway). In this property, you can check if the field is null before returning it, and if it is, initialize it.