Unity Custom Editor NullReferenceException When SerializedProperty Has Any Accessor - c#

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

Related

Awake function issues in relation to Singleton

So I was looking into implementing the new Unity input system into my game by reworking my code in my PlayerController script and got stuck pretty early on.
Here is the code that causes issues:
private PlayerInput playerInputController;
private void Awake()
{
playerInputController = new PlayerInput();
}
A green scribbly line appears under the 'Awake()' bit.
The code itself actually works, but it causes a lof of other issues, which makes a bunch of error messages appear saying:
NullReferenceException: Object reference not set to an instance of an object
The error messages all lead to places where I've used lines of code like this one in other scripts, to get variables and methods from my PlayerController script:
if (PlayerController.instance.isGroundedPhys)
Lastly, here's my singleton script, since it's likely relevant, as it's connected to my PlayerController script and allows it to be accessible from other scripts:
using UnityEngine;
public class Singleton<Instance> : MonoBehaviour where Instance : Singleton<Instance>
{
public static Instance instance;
public virtual void Awake()
{
if (!instance)
{
instance = this as Instance;
}
else
{
Destroy(gameObject);
}
}
}
This bit is at the top of my PlayerController script to make it accessible from other scripts:
public class PlayerController : Singleton<PlayerController>
{
I've been looking around and can't find anybody talking about similar issues. I think it's related to my singleton script, but I can't figure it out. :[
[Edit]:
I just found out it also gives this warning in the Unity console:
warning CS0114: 'PlayerController.Awake()' hides inherited member 'Singleton<PlayerController>.Awake()'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword.
PlayerInput is a MonoBehaviour!
No seriously, this is "Not Allowed" and you should actually give you an according warning in the console.
A MonoBehaviour "can"/should only exist attached to an according GameObject and always be created by the underlying c++ framework. It's a c# thing that Unity can't fully prevent you from using new to create one.
The only valid way of creating an instance of a MonoBehaviour are
Instantiate a prefab that has the component attached
use AddComponent on an existing GameObject
use the constructor of GameObject and pass in the according component type(s)
Or you probably rather want to use e.g. GetComponent or FindObjectOfType in order to get a reference to an already existing instance of PlayerInput.
And then the other warning means you implemented
public void Awake()
{
// ...
}
or similar in your PlayerController class. The warning already tells you exactly what to rather do:
// Use "override" in order to not hide the already existing implementation
// of "Awake" in the base class
public override void Awake()
{
// Make sure the base class behavior is executed
// this will initialize the Singleton thing
base.Awake();
// ... your additional behavior here
}

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 minimize some of the stats in your script UI - Unity

I am searching in google for my problem, but it is hard to find any information on the web when you don't know the exact word for it.
I saw this in a video and I really would like to know how can I do that. You can see a screenshot below about it:
If anyone knows the name of it or how to do it, i would appreciate it a lot.
To get the same visual as I stated in comment ( expandable element ) you need to either create your own EditorScript or use smaller data models.
Example usage of data models:
Let's say you want to display player's statistics inside of that expandable element, all you have to do is to create a class for that element ( PlayerStatistics and mark it with Serializable attribute:
[Serializable]
public class PlayerStatistics
{
public float Health;
}
Now in your MonoBehaviour add this as a member field :
public class MeBehaviour
: MonoBehaviour
{
public PlayerStatistics PlayerStats;
}
Default editor behavior should automatically wrap PlayerStatistics inside an expandable element.
Another way is to use an array or List<> that will do basically the same.

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!

Unity: Publics don't show up in default references/inspector..?

I've been trying for hours now, but I have no idea what is wrong.
Normally, when you define a public object/var in Unity C#, it shows up in the inspector or default references where you can drag something to it.
Here's my test code:
using UnityEngine;
using System;
using System.Collections;
[Serializable]
public class modGlobals : MonoBehaviour {
public static GUIStyle defaultMask;
public GUIStyle trollo;
}
Aaaaaand...
I tried several options, wrote the thing in MonoDev as also in Visual Studio, put all my files in an namespace, w and w/o Serializable attribe but.. What could be wrong?
Thanks for help!
Yes you do need to attach it to a gameObject in the scene in order to show the public members in the property inspector, since your script inherits from MonoBehaviour.
This is what I got.
Additionally, you cannot expose static members in the property inspector - even when marked as SerializableAttribute. System.SerializableAttribute is not valid on this declaration type. It is valid on `class, struct, enum, delegate' declarations only.
In relation to your comment about requiring a globally accessible script with objects, you have a couple options, such as
Singleton script attached to gameObject (example), set with DontDestroyOnLoad() so it persists between scene changes.
Static class

Categories