Strange behaviour when inheriting parent class with Monobehaviour

Hi all,

I am having some strange behavior with Unity and my code itself. Now I can firmly say the cause is the inheritance of a parent class with Monobehaviour as it could be a bug. My issues is that sometimes my code works, sometimes it just doesn’t, closing Unity often seems to break it, and only way to fix it is to delete the Prefab and drag it back into the scene. Details below.

I have the following :

  • Task Bar Manager- This is attached to a prefab with a Slider and text. Is going to keep track of all the tasks that need to be done.

  • Task Controller - This is the Parent class that inherits Monobehaviour, this class is inherited by the individual task controllers.

  • Individual Task Controller (Computer Controller) - This is the class that inherits Task Controller, Task Controller contains all the generic task code and the individual task controller will contain code specific to this task.

Code for each class is shown below. Further details on the issues below. ps. Sorry for weird formatting.

public class TaskBarManager : MonoBehaviour {

public static Canvas canvas;
public static Slider slider;
public static Text text;

public static int tasksTotalCount = 0;
public static int tasksCompleted = 0;
public static int tasksRemaining = 0;

private static float targetProgress = 0;
private float fillSpeed = 0.5f;

// Start is called before the first frame update
void Start()
{

    canvas = GetComponent<Canvas>();
    slider = GetComponentInChildren<Slider>();
    text = GetComponentInChildren<Text>();

    Debug.Log("Task-Bar finalised.");
    
}

// Update is called once per frame
void Update()
{
    if (slider.value <= tasksCompleted) {
          slider.value += fillSpeed * Time.deltaTime;
    }   
}

public static void IncrementProgress(float newProgress) {
   targetProgress = newProgress;
}

public static void UpdateTasksCompleted(int Value) {
    tasksCompleted = Value;
    IncrementProgress(Value);

    text.text = "Tasks Remaining " + tasksCompleted + " / " + tasksTotalCount;

    TaskBarDidComplete();
}

public static void UpdateTaskBarMaxValue(int Value) {
    tasksTotalCount = Value;
    slider.maxValue = Value; 
    
    text.text = "Tasks Remaining " + tasksCompleted + " / " + tasksTotalCount;
}

public static void TaskBarDidComplete() { // - Maybe an Event would be good here?
    if (tasksCompleted == tasksTotalCount) {
        SceneManager.LoadScene("Game-Over");
    }
}

}

public class TaskController : MonoBehaviour {

[HideInInspector] public Canvas canvas;
[HideInInspector] public CircleCollider2D detectionRadius;
[HideInInspector] public SpriteRenderer spriteRenderer;

[HideInInspector] public TaskManager taskManager;

[HideInInspector] public Slider progressBar;
[HideInInspector] public Text interactionText;

// - SETUP

public void InstantiateTask(Task task) {
    
    canvas = gameObject.GetComponent<Canvas>();
    detectionRadius = gameObject.GetComponent<CircleCollider2D>();
    spriteRenderer = gameObject.GetComponent<SpriteRenderer>();

    taskManager = gameObject.GetComponent<TaskManager>();

    progressBar = gameObject.GetComponentInChildren<Slider>();
    interactionText = gameObject.GetComponentInChildren<Text>();
    
    ShowProgressBar(false);
    SetInteractionTextPrefix(task.taskPrefix);
    ShowInteractionText(false);

    Debug.Log(task.taskName + " instantiated.");

}

// - USER INTERFACE HANDLING

public void ShowCanvas(bool Bool) {
    canvas.gameObject.SetActive(Bool);
}

public void ShowInteractionText(bool Bool) {
    interactionText.gameObject.SetActive(Bool);
}

public void SetInteractionTextPrefix(string Prefix) {
    interactionText.text = "Hold 'Left-Click' to " + Prefix;
}

public void ShowProgressBar(bool Bool) {
    progressBar.gameObject.SetActive(Bool);
}

// - COLLISION HANDLING

public void HandleTriggerEnter2D(Collider2D collider, Task task) {

    if (!task.isCompleted) {
        if (detectionRadius.IsTouching(collider.gameObject.GetComponent<BoxCollider2D>())) {
            task.SetState(Task.TaskState.Hightlight);
            spriteRenderer.sprite = task.activeSprite;

            ShowInteractionText(true);
        }
    }
}

public void HandleTriggerExit2D(Collider2D collider, Task task) {

    if (!task.isCompleted) {
        task.SetState(Task.TaskState.Default);
        spriteRenderer.sprite = task.activeSprite;

        ShowInteractionText(false);
        ShowProgressBar(false);
    }
}

    // CANVAS OVERLAY

private float targetProgress = 0;

public void CalculateProgress(Task task) {

    if (progressBar.gameObject.activeInHierarchy && Input.GetButton("Fire1")) {
        
        IncrementProgress(1f);

        if (progressBar.value <  targetProgress) {
            progressBar.value += task.openSpeed * Time.deltaTime;
        }

        if (progressBar.value == 1) {
            task.SetState(Task.TaskState.Complete);
            spriteRenderer.sprite = task.activeSprite;
            ShowProgressBar(false);

            taskManager.OnTaskComplete();

            if (!task.canBeCompleted) {
                progressBar.value = 0;
                task.isCompleted = false;
                targetProgress = 0;
            }
        }

    } else if (progressBar.gameObject.activeInHierarchy) {
        ShowProgressBar(false);
        ShowInteractionText(true);
    }
}

public void IncrementProgress(float newProgress) {
   targetProgress =  progressBar.value + newProgress;
}

// COLLISION HANDLING

public void OpenTaskListener() { // NEEDS TO BE RECODED - JUST FOR TESTING
    
    if (Input.GetMouseButtonDown(0)) {

        GameObject player = GameObject.Find("Player-Prefab");
        PlayerController pc = player.GetComponent<PlayerController>();

        if (!pc.player.GetPlayerIsThing()) {
            
            BoxCollider2D collider = player.GetComponent<BoxCollider2D>();

            if(detectionRadius.IsTouching(collider.gameObject.GetComponent<BoxCollider2D>())) {
                ShowProgressBar(true);
                ShowInteractionText(false);
            }
        }else{ 
            Debug.Log("Player is the Thing, cannot fix JB.");
        }
    }
}

}

public class ComputerController : TaskController, TaskManager
{

public Task task;

void Start()
{
    InstantiateTask(task);
    TaskBarManager.UpdateTaskBarMaxValue(TaskBarManager.tasksTotalCount+1);
}

void Update()
{
    CalculateProgress(task);
    OpenTaskListener(); 
}

private void OnTriggerEnter2D(Collider2D collider) {
    HandleTriggerEnter2D(collider, task);
}

private void OnTriggerExit2D(Collider2D collider) {
    HandleTriggerExit2D(collider, task);
}

// - INTERFACE

public void OnTaskOpen() {
    Debug.Log("Task has opened.");
}

public void OnTaskComplete() { 
    TaskBarManager.UpdateTasksCompleted(TaskBarManager.tasksCompleted+1);
}

}

As mentioned above, for some reason I get strange behavior. One of the following will happen.

  1. The Task counter will stay at 0 / 0, or rather be set to 0 / 0. It’s almost like the TaskBarManager class is loading before the individual Task classes and not getting updated.

  2. Closing Unity seems to unset the Object Reference, I get a NullReferenceException (NullReferenceException: Object reference not set to an instance of an object
    TaskBarManager.UpdateTaskBarMaxValue (System.Int32 Value) (at Assets/Tasks/TaskBarManager.cs:60))
    which can only be fixed by deleting the prefab and dragging it back to the scene. (This happens on re-opening Unity after closing it)

  3. The counter will update the max value won’t, i.e. it will say 0 / 2 but when one is completed the slider will act as if the Max value is only one.

Please point out if I have made any architectural mistakes in my code,

Any help is greatly appreciated,

Thanks

Sounds like a serialization issue. Static variables are shared by all instances of a class and cannot be serialized. This would explain why you are losing the connection to your prefab when you quit, and probably also on recompilation.

I haven’t followed through your code with a fine toothed comb, but it seems like the problem has something to do with your use of static variables.