- Home /
Question by
Redsam121 · Sep 16, 2020 at 12:38 PM ·
nullreferenceexceptionscriptableobjectinventory systemobject referenceinstances
Nullreference Exception with ScriptableObject
I am receiving the error NullReferenceException: Object reference not set to an instance of an object with my current code. The error was traced to PickedUpItemReaction.cs:13 and DelayedReaction.cs:25.
I'm also getting the error UnityException: StartTracker is not allowed to be called from a ScriptableObject constructor (or instance field initializer), call it in OnEnable instead. Called from ScriptableObject 'ReactionCollectionEditor'.
My attempts to fix the problem have to add [Serializable] to no effect.
Have I missed something.
Item1.cs
using System;
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
[CreateAssetMenu]
[Serializable]
public class Item1 : ScriptableObject
{
public Sprite sprite;
public string ItemName;
}
PickUpItemReaction.cs
public class PickedUpItemReaction : DelayedReaction
{
public Item1 item; // The item asset to be added to the Inventory.
private Inventory inventory; // Reference to the Inventory component.
protected override void SpecificInit()
{
inventory = FindObjectOfType<Inventory>();
}
protected override void ImmediateReaction()
{
inventory.AddItem(item);
}
}
DelayedReaction.cs
using UnityEngine;
using System.Collections;
public abstract class DelayedReaction : Reaction
{
public float delay;
protected WaitForSeconds wait;
public new void Init ()
{
wait = new WaitForSeconds (delay);
SpecificInit ();
}
public new void React (MonoBehaviour monoBehaviour)
{
monoBehaviour.StartCoroutine (ReactCoroutine ());
}
protected IEnumerator ReactCoroutine ()
{
yield return wait;
ImmediateReaction ();
}
}
Reaction.cs
using UnityEngine;
public abstract class Reaction : ScriptableObject
{
public void Init ()
{
SpecificInit ();
}
protected virtual void SpecificInit()
{
}
public void React (MonoBehaviour monoBehaviour)
{
ImmediateReaction ();
}
protected abstract void ImmediateReaction ();
}
ReactionCollection.cs is where Init() begins:
using UnityEngine;
public class ReactionCollection : MonoBehaviour
{
public Reaction[] reactions = new Reaction[0];
private void Start ()
{
for (int i = 0; i < reactions.Length; i++)
{
DelayedReaction delayedReaction = reactions[i] as DelayedReaction;
if (delayedReaction)
{
delayedReaction.Init();
}
else
{
reactions[i].Init();
}
}
}
public void React ()
{
for (int i = 0; i < reactions.Length; i++)
{
DelayedReaction delayedReaction = reactions[i] as DelayedReaction;
if(delayedReaction)
{
delayedReaction.React(this);
}
else
{
reactions[i].React(this);
}
}
}
}
And the final code ReactionCollectionEditior()
using System;
using UnityEngine;
using System.Collections.Generic;
using UnityEditor;
[CustomEditor(typeof(ReactionCollection))]
public class ReactionCollectionEditor : EditorWithSubEditors<ReactionEditor, Reaction>
{
private ReactionCollection reactionCollection;
private SerializedProperty reactionsProperty;
private Type[] reactionTypes;
private string[] reactionTypeNames;
private int selectedIndex;
private const float dropAreaHeight = 50f;
private const float controlSpacing = 5f;
private const string reactionsPropName = "reactions";
private readonly float verticalSpacing = EditorGUIUtility.standardVerticalSpacing;
private void OnEnable()
{
reactionCollection = (ReactionCollection)target;
reactionsProperty = serializedObject.FindProperty(reactionsPropName);
CheckAndCreateSubEditors(reactionCollection.reactions);
SetReactionNamesArray();
}
private void OnDisable()
{
CleanUpEditors();
}
protected override void SubEditorSetup(ReactionEditor editor)
{
editor.reactionsProperty = reactionsProperty;
}
public override void OnInspectorGUI()
{
serializedObject.Update();
CheckAndCreateSubEditors(reactionCollection.reactions);
for (int i = 0; i < subEditors.Length; i++)
{
subEditors[i].OnInspectorGUI();
}
if (reactionCollection.reactions.Length > 0)
{
EditorGUILayout.Space();
EditorGUILayout.Space();
}
Rect fullWidthRect = GUILayoutUtility.GetRect(GUIContent.none, GUIStyle.none, GUILayout.Height(dropAreaHeight + verticalSpacing));
Rect leftAreaRect = fullWidthRect;
leftAreaRect.y += verticalSpacing * 0.5f;
leftAreaRect.width *= 0.5f;
leftAreaRect.width -= controlSpacing * 0.5f;
leftAreaRect.height = dropAreaHeight;
Rect rightAreaRect = leftAreaRect;
rightAreaRect.x += rightAreaRect.width + controlSpacing;
TypeSelectionGUI(leftAreaRect);
DragAndDropAreaGUI(rightAreaRect);
DraggingAndDropping(rightAreaRect, this);
serializedObject.ApplyModifiedProperties();
}
private void TypeSelectionGUI(Rect containingRect)
{
Rect topHalf = containingRect;
topHalf.height *= 0.5f;
Rect bottomHalf = topHalf;
bottomHalf.y += bottomHalf.height;
selectedIndex = EditorGUI.Popup(topHalf, selectedIndex, reactionTypeNames);
if (GUI.Button(bottomHalf, "Add Selected Reaction"))
{
Type reactionType = reactionTypes[selectedIndex];
Reaction newReaction = ReactionEditor.CreateReaction(reactionType);
reactionsProperty.AddToObjectArray(newReaction);
}
}
private static void DragAndDropAreaGUI(Rect containingRect)
{
GUIStyle centredStyle = GUI.skin.box;
centredStyle.alignment = TextAnchor.MiddleCenter;
centredStyle.normal.textColor = GUI.skin.button.normal.textColor;
GUI.Box(containingRect, "Drop new Reactions here", centredStyle);
}
private static void DraggingAndDropping(Rect dropArea, ReactionCollectionEditor editor)
{
Event currentEvent = Event.current;
if (!dropArea.Contains(currentEvent.mousePosition))
return;
switch (currentEvent.type)
{
case EventType.DragUpdated:
DragAndDrop.visualMode = IsDragValid() ? DragAndDropVisualMode.Link : DragAndDropVisualMode.Rejected;
currentEvent.Use();
break;
case EventType.DragPerform:
DragAndDrop.AcceptDrag();
for (int i = 0; i < DragAndDrop.objectReferences.Length; i++)
{
MonoScript script = DragAndDrop.objectReferences[i] as MonoScript;
Type reactionType = script.GetClass();
Reaction newReaction = ReactionEditor.CreateReaction(reactionType);
editor.reactionsProperty.AddToObjectArray(newReaction);
}
currentEvent.Use();
break;
}
}
private static bool IsDragValid()
{
for (int i = 0; i < DragAndDrop.objectReferences.Length; i++)
{
if (DragAndDrop.objectReferences[i].GetType() != typeof(MonoScript))
return false;
MonoScript script = DragAndDrop.objectReferences[i] as MonoScript;
Type scriptType = script.GetClass();
if (!scriptType.IsSubclassOf(typeof(Reaction)))
return false;
if (scriptType.IsAbstract)
return false;
}
return true;
}
private void SetReactionNamesArray()
{
Type reactionType = typeof(Reaction);
Type[] allTypes = reactionType.Assembly.GetTypes();
List<Type> reactionSubTypeList = new List<Type>();
for (int i = 0; i < allTypes.Length; i++)
{
if (allTypes[i].IsSubclassOf(reactionType) && !allTypes[i].IsAbstract)
{
reactionSubTypeList.Add(allTypes[i]);
}
}
reactionTypes = reactionSubTypeList.ToArray();
List<string> reactionTypeNameList = new List<string>();
for (int i = 0; i < reactionTypes.Length; i++)
{
reactionTypeNameList.Add(reactionTypes[i].Name);
}
reactionTypeNames = reactionTypeNameList.ToArray();
}
}
Comment