Non-MonoBehaviour object with SerializableField

Hi, I’m trying to implement health for my player character and enemies.
My class looks like this:

[Serializable]
public class Health {

  [SerializeField] private int totalHealthPoints;

  public Health() {
    HealthPoints = totalHealthPoints;
  }

  public int TotalHealthPoints => totalHealthPoints;

  public int HealthPoints { get; private set;  }

  public bool IsEmpty => HealthPoints <= 0;

  public void TakeDamage(int damagePoints) {
    HealthPoints -= damagePoints;
  }

}

I have another class that checks the HealthPoints in their Update method (with a reference to my PlayerCharacter class which is a MonoBehaviour with a IsAlive property) and ends the game if the player has no health left. My problem is that the game ends immediately and if I log the value of totalHealthPoints in the constructor, it’s always 0, no matter what I input in the editor.

Is there no way to access a serialized value in the constructor without making the class a MonoBehaviour? The only other way to solve this problem would be to introduce an event to the Health, which the “observer” can listen to.

public class PlayerCharacter : MonoBehaviour {

  [SerializeField] private Health health;

  public bool IsAlive => !health.IsEmpty;

}

public class PlayerHealthObserver : MonoBehaviour {

  [SerializeField] private PlayerCharacter playerCharacter;

  private void Update() {
    if (playerCharacter.IsAlive)
      return;

    EndGame();
  }

  private void EndGame() {
    // Does stuff to end the game.
  }

}

Hello, have you consider using ScriptableObject for that task? simply create the scriptable object and use it for serialization

using UnityEngine;
[CreateAssetMenu(fileName = "Health", menuName = "ScriptableObjects/Health", order = 1)]

public class HealthScriptable : ScriptableObject
{
    [SerializeField] private int totalHealthPoints;

    public int TotalHealthPoints => totalHealthPoints;

    public int HealthPoints { get; private set; }

    public bool IsEmpty => HealthPoints <= 0;

    private void OnEnable()
    {
        HealthPoints = totalHealthPoints;
    }

    public void TakeDamage(int damagePoints)
    {
        HealthPoints -= damagePoints;
    }
}

I was able to find a solution to my problem: Unity 4.5 introduced the ISerializationCallbackReceiver interface. It declares two methods called OnBeforeSerialize and OnAfterDeserialize. I implemented the interface on my Health class to set the healthPoints variable after deserialization.

[Serializable]
public class Health : ISerializationCallbackReceiver {

  [SerializeField] private int totalHealthPoints;

  public int TotalHealthPoints => totalHealthPoints;

  public int HealthPoints { get; private set; }

  public bool IsEmpty => HealthPoints <= 0;

  public void TakeDamage(int damagePoints) {
    HealthPoints -= damagePoints;
  }

  public void OnBeforeSerialize() {
  }

  public void OnAfterDeserialize() {
    HealthPoints = totalHealthPoints;
  }

}

The only down-side in my case is the empty OnBeforeSerialize method.

ISerializationCallbackReceiver Documentation link