HasNext() in PlayerConversant has a null error

After finishing the Aggro Group lecture my HasNext is now getting a null error message:
NullReferenceException: Object reference not set to an instance of an object
RPG.Dialogue.PlayerConversant.HasNext () (at Assets/Scripts/Dialogue/PlayerConversant.cs:91)
RPG.UI.DialogueUI.UpdateUI () (at Assets/Scripts/UI/DialogueUI.cs:55)
RPG.UI.DialogueUI.Start () (at Assets/Scripts/UI/DialogueUI.cs:30)

Dialogue currentDialogue is not set to null at the beginning but even setting it to null does not change the error. The script project now does not like the message and I am not sure why.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

namespace RPG.Dialogue
{
    public class PlayerConversant : MonoBehaviour
    {
        Dialogue currentDialogue;
        DialogueNode currentNode = null;
        AIConversant currentConversant = null;
        bool isChoosing = false;

        public event Action onConversationUpdated;

        public void StartDialogue(AIConversant newConversant, Dialogue newDialogue)
        {
            currentConversant = newConversant;
            currentDialogue = newDialogue;
            currentNode = currentDialogue.GetRootNode();
            TriggerEnterAction();
            onConversationUpdated();
        }

        public void Quit()
        {
            currentDialogue = null;
            TriggerExitAction();
            currentNode = null;
            isChoosing = false;
            currentConversant = null;
            onConversationUpdated();
        }

        public bool IsActive()
        {
            return currentDialogue != null;
        }

        public bool IsChoosing()
        {
            return isChoosing;
        }

        public string GetText()
        {
            if (currentDialogue == null)
            {
                return "";
            }
            return currentNode.GetText();
        }

        public IEnumerable<DialogueNode> GetChoices()
        {
            return currentDialogue.GetPlayerChildren(currentNode);
        }

        public void SelectChoice(DialogueNode chosenNode)
        {
            currentNode = chosenNode;
            TriggerEnterAction();
            isChoosing = false;
            // onConversationUpdated(); // Only call onConversationUpdated() here if not calling Next().
            Next(); // Moves directly to the next node in dialogue rather than display button response text.
        }

        public void Next()
        {
            int numPlayerResponses = currentDialogue.GetPlayerChildren(currentNode).Count();
            if (numPlayerResponses > 0)
            {
                isChoosing = true;
                TriggerExitAction();
                onConversationUpdated();
                return;
            }

            DialogueNode[] children = currentDialogue.GetAIChildren(currentNode).ToArray();
            int randomIndex = UnityEngine.Random.Range(0, children.Count());
            TriggerExitAction();
            currentNode = children[randomIndex];
            TriggerEnterAction();
            onConversationUpdated();
        }

        public bool HasNext()
        {
            return currentDialogue.GetAllChildren(currentNode).Count() > 0;
        }

        private void TriggerEnterAction()
        {
            if (currentNode != null)
            {
                TriggerAction(currentNode.GetOnEnterAction());
            }
        }

        private void TriggerExitAction()
        {
            if (currentNode != null)
            {
                TriggerAction(currentNode.GetOnExitAction());
            }
        }

        private void TriggerAction(string action)
        {
            if (action == "")
            {
                return;
            }

            foreach (DialogueTrigger trigger in currentConversant.GetComponents<DialogueTrigger>())
            {
                trigger.Trigger(action);
            }
        }
    }
}

Then here is the code that is for the rest of the error when the rest of the error:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using RPG.Dialogue;
using TMPro;
using UnityEngine.UI;
using System;

namespace RPG.UI
{
    public class DialogueUI : MonoBehaviour
    {
        PlayerConversant playerConversant;
        [SerializeField] TextMeshProUGUI AIText;
        [SerializeField] Button nextButton;
        [SerializeField] GameObject AIResponse;
        [SerializeField] Transform choiceRoot;
        [SerializeField] GameObject choicePrefab;
        [SerializeField] Button quitButton;
        [SerializeField] TextMeshProUGUI conversantName;

        // Start is called before the first frame update
        void Start()
        {
            playerConversant = GameObject.FindGameObjectWithTag("Player").GetComponent<PlayerConversant>();
            playerConversant.onConversationUpdated += UpdateUI;
            nextButton.onClick.AddListener(() => playerConversant.Next());
            quitButton.onClick.AddListener(() => playerConversant.Quit());

            UpdateUI();
        }

        // Update is called once per frame
        void Update()
        {

        }

        void UpdateUI()
        {
            gameObject.SetActive(playerConversant.IsActive());
            if (playerConversant.IsActive())
            {
                return;
            }
            AIResponse.SetActive(!playerConversant.IsChoosing());
            choiceRoot.gameObject.SetActive(playerConversant.IsChoosing());
            if (playerConversant.IsChoosing())
            {
                BuildChoiceList();
            }
            else
            {
                AIText.text = playerConversant.GetText();
                nextButton.gameObject.SetActive(playerConversant.HasNext());
            }
        }

        private void BuildChoiceList()
        {
            foreach (Transform item in choiceRoot)
            {
                Destroy(item.gameObject);
            }

            foreach (DialogueNode choice in playerConversant.GetChoices())
            {
                GameObject choiceInstance = Instantiate(choicePrefab, choiceRoot);
                var textComponent = choiceInstance.GetComponentInChildren<TextMeshProUGUI>();
                textComponent.text = choice.GetText();
                Button button = choiceInstance.GetComponentInChildren<Button>();
                button.onClick.AddListener(() => 
                {
                    playerConversant.SelectChoice(choice);
                });
            }
        }
    }
}

It appears that your PlayerConversant is reporting that it is active at the beginning of the game (bypassing the check at the beginning of UpdateUI())

What I can’t see in the script is how currentDialogue is anything but null at the beginning of the game, as it appears to be null from the script.

It’s possible that it’s somehow serialized from earlier in the course… I can’t remember if at some point we serialized the currentDialogue for testing purposes…

Let’s do a little brute force to break that: Add this method to PlayerConversant:

void Awake()
{
    currentDialogue = null;
}

This will override any accidental serialization errors and ensure that currentDialogue is, in fact, null.

After adding in the new code with the Awake Method in the PlayerConversant script, I still end with a null reference, I am pointed to UpdateUI but now also when I am dealing with the conversantName variable.

Error:
NullReferenceException: Object reference not set to an instance of an object
RPG.Dialogue.PlayerConversant.GetCurrentConversantName () (at Assets/Scripts/Dialogue/PlayerConversant.cs:71)
RPG.UI.DialogueUI.UpdateUI () (at Assets/Scripts/UI/DialogueUI.cs:46)
RPG.UI.DialogueUI.Start () (at Assets/Scripts/UI/DialogueUI.cs:30)

I did a quick test and set the conversantName variable equal to null and retested but the same error message comes up.

I also tested by adding another Awake() method and setting the conversantName = null;
But it still leads me to the same error.

Maybe I missed something or the syntax is wrong when I was writing the code that is causing this chain. But I am not sure, I can try to look back at the lessons and see if I did miss something in the meantime.

Any other thoughts? It still is centering around the UpdateUI() method so I think there must be something wrong there.

I know in the previous lesson we removed the Awake() method since the code we had was moved or no longer needed. But If that is not the case here then something must have been calling to the awake or at least how we had the code setup before the Awake() method was removed.

Third look is always the charm… in this case, it’s because we’re only exiting the UpdateUI if the playerConversant is Active, not (as it should be) when it is inactive.

if(!playerConversant.IsActive()) 
{
    return;
}

That fixed it! Thank you for the help!

1 Like

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.

Privacy & Terms