Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Help me understand where i should search...

Discussion in 'Scripting' started by unity_1236alman, Jun 16, 2021.

  1. unity_1236alman

    unity_1236alman

    Joined:
    Mar 4, 2019
    Posts:
    66
    Hello guys, i have a problem with a constructor. The class of this constructor is not instantiated at all, but when i press play, an error occurs from the constructor that can't find a singleton instance. I searched all the references to this class, and i set the variables of this type to Null, i don't know if this makes any sense. Another weird thing is that whenever i compile this class, the same errors occurs while in edit mode! Is the first time i use constructors, can you help me?
    Other info: The class has the modifier (?) [System.serializable] and it does't inherits monobehaviour.
    Thanks in advance!
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,756
    If it is System.Serializable, you probably have to provide a default no-argument constructor. Did you provide this?
     
  3. Lurking-Ninja

    Lurking-Ninja

    Joined:
    Jan 20, 2015
    Posts:
    9,907
    Code? Exact error message?
     
  4. unity_1236alman

    unity_1236alman

    Joined:
    Mar 4, 2019
    Posts:
    66
    Yes, i provided it
    I didn't think it is necessary but here it is

    Code (CSharp):
    1. [System.Serializable]
    2. public class PCData
    3. {
    4.     public byte level { get; private set; }
    5.  
    6.     public short maxHp { get; private set; }
    7.     public short maxMana { get; private set; }
    8.  
    9.     public string name { get; private set; }
    10.     public void SetName(string value)
    11.     {
    12.         name = value;
    13.     }
    14.  
    15.     public PCData()
    16.     {
    17.         GeneralGameData data = GeneralGameData.singleton;
    18.  
    19.         if (data != null)
    20.         {
    21.             name = data.GetRandomName();
    22.  
    23.             level = data.startingLevel;
    24.             maxHp = data.startingHp;
    25.             maxMana = data.startingMp;
    26.             uniqueId = Saver.ObtainUniqueId();
    27.         }
    28.         else
    29.         {
    30.             UnityEngine.Debug.LogError("error"); //THIS IS THE ERROR DISPLAYED IN CONSOLE
    31.         }
    32.     }
    In the data class

    Code (CSharp):
    1.  static public GeneralGameData singleton;
    2.  
    3.     private void Awake()
    4.     {
    5.         if (singleton == null)
    6.         {
    7.             singleton = this;
    8.         }
    9.         else
    10.         {
    11.             Destroy(this);
    12.         }
    13.     }
     
  5. Lurking-Ninja

    Lurking-Ninja

    Joined:
    Jan 20, 2015
    Posts:
    9,907
    Exactly this is the reason I asked for it. So, what is your plan exactly? Why are you making a general, serializable class when you have a serialized MonoBehaviour with the very same data? What's the reasoning?

    I'm assuming this
    is the GeneralGameData class. Right? (You left out the most important parts from your code you shared... it's hard to help when you aren't clear with your intentions) If that's the case, I think it is a simple race condition. The creation of the PCData class comes first, the Awake of the GeneralGameData didn't run yet, so the singleton is null.

    (BTW, comparing unity objects, including MonoBehaviours to null is very costly, should avoid whenever possible, but this is a problem for another day)
     
  6. unity_1236alman

    unity_1236alman

    Joined:
    Mar 4, 2019
    Posts:
    66
    I made that serializable class to store the player data and make it persistent between play sessions. The problem is that this constructor should be called only when i need it, and i need it when i create a new character. In the constructor it takes the starting values from the data, and i did it this way because the values can be changed from the inspector. The problem is that the constructor is called whenever i press the play button, and i don't understand why, where this call is from. I'm sorry if this is unclear, and thanks for the help.

    About this: "comparing unity objects, including MonoBehaviours to null is very costly", is there a "cheap" way to do this?
     
  7. Lurking-Ninja

    Lurking-Ninja

    Joined:
    Jan 20, 2015
    Posts:
    9,907
    I'm guessing you have this class serialized somewhere. During deserialization the parameterless constructor gets called. So probably what happens is your game gets loaded and this class gets deserialized somewhere.

    What I do is this:
    Optional.cs

    Code (CSharp):
    1. using System;
    2. using UnityEngine;
    3.  
    4. [Serializable]
    5. public class Optional<T>
    6. {
    7.     [SerializeField]
    8.     private T value;
    9.     [SerializeField]
    10.     private bool isEmpty = true;
    11.  
    12.     private Optional() {}
    13.     private Optional(T value) => Value = value;
    14.  
    15.     public T Value
    16.     {
    17.         get => value;
    18.         set
    19.         {
    20.             this.value = value;
    21.             isEmpty = value == null;
    22.         }
    23.     }
    24.  
    25.     public bool IsEmpty
    26.     {
    27.         get => isEmpty;
    28.         set => isEmpty = value;
    29.     }
    30.  
    31.     public static Optional<T> Create(T value) => new(value);
    32.     public static Optional<T> Empty() => new();
    33. }
    SingletonBehaviour.cs

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public abstract class SingletonBehaviour<T> : MonoBehaviour where T : Component
    4. {
    5.     private static readonly Optional<T> _instance = Optional<T>.Empty();
    6.  
    7.     public static T Instance => _instance.Value;
    8.  
    9.     protected void Init(T instance)
    10.     {
    11.         if(!_instance.IsEmpty)
    12.         {
    13.             Destroy(instance.gameObject);
    14.             return;
    15.         }
    16.         _instance.Value = instance;
    17.     }
    18. }
    Then you can inherit from the
    SingletonBehaviour 
    if you want a singleon and call the
    Init(this);
    from
    Awake()
    . Obviously there are simpler ways to achieve this, but I have a framework where I make use all of these on different ways.
     
    mopthrow likes this.