I got stuck in pretty dumb situation: I'm making new instance of the generic class but it returns "weird" null.
Rule rule2 = new Rule(); // initiate the class
Debug.Log(rule2); //1st debug
rule2.RuleSetup(r: "CaughtEnough", li: 0); //setting up the parameters
Debug.Log(rule2.rule); //2nd debug
1st debug gives me
null
UnityEngine.Debug:Log(Object)
at the same time setting up the parameters works, and 2nd debug gives me
CaughtEnough
UnityEngine.Debug:Log(Object)
which is what supposed to be in the proper class instance.
One (only so far) issue that it is bringing to me is that if whitin this Rule class instance I call
Invoke(rule, 0f);
it gives me the NullReferenceException error. But at the same time the actual function
CaughtEnough();
works just fine and as expected.
Any ideas what could be the source of the problem and how to overcome it?
UPD also posting setup part of Rule class, as asked, though it is straightforward
public class Rule : MonoBehaviour {
public string rule;
public int leftInt;
public Dictionary<string, int> leftDict;
public float countdown;
public int outcome;
public CatchManager catchMan;
public Net net;
// Use this for initialization
void Start () {
RuleSetup();
}
public void RuleSetup(string r = "NoRule", int li = 0, Dictionary<string, int> ld = null, float cd = float.PositiveInfinity) {
rule = r;
leftInt = li;
leftDict = ld;
countdown = cd;
}
.....
public class Rule : MonoBehaviour{}
Rule rule2 = new Rule();
You can't use new keyword to create new instance if you are inheriting from MonoBehaviour.
You should get exception that says:
You are trying to create a MonoBehaviour using the 'new' keyword.
This is not allowed. MonoBehaviours can only be added using
AddComponent(). Alternatively, your script can inherit from
ScriptableObject or no base class at all
Your code would have worked if you had public class Rule {} but you have public class Rule : MonoBehaviour {}.
Creating new instance of class that derives from MonoBehaviour:
Example class:
public class Rule : MonoBehaviour
{
public Rule(int i)
{
}
}
If you inherit from MonoBehaviour, you should either use GameObject.AddComponent or Instantiate to create new instance of it.
Rule rule2 = null;
void Start()
{
rule2 = gameObject.AddComponent<Rule>();
}
OR
public Rule rulePrefab;
Rule rule2;
void Start()
{
rule2 = Instantiate(rulePrefab) as Rule;
}
If the Rule script already exist and is attached to the GameObject, you don't need to create/add/instantiate new instance of that script. Just use GetComponent function to get the script instance from the GameObject it is attached to.
Rule rule2;
void Start()
{
rule2 = GameObject.Find("NameObjectScriptIsAttachedTo").GetComponent<Rule>();
}
You will notice that you cannot use the parameter in the constructor when you derive your script from MonoBehaviour.
Creating new instance of class that does NOT derives from MonoBehaviour:
Example class: (Note that it does not derive from "MonoBehaviour"
public class Rule
{
public Rule(int i)
{
}
}
If you don't inherit from MonoBehaviour, you should use the new keyword to create new instance of it. Now, you can use the parameter in the constructor if you want.
Rule rule2 = null;
void Start()
{
rule2 = new Rule(3);
}
EDIT:
In the latest version of Unity, creating new instance of a script that inherits from MonoBehaviour with the new keyword may not give you error and may not be null too but all the callback functions will not execute. These includes the Awake, Start, Update functions and others. So, you still have to do it properly as mentioned at the top of this answer.
Just a a follow up, how I ended up doing it and why:
I no longer inherit the Rule class from MonoBehaviour to avoid tracking cretion and deletion of the gameObjects, which appeared to be the pain.
As Invoke method does not exist in generic classes, I replaced it with reflection, as described here
Related
First i'd like to give a short version of my question:
How can i access another code pieces attached to another game object, or how can i initiazlie a class without have an game object attched.
When i making a small game in Unity, i made an unit designer where you give some value such as how many weapon does it carry, and the status of that unit (attack, range, speed, etc.) will be calculated by ComputeValues() and saved when you click confirm. But all those values were adjusted by clicking a button instead of direct input. (I.e. Click a button and add/reduce 1 weapon)
However, when i try to add some template unit at start up it won't work. So i made a CreateDesignWithValue() function. Which takes input for all the related data, and use the ComputeValues() above to compute the value for that object.
The problem is i'm trying to do it in player class. But i can't create new ShipDesigner, and neither can i set it to static. How can i get access to it?
Without knowing you exact usecase and what the methods do you are talking about we can only give a very general answer:
Not all classes have to be of type MonoBehaviour it really depends on your needs.
Extension Methods
If you have a certain calculation for a certain type you can use Extension Methods like
public static class Vector3Extensions
{
public static Vector3 DevideBy(this Vector3 a, Vector3 b)
{
return new Vector(a.x / b.x, a.y / b.y, a.z / b.z);
}
}
which you can use like e.g.
var newVector = transform.position.DevideBy(new Vector(1, 2, 3));
in all other classes.
public static class
In general you can use a public static class to implement methods and store values that shall be executable from everywhere e.g.
public static class Settings
{
private static int _currentInt = 7;
public static void SaySomething(string something)
{
Debug.Log(something);
}
public static void DoubleCurrentInt()
{
_currentInt *= 2;
}
public static int GetSquareOfCurrentInt()
{
return _currentInt * _currentInt;
}
}
which you can call now from everywhere like
Settings.DoubleCurrentInt();
Settings.SaySomething(Settings.GetSquareOfCurrentInt.Tostring);
Instances
Ofcourse sometimes you do not want that something is accessible from everywhere so you can also simply have a normal instanced class for your calculation like
public class Settings
{
private int _currentInt = 7;
public Settings(int initialInt = 0)
{
_currentInt = initialInt;
}
public void SaySomething(string something)
{
Debug.Log(something);
}
public void DoubleCurrentInt()
{
CurrentInt *= 2;
}
public int GetSquareOfCurrentInt()
{
return CurrentInt * CurrentInt;
}
}
So you can use
private Settings settings;
private void Start()
{
new Settings(3);
}
in one MonoBehaviour and
private Settings settings;
private void Start()
{
new Settings(26);
}
in another MonoBehaviour, both have different instances but can use all the implemention in it for calculating and doing stuff individually.
public static void
you can also only "share" one method among all instances of a certain type (static) and also allow other types to access it (public)
public class A : MonoBehaviour
{
// A prefab only this specific component has access to
[SerializeField] private GameObject prefab;
// example for a kind of singleton pattern
private static GameObject prefabSingleton;
private void Start()
{
prefabSingleton = prefab;
}
public static void Spawn(int someIntToAssign, string someTextToAssign)
{
var obj = Instantiate(prefabSingleton)
;
componentReference = obj.GetComponent();
componentReference.someIntField = someIntToAssign;
componentReference.Getcomponent<Text>().text = someTextToAssign;
}
}
this you can call from other types as well like
A.Setup(someExampleReference, "Yeay!");
(in this example you could consider to rather implement it in SomeExampleType, though ^^)
ScriptableObjects
What you described also sounded like ScriptableObjects (Tutorial) might be interesting for you.
ScriptableObjects are kind of assets similar to prefabs but can store values and also methods. You than can reference them in fields of MonoBehaviour components to change their behaviour according to the values or in order to share it as kind of container between multiple instances and different types.
Instance with public method
Last but not least the most "usual" of doing it would be to have a
public class A : MonoBehaviour
{
[SerializeField] private Transform someObject;
public Vector3 GetObjectPosition()
{
return someObject.position;
}
}
and access it via one of the many GetComponent or/and FindObjectOfType variants or simply by referencing the according component like
public class B : MonoBehaviour
{
// drag in via the Inspector
public A AReference;
private void Start()
{
// or get it on runtime e.g.
AReference = GameObject.Find("ObjectWithA").GetComponent<A>();
// or if there is only one e.g.
AReference = FindObjectOfType<A>();
Debug.Log(AReference.GetObjectPosition());
}
}
Answer of short versions:
How can i access another code pieces attached to another game object:
Declare a public field for the script you want to reach e.g. public ExampleScript exampleScript; and assign the gameobject which has ExampleScript to your field in the inspector.
how can i initiazlie a class without have an game object attched: You can't create an instance of a script derived from MonoBehaviour just like new ExampleScript();. But instead you can add that script to your existing gameobject with gameObject.AddComponent<ExampleScript>(); and you can reach this script from another script which is attached the very same gameObject like: gameObject.GetComponent<ExampleScript>();
I have a problem that I want to solve. I have two classes
public class Class1
{
[ReadOnly] public int Selector
private void Start()
{
Selector = Random.Range(0, 4);
Debug.Log("Selectorul " + selector);
}
}
public class Class2
{
private Class1 sp;
private void Start()
{
Debug.log(sp.Selector);
}
}
I'm a beginner and I want to understand why when I start Unity the second class the selector is always 0?
maybe I do not get right!!!
Help!!!
It seems like you are using Unity.
In Unity, the Start method of a script is only called if the class derives from MonoBehaviour, which yours does not at the moment.
Fix this by simply replacing
class Class1
with
class Class1 : MonoBehaviour
Now, if you drag the script onto a GameObject, the Start method of Class1 should get called.
You never initialize the seed for the random number generator so every attempt will generate the same number (in your case 0).
Use Random.initState (int seed) before Selector = Random.Range(0, 4);
But again this might not even be your problem. Since both Debug.log() methods are in Start() how do you make sure which one is ran first? Also the Classes are not derived from MonoBehaviour so do you call Start() yourself? or?
For instance Class2 Start() might run before Class1 sets the value of selector.
I got stuck in pretty dumb situation: I'm making new instance of the generic class but it returns "weird" null.
Rule rule2 = new Rule(); // initiate the class
Debug.Log(rule2); //1st debug
rule2.RuleSetup(r: "CaughtEnough", li: 0); //setting up the parameters
Debug.Log(rule2.rule); //2nd debug
1st debug gives me
null
UnityEngine.Debug:Log(Object)
at the same time setting up the parameters works, and 2nd debug gives me
CaughtEnough
UnityEngine.Debug:Log(Object)
which is what supposed to be in the proper class instance.
One (only so far) issue that it is bringing to me is that if whitin this Rule class instance I call
Invoke(rule, 0f);
it gives me the NullReferenceException error. But at the same time the actual function
CaughtEnough();
works just fine and as expected.
Any ideas what could be the source of the problem and how to overcome it?
UPD also posting setup part of Rule class, as asked, though it is straightforward
public class Rule : MonoBehaviour {
public string rule;
public int leftInt;
public Dictionary<string, int> leftDict;
public float countdown;
public int outcome;
public CatchManager catchMan;
public Net net;
// Use this for initialization
void Start () {
RuleSetup();
}
public void RuleSetup(string r = "NoRule", int li = 0, Dictionary<string, int> ld = null, float cd = float.PositiveInfinity) {
rule = r;
leftInt = li;
leftDict = ld;
countdown = cd;
}
.....
public class Rule : MonoBehaviour{}
Rule rule2 = new Rule();
You can't use new keyword to create new instance if you are inheriting from MonoBehaviour.
You should get exception that says:
You are trying to create a MonoBehaviour using the 'new' keyword.
This is not allowed. MonoBehaviours can only be added using
AddComponent(). Alternatively, your script can inherit from
ScriptableObject or no base class at all
Your code would have worked if you had public class Rule {} but you have public class Rule : MonoBehaviour {}.
Creating new instance of class that derives from MonoBehaviour:
Example class:
public class Rule : MonoBehaviour
{
public Rule(int i)
{
}
}
If you inherit from MonoBehaviour, you should either use GameObject.AddComponent or Instantiate to create new instance of it.
Rule rule2 = null;
void Start()
{
rule2 = gameObject.AddComponent<Rule>();
}
OR
public Rule rulePrefab;
Rule rule2;
void Start()
{
rule2 = Instantiate(rulePrefab) as Rule;
}
If the Rule script already exist and is attached to the GameObject, you don't need to create/add/instantiate new instance of that script. Just use GetComponent function to get the script instance from the GameObject it is attached to.
Rule rule2;
void Start()
{
rule2 = GameObject.Find("NameObjectScriptIsAttachedTo").GetComponent<Rule>();
}
You will notice that you cannot use the parameter in the constructor when you derive your script from MonoBehaviour.
Creating new instance of class that does NOT derives from MonoBehaviour:
Example class: (Note that it does not derive from "MonoBehaviour"
public class Rule
{
public Rule(int i)
{
}
}
If you don't inherit from MonoBehaviour, you should use the new keyword to create new instance of it. Now, you can use the parameter in the constructor if you want.
Rule rule2 = null;
void Start()
{
rule2 = new Rule(3);
}
EDIT:
In the latest version of Unity, creating new instance of a script that inherits from MonoBehaviour with the new keyword may not give you error and may not be null too but all the callback functions will not execute. These includes the Awake, Start, Update functions and others. So, you still have to do it properly as mentioned at the top of this answer.
Just a a follow up, how I ended up doing it and why:
I no longer inherit the Rule class from MonoBehaviour to avoid tracking cretion and deletion of the gameObjects, which appeared to be the pain.
As Invoke method does not exist in generic classes, I replaced it with reflection, as described here
I have two classes. One called GameManager and another one Enemies.
I have two variables in GameManager which I have changed from inspector currentLevel=1 and totalEnemy=10.
// GameManager.cs
private static GameManager instance = new GameManager();
public static GameManager get(){ return instance; }
public int currentLevel;
public int curLevel { get; set; }
public int totalEnemy;
public int totLevel { get; set; }
void Start () {
curLevel = currentLevel;
totLevel = totalEnemy;
}
I'm trying to access these two variable from Eneimes class like this; but everytime it gives me curLevel = 0, but I'm expecting to get curLevel = 1. What I'm doing wrong?
// Enemies.cs
void Start () {
Debug.Log (GameManager.get().curLevel); // always output = 0
}
The line private static GameManager instance = new GameManager(); is the issue.
When a script is attached to a GameObject, an instance of the type of the script is referenced as this inside the script. In other words, there can be multiple instances of same type if the same script is attached to multiple GameObjects.
Therefore, the specific instance that have curLevel = 1 as you set in the Inspector is an instance of the type attached to the specific GameObject. This means the one should be referred to as this inside the script.
If you declare a new instance of GameManager as in your code, you are basically ignoring all values in the Inspector because the static GameManager instance is pointing to a different instance than the instance you set values for in the Inspector.
In order to use the specific instance that you declared using the Inspector, you should do the following.
using System.Collections.Generic;
using System.Collections;
using UnityEngine;
public class GameManager : MonoBehaviour
{
private static GameManager instance;
public static GameManager get() { return instance; }
public int currentLevel;
public int curLevel { get; set; }
public int totalEnemy;
public int totLevel { get; set; }
void Awake()
{
if (instance == null)
{
instance = this;
}
else
{
Debug.LogError(string.Format("GameManager.Awake(): More than one instances of this type {0} is being initialised but it's meant to be Singleton and should not be initialised twice. It is currently being initialised under the GameObject {1}.", this.GetType(), this.gameObject.name));
Destroy(gameObject);
}
curLevel = currentLevel;
totLevel = totalEnemy;
}
}
Note that I changed Start() to Awake(). This is because you are referring to values initiliased in this method from other scripts, and you cannot guarantee which Start() is called first between different MonoBehaviours in the runtime. However, Unity guarantees that Awake() is always called earlier than Start(). Further, it is Unity's best practice to initialise self-initialisable variables in Awake(), and initialise variables dependent on other scripts in Start() because of this execution order.
Lastly, there will be problems when there are multiple GameObject that has GameManager as its component in your scene. Consider a case where you have two such objects. when the scene is loaded, each of the script will call Awake(), and both of them will set private static GameManager instance; to each of the two this. The result would be one is overriden by another.
You could say that you will be careful to use this script and make sure only one GameObject has this script as its component. However, you should always write your code as if someone who do not know about your code can use it without thinking, and stupid mistakes of other people new to the project could be easily detected.
EDIT:
To respond to the OP's comment, I added code to handle when this type is initialised more than once in the project. In addition to #Kardux's suggestion, I added Debug.LogError() because I do not want the project to silently solve things. If a problem happens, I want to get notified of it.
If you are using Singletons frequently in your project, you might want to have a parent abstract class Singleton that handles this instance checking process for all child Singletons, and have GameManager inherit from Singleton.
However, use Singleton with care as it is considered a bad design pattern if misused. (And I don't know how to use it properly so I avoid using it.)
I'm currently attempting to use a Singleton as a global data structure for Task organization in a game I'm making in Unity.
My Singleton class code is as follows:
public class TaskManager : MonoBehaviour
{
private List<Task> Tasks;
private static TaskManager instance;
private TaskManager()
{
Tasks = new List<Task>();
}
public static TaskManager Instance
{
get
{
if(instance == null)
{
instance = new TaskManager();
}
return instance;
}
}
}
I used this example as a basis for my class:
https://msdn.microsoft.com/en-us/library/ff650316.aspx
However, the problem is, when I try to access the TaskManager in different scripts, the values don't get saved.
For example, in one script I do:
TaskManager tm = TaskManager.Instance;
Task newTask = new Task();
tm.PushTask(newTask);
print(tm.GetTaskList().Count);
Through this I can see that the TaskManager has a count of 1, showing the new task.
But then my other script attempts to read from the TaskManager:
TaskManager tm = TaskManager.Instance;
List<Task> l = tm.GetTaskList();
print(l.Count);
When I do this, the Count is returned as 0, showing that the above task from the world has not been saved to the TaskManager.
I'm pretty sure the error is resulting from me misunderstanding how to use Singletons. Do I need to implement a set property for TaskManager Instance? Or is there another mistake I'm making?
Thanks!
Edit:
The PushTask() code is as follows:
public void PushTask(Task t)
{
Tasks.Add(t);
}
Personally, I don't think that solution that you chose is a perfect one. For some time, I tried using "abstract" classes that didn't inherit from MonoBehaviours, to decouple logic from Unity mechanics — but found that this makes code bloated and unnecessary complicated. In fact, every class that doesn't only contain data but has some logic of its own ends up being a MonoBehaviour sooner or later, in my experience.
So, instead of removing inheritance from MonoBehaviour, I'd solve it by implementing the usual MonoBehaviour singleton pattern:
using UnityEngine;
public abstract class Singleton<T> : MonoBehaviour where T : Singleton<T>
{
public static T Instance { get; private set; }
protected virtual void Awake()
{
if (Instance == null)
{
Instance = (T) this;
}
else
{
Debug.LogError("Got a second instance of the class " + this.GetType());
}
}
}
And then just inheriting your class from it.