Making static class data serializable - c#

Consider the following:
public class myClass : MonoBehaviour
{
public int i; //<- value set from inspector
static myClass()
{
Awake();
}
private static void Awake()
{
Debug.Log(i); //<- Error CS0120
}
}
This would throw me an error error CS0120: An object reference is required for the non-static field, method, or property 'myClass.i'
I could then (according to this page) try to write it in another way:
public class myClass : MonoBehaviour
{
public int i;
static myClass()
{
Awake();
}
private static void Awake()
{
var mc = new myClass();
Debug.Log(mc.i); //<- NullReferenceException
}
}
But that would throw me error NullReferenceException: Object reference not set to an instance of an object myClass.Awake ().
Meaning that I cannot serialize static variables? Is there a workaround? I'm sorry but I'm still getting used to C# and if you could also give me a brief theoretical reason why it doesn't work it would help me greatly in understanding. Thanks!

First of all: Your issue has nothing to do with a field being serializable or not.
It is rather related to your instanced class field i not being accessible from a static context. As the error states you would need an instance of your class in order to access it there but
As also mentioned classes of type MonoBehaviour are not allowed to have any constructor and may not be instanciated via the new keyword in Unity. The only allowed ways of creating instances of components is via Instantiate, AddComponent or via the constructor of e.g. new GameObject("someName", typeof(YOUR_COMPONENT));.
You don't need a Singleton for what you want. It sounds like you actually would want to go this way round:
public class myClass : MonoBehaviour
{
// This one you set via the Inspector
[SerializeField] private int _i;
// This is now a read-only property
// That can only be set by this class
public static int i { get; private set; }
private void Awake()
{
// Your instance can always access its static fields
// So this way you can assign the value
i = _i;
}
}
In general we would need more input in order to figure out your actual usecase here. It is also possible that you could rather use an entirely static class like e.g.
public static class myClass
{
public static int i = 42;
}
this makes your field not serialized but simply accessible from everywhere without the need of an instance in the Scene. You would simply access it from another MonoBehaviour like e.g.
public class Example : MonoBehaviour
{
private void Start()
{
Debug.Log(myClass.i);
}
}
Or you might want to make your class not static at all but rather access it through the correct reference like
[Serializable]
public class myClass
{
public int i = 42;
}
public class Example : MonoBehaviour
{
// Since it is tagged Serializable and now serialized in the Inspector
// an instance is created for this field automatically
[SerializedField] private myclass _myClass;
private void Awake()
{
Debug.Log(_myclass.i);
}
}

Unity can only serialize a specific instance of an class (monobehaviour). Static information is shared between all instances of the class and thus cannot be serialized by unity.
Additionally, do not put a constructor in your monobehaviours, the object is constructed by the unity engine using its own process and the awake function is called automatically. You're getting that null reference exception because the awake function is being called from your constructor and not from unities internal functions that are supposed to be initializing it, so things that need to be set up for it haven't been.
You haven't said why you need static information serialized but if you're trying to use a singleton approach there are a couple simple methods for doing it in unity:
class MySingleton : MonoBehaviour
{
[SerializeField] private int someDataA;
[SerializeField] private int someDataB;
public static MySingleton Instance
{
get;
private set;
}
public int SomeDataA
{
get
{
return someDataA;
}
set
{
someDataA = value;
}
}
public int SomeDataB
{
get
{
return someDataB;
}
set
{
someDataB = value;
}
}
private void Awake()
{
Instance = this;
}
}
alternatively
class MySingleton : MonoBehaviour
{
[SerializeField] private int someDataA;
[SerializeField] private int someDataB;
private static MySingleton instance;
public static int SomeDataA
{
get
{
return instance.someDataA;
}
set
{
instance.someDataA = value;
}
}
public static int SomeDataB
{
get
{
return instance.someDataB;
}
set
{
instance.someDataB = value;
}
}
private void Awake()
{
instance = this;
}
}
In the first example you can access the current instance of the component by going MySingleton.Instance and access your properties and functions on it. In the second example the instance is kept private and all the properties and functions are made static so you can access them directly on MySingleton without needing to see the instance.

Related

i want to know how the static properties work in c# and i don't get the meaning of 'Instance._input;

I don't get the meaning of 'Instance._input;
1.What is the relationship between the properties 'Instance' and the instance '_input"? is it related to static?
2.I don't understand that why 'Instance" comes earlier then _input (Instance._input;)
I am sorry asking for easy question
public class managers : MonoBehaviour
{
static managers s_instance; // 유일한 매니저를 갖고온다
public static managers Instance
{
get { return s_instance; }
}
InputManager _input = new InputManager();
public static InputManager Input
{
get { return Instance._input; }
}
The Instance is a static property of class managers, it returns s_instance static variable of class managers.
Therefore Instance._input will return the instance variable _input of type InputManager of static variable Instance of type managers.
The naming conventions of your provided code could cause confusions. Consider this code:
public class Manager : MonoBehavior {
static Manager staticManager;
public InputManager inputManager = new InputManager();
public static Manager GetStaticManager() { return staticManager;}
public static InputManager GetInputManagerOfStaticManager() { return GetStaticManager().inputManager; }
}
When you declare something static, its on class scope. All objects created from the same class shared the same static copy. You access them as Manager.GetStaticManager(). Calling Manager.inputManager will fail.
On the other hand, non-static member is on object/instance scope. You need to create that object first:
var manager = new Manager();
var myInputManager = manager.inputManager;

How can i re-use a code in monobehaviour class?

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>();

How to create a dynamic variable system with Scriptable Objects

In this talk I learned how to create variables with scriptable objects, creating classes like FloatVariable, DoubleVariable, StringVariable and others. But, in the same talk, the guy said that he uses a more dynamic variable system, that prevents creating several classes to handle all variable types.
Using the first system, I had a C# script called ImageFillSetter, that given two float variables and a Image script, it returns the division of the two variables to the fillAmount variable of the image.
But, when I get a Double Variable, and I'd like to set a progress bar with this value, I need to create another script called ImageFillSetterDouble, and put in these variables. And if I needed to create one with Integers? Every time I create a script like this, I will need to create two duplicates to handle the other number variable types?
With this dynamic variable system, this problem should be solved, but I have no idea how to start/create this system.
The code looks like this:
[CreateAssetMenu(menuName="Variable/Float")]
public class FloatVariable : ScriptableObject, ISerializationCallbackReceiver
{
public float initialValue;
[NonSerialized]
public float value;
public void OnAfterDeserialize()
{
value = initialValue;
}
public void OnBeforeSerialize() { }
}
What I want is something like this (Totally hypothetical, I know that this doesn't works)
[CreateAssetMenu(menuName="Variable")]
public class Variable : ScriptableObject, ISerializationCallbackReceiver
{
public var initialValue;
[NonSerialized]
public var value;
public void OnAfterDeserialize()
{
value = initialValue;
}
public void OnBeforeSerialize() { }
}
I know there is an accepted answer that works, but I feel that the usage of ScriptableObject variables as described in the linked video was misinterpreted.
I think you would be better off making your FloatVariable independent of the calculation.
Let's say the calculation is for player health and your fill value would be calculated by currentHealth/maxHealth.
public class PlayerHealth: MonoBehaviour
{
[SerializeField] private FloatVariable floatReference;
[SerializeField] private float maxHealth;
[SerializeField] private float currentHealth;
void Update()
{
this.floatReference.value = currentHealth/maxHealth;
}
}
public class ImageFillSetter: MonoBehaviour
{
[SerializeField] private FloatVariable floatReference;
[SerializeField] private Image imageReference;
void Update()
{
this.imageReference.fill = this.floatReference.value;
}
}
Or let's say that player health is stored as double:
public class PlayerHealth: MonoBehaviour
{
[SerializeField] private FloatVariable floatReference;
[SerializeField] private double maxHealth;
[SerializeField] private double currentHealth;
void Update()
{
this.floatReference.value = (float)(currentHealth/maxHealth);
}
}
Now let's say that you add an input field where the fill value can be entered as a percentage string (like '76'):
public class FillInput: MonoBehaviour
{
[SerializeField] private FloatVariable floatReference;
[SerializeField] private Input input;
void Update()
{
if(Input.GetKeyDown(KeyCode.Enter))
{
this.floatReference.value = float.Parse(input.text)/100f;
}
}
}
The ImageFillSetter will 'observe' the FloatVariable without being aware of how that float was calculated.
This way you only ever have to have one ImageFillSetter that can be used for any image and any data source, while having 1 or more ways of altering the fill that does not require any changes to be made to ImageFillSetter.
For example, let's say that you want to use the same approach to indicate async level load progress:
public class FillInput: MonoBehaviour
{
[SerializeField] private FloatVariable floatReference;
private AsyncOperation loadOperation;
void LoadLevelAsync(string levelName)
{
this.loadOperation = SceneManager.LoadLevelAsync(levelName, LoadSceneMode.Additive);
}
void Update()
{
this.floatReference.value = this.loadOperation?.progress ?? 0;
}
}
This will work without making any other changes as long as your ImageFillSetter references the same FloatVariable.
Think of the FloatVariable (or whichever primitive you have eg. DoubleVariable) as a value stored in a database. Anyone can read the value and anyone can save a new value. It would be strange to store all possible calculations for the value in the database instead of doing the calculation and just storing the answer.
This does not change the fact that you need Scriptable implementations for each primitive:
FloatVariable
DoubleVariable
StringVariable
BoolVariable
etc
but you will only need one of each as demonstrated in the first section of derHugo's answer.
Have a look at Generics
Have one abstract class like
public abstract class ValueAsset<T> : ScriptableObject
{
public T value;
// Add your methods
// Here some more examples also using the T value. They might also be abstract but they don't have to be
// return a T
public T GetValue()
{
return value;
}
// or pass a T
public void SetValue(T input)
{
value = input;
}
}
This class you will never instantiate but now derive multiple implementations from it e.g.
[CreateAssetMenu(fileName = "new int", menuName = "ValueAssets/int")]
public class IntValue : ValueAsset<int>
{
// Maybe constructors here or additional fields and methods
}
[CreateAssetMenu(fileName = "new float", menuName = "ValueAssets/float")]
public class FloatValue : ValueAsset<float>
{
// Maybe constructors here or additional fields
}
You can also have multiple generic values like
public abstract class OtherExample<TKey, TValue> : ScriptableObject
{
// Note that this is just an example
// Dictionary is not serializable
public Dictionary<TKey, TValue> values = new Dictionary<TKey, TValue>();
public void AddPair(TKey key, TVakue value)
{
values.Add(key, value);
}
}
And implement something like
public OneImplementation : OtherExample<string, GameObject>
{
//...
}
The same way this can be used for reference values (components, GameObject etc)
So for IntValue the method GetValue will return an int and SetValue will take an int as parameter. The same way they take and return a float in FloatValue.
Doing the same thing with an ImageFillSetter<T> you can than make your method abstract and implement different behaviours for different T values (like e.g. a different parsing etc)
Note: I don't know why exactly but in the past I noticed that
public ValueAsset<T> valueAsset;
will not be serialized in the inspector even if later implemented so you have to implement the field with the correct type in the implementation instead. You also still could override it on runtime but you can skip the whole FetchValue part if you don't need it and anyway use valueReference instead - just added it for completeness.
public abstract class ImageFillSettet<T> : MonoBehaviour
{
// Will not appear in the Inspector
public ValueAsset<T> ValueAsset;
// Override this in implementation
protected abstract void FetchValue();
// Use it for Initializing the value
private void Awake ()
{
FetchValue();
}
public abstract void SetFill();
}
Than later
public class ImageFillSetterFloat : ImageFillSetter<float>
{
// Show in the inspector
[SerializeField] private FloatValue valueReference;
// Provide the reference to the base class
protected override void Fetch value()
{
valueAsset = valueReference;
}
public override void SetFill()
{
// Use valueReference for something
}
}

How to pass parameter to static class constructor?

I have a static class with a static constructor. I need to pass a parameter somehow to this static class but I'm not sure how the best way is.
What would you recommend?
public static class MyClass {
static MyClass() {
DoStuff("HardCodedParameter")
}
}
Don't use a static constructor, but a static initialization method:
public class A
{
private static string ParamA { get; set; }
public static void Init(string paramA)
{
ParamA = paramA;
}
}
In C#, static constructors are parameterless, and there're few approaches to overcome this limitation. One is what I've suggested you above.
As per MSDN, A static constructor is called automatically to initialize the class before the first instance is created. Therefore you can't send any parameters.
CLR must call a static constructor, how will it know which parameters to pass it?
So don't use a static constructor.
Here's the work around for your requirement.
public class StaticClass
{
private int bar;
private static StaticClass _foo;
private StaticClass() {}
static StaticClass Create(int initialBar)
{
_foo = new StaticClass();
_foo.bar = initialBar;
return _foo;
}
}
Static constructors have the following properties:
A static constructor does not take access modifiers or have parameters. A static constructor is called automatically to
initialize the class before the first instance is created or any
static members are referenced.
A static constructor cannot be called directly.
The user has no control on when the static constructor is executed in the program.
A typical use of static constructors is when the class is using a log file and the constructor is used to write entries to this file.
Static constructors are also useful when creating wrapper classes for unmanaged code, when the constructor can call the LoadLibrary
method.
If a static constructor throws an exception, the runtime will not invoke it a second time, and the type will remain uninitialized for
the lifetime of the application domain in which your program is
running.
If by "HardCodedParameter" you really mean hard coded, you can use constants.
public static class YoursClass
{
public const string AnotherHardCodedParam = "Foo";
}
public static class MyClass
{
private const string HardCodedParam = "FooBar";
static MyClass()
{
DoStuff(MyClass.HardCodedParam);
DoStuff(YoursClass.AnotherHardCodedParam);
}
}
Also, you can use static readonly properties.
Constructors on non-static class have have the benefit to ensure they're properly initialized before they're actually being used.
Since static classes don't have this benefit, you have to make ensure that yourself.
Use a static constructor with an obvious name, then in the relevant portion of your static procedures check to make sure the initialization has been performed.
The example below assumes your want to "initialize" your static class with a Form object.
public static class MyClass
{
private static Form FormMain { get; set; }
public static void Init(Form initForm)
{
FormMain = initForm;
}
private static bool InitCheck()
{
return FormMain != null ? true: false;
}
public static void DoStuff()
{
if (InitCheck())
{
// Do your things
}
else
{
throw new Exception("Object reference not set to an instance of an object");
}
}
}

How to cache static data for classes with use of inheritance?

Imagine you're making a game object that is using some texture:
public class Action {
//This two can (must) be overriden
public const string _HoverCursor = "Textures/cursors/select";
public virtual string HoverCursor { get { return _HoverCursor; } }
//This is the get texture by string
private static Texture2D cursorTex = null;
public static Texture2D cursor { get { return ResourceManager.loadTexture(ref cursorTex, HoverCursor); } }
}
The loadTexture function will load the texture if null is passed. otherwise, it just returns the original value.
As you can see, I'm making an Action prototype. There will be actions like Attack, Move or Build. I want to store one (and different) texture object per class definition. This object shall be lazy-loaded when needed using the getter above.
Since the static property can't be overriden, how can I implement this for all children instances?
Here's the loadTexture:
public static Texture2D loadTexture(ref Texture2D target, string name)
{
if (target == null)
{
target = (Texture2D)Resources.Load(name, typeof(Texture2D));
}
return target;
}
You can use inheritance and polymorphism with instance members, and use a static factory method as a façade to simplify the instantiation of the implementation.
For example
public abstract class Action
{
public abstract void LoadTexture(...);
}
// Since static façade class has a generic type parameter, we're talking
// about a completely different class than just "Action" and both can co-exist!
public static class Action<TAction> where TAction : Action, new()
{
public static Texture2D LoadTexture(...)
{
// Since generic TAction parameter must implement a public parameterless
// constructor, you may instantiate T like a concrete class:
return new TAction().LoadTexture(...);
}
}
Thus, you would use the static method as follows:
Texture2D texture = Action<CustomAction>.LoadTexture2D(...);
You've practically answered the question yourself:
Since the static property can't be overriden, how can I implement this for all children instances?
Just make the property an instance property. For example:
public class Action {
//This two can (must) be overriden
public const string _HoverCursor = "Textures/cursors/select";
public virtual string HoverCursor { get { return _HoverCursor; } }
//This is the get texture by string
private static Texture2D cursorTex = null;
public virtual Texture2D cursor
{
get
{
return ResourceManager.loadTexture(ref cursorTex, HoverCursor);
}
}
}
public class Attack {
//This two can (must) be overriden
public const string _HoverCursor = "Textures/cursors/attack";
public virtual string HoverCursor { get { return _HoverCursor; } }
//This is the get texture by string
private static Texture2D cursorTex = null;
public override Texture2D cursor
{
get
{
return ResourceManager.loadTexture(ref cursorTex, HoverCursor);
}
}
}
You would still save just one texture per class, but given an instance of each class you'll be able to retrieve the correct cursor texture for that class. Presumably you need to set the cursor only when there's an instance of the class around anyway, so this shouldn't be an unreasonable limitation.
Note in the above that if no other code actually needs the HoverCursor property, you can get rid of it, make the _HoverCursor const a private member, and then use that directly in the cursor property getter.
Also note that this implementation isn't thread-safe. As long as you're always accessing the cursor property from a single thread, that's fine. But if not (or perhaps just for the simplicity) you may prefer using the Lazy<T> class. For example:
//This is the get texture by string
private static Lazy<Texture2D> cursorTex =
new Lazy<Texture2D>(() => ResourceManager.loadTexture(HoverCursor));
public virtual Texture2D cursor
{
get
{
return cursorTex.Value;
}
}
In this version, note that the loadTexture() method has changed to always load the texture, and thus does not need the ref parameter. The Lazy<T> class will ensure thread-safe lazy initialization, calling loadTexture() only once per class.

Categories

Resources