How to pass value to constructor from unity inspector - c#

I have a base abstract Character class I created (it extends MonoBehaviour) which contains some data, one of which is maxHealth. I don't want the maxHealth to be changed and so I made it readonly and use a property to change it (public float MaxHealth { get; }).
I want different characters to have different max hp values so I tried to use a constructor and pass in the max health there but I can't pass a value to the constructor from the unity inspector.
Does anyone have any idea how to pass a value to a constructor from the Unity inspector or any other way to achieve what I'm trying to do (make readonly hp values for different characters and to be able to edit them from just the unity inspector).

MonoBehaviour instances are created using AddComponent, not by using new(). Because you are not creating the instance using the new keyword, there is no constructor to access.
If you want a variable that can be set from the inspector, but not public, use [SerializeField] on a private/protected field.
[SerializeField]
private float maxHealth;
public float MaxHealth => maxHealth; // Allow others to use this value, but not change it

Related

Unity: Use Inherited properties component or Inspector Injected components

I'm new to Unity but have development experience. I'd like to know whether I should be using the inherited properties in my MonoBehaviour scripts, or if I should be injecting everything from the inspector instead.
I'm following a tutorial that has suggested writing some code like this:
public class BirdScript : MonoBehaviour
{
public Rigidbody2D myRigidbody;
So that the Inspector can inject the component's Rigidbody2D into it:
When playing with the code, I noticed that MonoBehaviour extends Component and I can do this.rigidbody2D instead. I got told off by Intellisense for this, but it suggested instead using: GetComponent<Rigidbody2D>().
Using this, instead of my code reading:
void Update()
{
myRigidbody.velocity = Vector2.up * 10;
}
I can write:
void Update()
{
GetComponent<Rigidbody2D>().velocity = Vector2.up * 10;
}
But which is better? I'm thinking GetComponent<Rigidbody2D>() because it makes sure that I'm directly referencing the current rigid body that my script is attached to, instead of requiring me to apply things in the Inspector, but maybe this is too inflexible for Unity? In general, the style seems more lenient towards making things public instead of private for the sake of the Inspector.
Any advice on some best practices I can use to make these kinds of decisions?

Unity Custom Editor NullReferenceException When SerializedProperty Has Any Accessor

I am trying to make a custom editor in Unity for a ScriptableObject class which has private fields like such:
But has soon as one of my fields has an accessor (I also tried properties with get;), I get the following error when I try to see my ScriptableObject in the inspector.
I made some tests and it works perfectly without accessors. For instance, I can see the field "test".
Here is the code for my custom editor:
Any idea? I would not believe a custom editor for a class that has accessors would not be supported.
Thank you!
This has nothing to do with an "accessor". And note that properties are not serialized by Unity at all.
The mistake is pretty simple: Your field's name is not ingredients but rather _ingredients!
Therefore FindProperty("ingredients") returns null since it doesn't find any field called ingredients.
(This is assuming of course that Item is a [Serializable] type at all.)
To avoid exactly this type of issues I usually prefer to embed the editor into the type itself and use e.g.
public class YourType : MonoBehaviour /*or ScriptableObject*/
{
[SerializeField] private int _someField;
#if UNITY_EDITOR
[CustomEditor(typeof(YourType))]
private class YourTypeEditor : Editor
{
private SerializedProperty _someFieldProperty;
private void OnEnable()
{
_someFieldProperty = serializedObject.FindProperty(nameof(_someField));
}
...
}
#endif
}
so whenever you rename the fields it will be fixed in the Inspector automatically

Best way to declare class properties in Unity3D

I am new to Unity 3D development and I wanted to know what is the best way to declare attributes of a class to be assigned later from the editor.
I have seen mainly two:
public class MyObject : MonoBehavour {
public GameObject relatedObect;
}
and
public class MyObject : MonoBehavour {
[SerializeField]
private GameObject relatedObect;
}
I understand that it is of the second form because the encapsulation is maintained, but I am not sure.
Please consider that in the example you declared class fields, and not properties.
They are both members of a class, but they are quite different.
For the sake of this answer I'll focus on your example, so I'll talk about fields.
Both public and [SerializeField] private let you inject a value in the Unity Inspector, but they act differently in the scripts.
1 - public field
You declare a public field when you want it to be used or set from another class. This choice goes against encapsulation, because other script may access it. It still might be an intended behaviour if you require the field to be accessible.
2 - [SerializeField] private field
You use this when you want to set the item in the inspector, but you do not want the other classes to see it. This option is better for encapsulation, because in this way no other script may access or change this field.
Second is the way to go if you want to do things "properly". But unfortunately there is a "bug" that throws warning on every private you set via the editor. I saw on github that it is finally getting fixed, but in the meantime if you want to remove the warning you can put your variables between pragma warning disable 0649 and pragma warning restore 0649
In addition to Jack Mariani's answer, I want to mention RangeAttribute and HideInInspectorAttribute.
[Range (min, max)]
If there's one, the inspector will show a slider instead of just a number. You move a slider and the value changes in specified range.
[SerializeField] [Range (0, 5)] private float _speed;
Documentation
[HideInInspector]
You should use it when you do not want a public variable to show up in the inspector (they are shown by default):
[HideInInspector] public float X;
Documentation

How to set different classes at runtime in Unity

I am making a game in Unity C# where the character will have different characterstics and movement functions in each scene. So I am trying to have different scripts for a player in different scenes, while all of them inheriting from the same base class. The definition will be something like below
public class PlayerScript1 : PlayerBase { /* for scene 1*/
public void Move(){
/* my movement code*/
}
}
Similarly, I have a separate class for Scene2 as
public class PlayerScript2 : PlayerBase { /* for scene 2*/
public void Move(){
/* my movement code*/
}
}
Now the problem is, my other scripts, like HealthScript,ScoreScript etc they do not change with scene. But they do access PlayerScript1 script. And thats why, I have the PlayerScript1 declaration in them. Like below:
public class HealthScript : MonoBehaviour {
PlayerScript1 playerScript;
void Start(){
/*accessing the script attached to game object*/
}
}
So how can I have my Health Script access different instances of my PlayerScript based on the scene? I know I could use delegates to call different methods in runtime, but how can I do the same with classes?
So how can I have my Health Script access different instances of my PlayerScript based on the scene?
Well first, you'll want to declare that object as of Type PlayerBase as you will be unable to assign an instance of PlayerScript2 to a variable of type PlayerScript1: those classes might inherit from the same parent, but they are not the same and you cannot convert from one to the other.
After that you will need to search for the player object in the scene, something like...
void Start(){
playerScript = GameObject.Find("Player").GetComponent<PlayerBase>();
}
Assuming, of course, that PlayerBase extends MonoBehaviour. If it doesn't you can't get a reference this way (as it won't exist in the scene at all). Additionally if you want this health object to persist from scene to scene, you need to call DontDestroyOnLoad() for it (as well as remembering that if you don't start testing from Scene 1 where this object is, it won't exist at all, or if you have a copy in every scene, you'll have duplication problems).
Someone answered to my question on the Unity forum which cleared all my doubts:
The key was using the below line in my HealthScript :
PlayerBase player = (PlayerBase)FindObjectOfType(typeof(PlayerBase));
http://answers.unity3d.com/answers/1348311/view.html

Why does C# use [System.Serializable] for keeping instance? (Unity3D)

I wanted to make a programme if it take a TextAsset from inspector(it is placed in 'ExampleEditor'), it make a custom data instance for use blendshape animation.
At first I made my custom data class not using [System.Serializable].
public class Matrix {
public int row;
public int col;
public double[,] mat;
}
My strategy was taking a TextAsset, parsing string from TextAsset and finally making a Matrix instance to a field variable in 'Example.cs'. I thought when it was initialized and assigned first, I was able to use that variable in Example instance. However, It did not work, It threw 'NullReferenceException'.
So I found solution using '[System.Serializable]'. It did work. (and I knew that Unity3D does not provide multi-dimensional array for their serialization method.)
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
[System.Serializable]
public class Matrix {
[System.Serializable]
public class mRow {
public List<float> aRow = new List<float>();
}
[HideInInspector]
[SerializeField]
public int row;
[HideInInspector]
[SerializeField]
public int col;
//[HideInInspector]
[SerializeField]
public List<mRow> mat = new List<mRow>();
}
Finally, I know the solution, But I don't know why this problem was happened. It is related to GC?
Your question isn't very clear, but I think to know what you are not understanding.
My strategy was taking a TextAsset, parsing string from TextAsset and
finally making a Matrix instance to a field variable in 'Example.cs'.
You didn't explain how you did that (perhaps call the parsing function from a custom inspector?).
However, despite on how you managed to initialize a field from the editor, if the field isn't serializable, it will be null when switching from editor to play mode.
That happens because when entering play mode, Unity3D serializes all C# classes populating the relative C++ classes of the engine. If a field isn't a public field of a type that Unity can serialize (or even a private field marked with a SerializeFieldAttribute, always of a type that Unity can serialize ), Unity3D won't serialize it.
So when you switch to play mode the reference will be null.
In other word, all fields that you want to initialize from the editor MUST be serializable in order to be used in play mode.
[SerializeField]
public List<mRow> mat = new List<mRow>();
The code above doesn't make much sense. If you plan to intialize a serializable field from the inspector don't initialize it by code, otherwise you could eventually lose the reference.
An example of such an error:
[SerializeField]
private List<int> aList;
public void Start()
{
aList = new List<int>();
}
The code above will cause troubles. If you had initialized aList from inspector, it will be correctly serialized but when you switch in play mode e the Start callback is called, aList will be reinitialized and you'll end up with a reference to an empty list.
Here's a nice blog post explaining details on how Unity3D handle serialization of objects. Read it!

Categories