Mismatch between portraits and dialogue when stopping a conversation and starting a new one

Announcements, support questions, and discussion for the Dialogue System.
Voltage3000
Posts: 14
Joined: Mon Nov 27, 2023 8:28 pm

Mismatch between portraits and dialogue when stopping a conversation and starting a new one

Post by Voltage3000 »

Hi Tony,

I'm wondering if you might be able to give me any insight into this. In our game Scrap Seas, we have ship battles where each ship has a "Pilot" associated with it. Each ship receives AI messages for when they find another enemy, when they hit 50% health, when they die, etc. Every time one of those messages is sent the ship tries to trigger a separate dialogue conversation (Each AI message is given a unique conversation to play for each character). Only one conversation is allowed by the UI at a time. However, I'm having issues with portraits being mismatched with their dialogue. So different characters may say other characters lines. Or it could be that their 50% health line will play instead of their Death line. etc.

My "DialogueBarkManager" script is mainly what runs this system:

Code: Select all

using System.Collections;

public class DialogueBarkManager : MonoBehaviour
{
    public static DialogueBarkManager Instance { get; private set; }

    IEnumerator BarkRoutine;

    [System.Serializable]
    public class AIMessageConvoPair
    {
        [SerializeField]
        string convoID;
        [SerializeField]
        AnimationVariables.AIMessage message;

        public string GetConversation()
        {
            return convoID;
        }

        public AnimationVariables.AIMessage GetAIMessage()
        {
            return message;
        }
    }


    [System.Serializable]
    public class BarkController
    {
        [SerializeField]
        string actorPersistentDataName;

        [SerializeField]
        List<AIMessageConvoPair> barkConvos;

        [SerializeField]
        string deathConvo;

        public void TriggerConvoFromAIMessage(AnimationVariables.AIMessage message, Transform actorTransform)
        {

            foreach (AIMessageConvoPair a in barkConvos)
            {
                if (a.GetAIMessage() == message)
                {

                    if (a.GetAIMessage() == AnimationVariables.AIMessage.spottedEnemy)
                    {
                        if(DialogueManager.IsConversationActive == true)
                        {
                            return;
                        }
                        else
                        {
                            DialogueBarkManager.Instance.CallBarkRoutine(a.GetConversation(), actorTransform);
                            return;
                        }
                    }

                    DialogueManager.StopConversation();

                    DialogueBarkManager.Instance.CallBarkRoutine(a.GetConversation(), actorTransform);
                }
            }
        }

        public void TriggerDeathConvo(Transform actorTransform)
        {
            DialogueManager.StopConversation();
            DialogueBarkManager.Instance.CallBarkRoutine(deathConvo, actorTransform);
         
        }

        public string GetActorPersistentDataName()
        {
            return actorPersistentDataName;
        }


    }

    [SerializeField]
    List<BarkController> barkControllers;

    private void Awake()
    {
        if (Instance == null)
        {
            Instance = this;

        }
        else
        {
            Debug.Log("YOU ARE TRYING TO MAKE MULTIPLE INSTANCES!");
            Destroy(gameObject);
        }


    }

    public BarkController GetBarkController(string actorDataName)
    {
        foreach (BarkController b in barkControllers)
        {
            if (b.GetActorPersistentDataName() == actorDataName)
            {
                Debug.Log("Found bark controller for " + actorDataName);
                return b;
            }
        }
        Debug.Log("Could not find bark controller for " + actorDataName);
        return null;
    }

    IEnumerator BarkAfterOneFrame(string convo, Transform actor)
    {
        //sequences are given one frame to resolve themselves after "Stop Conversation" is triggered.  So it's best to wait until they are resolved before moving to the next one (This appears to not fix the issue...)
        yield return null;
        DialogueManager.StartConversation(convo, actor);
    }

    public void CallBarkRoutine(string convo, Transform actor)
    {
        if(BarkRoutine != null)
        {
            StopCoroutine(BarkRoutine);
        }

        BarkRoutine = BarkAfterOneFrame(convo, actor);
        StartCoroutine(BarkRoutine);
    }
}
The DialogueBarkManager has a list of "BarkController" that have information regarding what character they are playing and what conversation to trigger for each AI message. Each ship, chooses a "BarkController" from the list to use.

When a conversation is triggered, DialogueManager.StopConversation() is called to end any conversation that is already playing. The BarkController then calls the DialogueBarkManager's "CallBarkRoutine" function. This kills any BarkRoutines that are already running and then the coroutine waits a frame before starting the conversation to allow any sequences to resolve.

This issue tends to happen most often when a lot of ships are being killed in rapid succession
. I suspect something is getting messed up because I'm ending and starting conversations so quickly. Having conversations only be triggered by "CallBarkRoutine" is meant to try and force only one conversation to be called at a time, but it seems like the issue still occurs.

I've also have suspicions that this could be related to how I change character portraits in my scripts. I've set up a script called "Dialogue Emotion Manager"

Code: Select all

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using PixelCrushers.DialogueSystem;

public class DialogueEmotionManager : MonoBehaviour
{
    [Tooltip("These will be the ids used as parameters for the set emotion function")]
    [Header("Emotion IDs")]
    [SerializeField]
    List<EmotionPair> emotions;


    [System.Serializable]
    class EmotionPair
    {
        [SerializeField]
        string ID;
        [SerializeField]
        int index;

        public string GetID()
        {
            return ID;
        }

        public int GetIndex()
        {
            return index;
        }
    }

    private void OnEnable()
    {
        Lua.RegisterFunction("SetEmotion", this, SymbolExtensions.GetMethodInfo(() => SetEmotion((string) "Emotion")));
    }


    public void SetEmotion(string ID)
    {
        StartCoroutine(SetEmotionRoutine(ID));

    }

    

    private void OnDisable()
    {
        Lua.UnregisterFunction("SetEmotion");
    }

    IEnumerator SetEmotionRoutine(string ID)
    {

        yield return new WaitForEndOfFrame();

        if (DialogueManager.currentConversationState == null)
        {
            yield break;
        }

        if (DialogueManager.currentConversationState.subtitle.speakerInfo.nameInDatabase == null)
        {
            yield break;
        }


        var actorName = DialogueManager.currentConversationState.subtitle.speakerInfo.nameInDatabase;


        var actor = DialogueManager.masterDatabase.GetActor(actorName);

        string emotionName;
        int emotionIndex;

        foreach (EmotionPair e in emotions)
        {
            if (e.GetID() == ID)
            {
                emotionName = ID;
                emotionIndex = e.GetIndex();

                DialogueLua.SetActorField(actorName, "Emotion", emotionName);
                DialogueManager.SetPortrait(actorName, "pic=" + emotionIndex.ToString());
                //Debug.Log("AM SETTING EMOTION " + emotionName + " For " + actorName);
                
            }
        }
    }
}
I've set this up to make it more intuitive to set the portraits for other users. They can type "angry" instead of remembering the index for the angry portrait. It also automatically adjusts the pitch of the character's type writer sfx. I've wondered if the fact that I use "WaitForEndOfFrame" could be causing issues in this scenario.

Sorry for the rambling message. I've kind of been chasing my tail for a while with this.


PS: I see you made it to Awesome Con. Hope it went well for you!
User avatar
Tony Li
Posts: 22112
Joined: Thu Jul 18, 2013 1:27 pm

Re: Mismatch between portraits and dialogue when stopping a conversation and starting a new one

Post by Tony Li »

Hi Calvin,

Thanks, Awesome Con was fun!

Are all of these conversations single lines of dialogue? If so, you might want to use actual barks. One nice thing about barks is that you can add BarkGroupMember components to all of your pilots. When one BarkGroupMember barks, it automatically hides any other pilots' barks.

Also, if you're using your SetEmotion() Lua function inside the conversation, by the time the conversation has started the characters' info has already been cached. (This is so each dialogue entry doesn't have to determine all of the characters' info again, since most of the time it doesn't change from entry to entry.) You can set the cached info like this:

Code: Select all

var actor = DialogueManager.MasterDatabase.GetActor(actorName);
var info = DialogueManager.ConversationModel.GetCharacterInfo(actor.id);
info.portrait = someSprite;
Voltage3000
Posts: 14
Joined: Mon Nov 27, 2023 8:28 pm

Re: Mismatch between portraits and dialogue when stopping a conversation and starting a new one

Post by Voltage3000 »

Hi Tony,

Glad Awesome Con went well!

So not all of the lines are single lines of dialogue. The majority of them are, but sometimes they'll put out 2 that are extra dramatic. I would really like to preserve this capability as I feel it adds a lot to the game/impact of the dialogue.


This is one that is used when one of our bosses, the Sentinel, is reduced to a quarter health. It's supposed to randomly choose one of these barks when it plays.
SentinelQuarterHealthBarkNodeGraph.JPG
SentinelQuarterHealthBarkNodeGraph.JPG (124.26 KiB) Viewed 471 times

They appear in battle like this.
RigPortraitDeath.png
RigPortraitDeath.png (447.02 KiB) Viewed 471 times
It's very possible that I misunderstand the limits of barks, but it seemed more appropriate to use regular dialogue to me.
Voltage3000
Posts: 14
Joined: Mon Nov 27, 2023 8:28 pm

Re: Mismatch between portraits and dialogue when stopping a conversation and starting a new one

Post by Voltage3000 »

Also, I've converted my SetEmotion script to work the way you mentioned. The idea is that it should not require a coroutine to work right?

I'm trying to break my bad habit of staying up until 1am, so I'm gonna try testing it tomorrow. I'll let you know how it goes.
Voltage3000
Posts: 14
Joined: Mon Nov 27, 2023 8:28 pm

Re: Mismatch between portraits and dialogue when stopping a conversation and starting a new one

Post by Voltage3000 »

Hi Tony,

I updated my Set Emotion function to the following:

Code: Select all

    public void SetEmotion(string name, string ID)
    {
        var actorName = name;
 
        var actor = DialogueManager.MasterDatabase.GetActor(actorName);
        var info = DialogueManager.ConversationModel.GetCharacterInfo(actor.id);


        string emotionName;
        int emotionIndex;
        foreach (EmotionPair e in emotions)
        {
            if (e.GetID() == ID)
            {
                emotionName = ID;
                emotionIndex = e.GetIndex();

                info.portrait = actor.GetPortraitSprite(emotionIndex);
                DialogueLua.SetActorField(actorName, "Emotion", emotionName);
            }
        }


    }
I've added the name field because

Code: Select all

 var actorName = DialogueManager.currentConversationState.subtitle.speakerInfo.nameInDatabase
alters the portrait for the actor on the previous entry not the current. I think that maybe why I tried to run it from a coroutine previously.

This also causes an issue because I have a character called "PlayerCharacter" which is supposed to serve as a stand in for the player's character selection. I use a "RegisterDialogueActorTransform" script to replace "PlayerCharacter" with the player's actual character selection. However this version of the Set Emotion function uses the PlayerCharacter portraits instead of the portraits of the player's selected character.

I thought that removing the coroutine from the script would likely prevent the mismatching issue from occuring. However, I've tested it and it appears to have not really made a difference, so I think I may revert it back to how it was.

Do you have any other suggestions for what I might look into to resolve this portrait mismatch issue?
User avatar
Tony Li
Posts: 22112
Joined: Thu Jul 18, 2013 1:27 pm

Re: Mismatch between portraits and dialogue when stopping a conversation and starting a new one

Post by Tony Li »

Hi Calvin,

I'll take a look as soon as I can this afternoon and get back to you.
User avatar
Tony Li
Posts: 22112
Joined: Thu Jul 18, 2013 1:27 pm

Re: Mismatch between portraits and dialogue when stopping a conversation and starting a new one

Post by Tony Li »

Hi Calvin,

In your conversation screenshot, I see that the primary participants are PlayerCharacter and Narrator. But the dialogue entry nodes are assigned to a third character, Sentinel.

If you pass Sentinel's transform as the actorTransform in:

Code: Select all

DialogueManager.StartConversation(convo, actor);
then Sentinel will stand in for PlayerCharacter. For a deeper explanation, please see Character GameObject Assignments.

If this conversation is meant to be spoken by Sentinel, I recommend setting the conversation's Actor to Sentinel and also setting the dialogue entry nodes' Actor dropdowns to Sentinel, too. That may clear up the issue by itself. To set the conversation's Actor dropdown, select Menu > Conversation Properties to see the conversation's main properties.

You shouldn't need to run a coroutine. I think getting rid of the coroutine will simplify things a lot, making it easier to identify the root issue.
Voltage3000
Posts: 14
Joined: Mon Nov 27, 2023 8:28 pm

Re: Mismatch between portraits and dialogue when stopping a conversation and starting a new one

Post by Voltage3000 »

Hi Tony, that seems like a good idea. I've set up the barks conversation I'm testing to have appropriate actor assignments. However, it does not appear to completely resolve the issue unfortunately. I'm recording video of my bug testing. I'll post it here in case you can give me any insight


Up to the first minute is a normal playthrough where these barks work 95% of the time. After the 1st minute I give myself a bunch of really powerful weapons and spawn a large number of weaker enemies so I can reliably reproduce the issue.

Looking at where the dialogue system node graphs are being highlighted, it appears that the Dialogue System is actually on the correct node, but it displays a different line on screen. I kind of wonder if maybe it has something to do with how I have typewriter fx setup.

Now that the actor assignments are appropriate it seems like if I enforce a delay of 3 seconds between stopping a conversation and starting a new one, the mismatching doesn't seem to occur. I'm probably going to experiment and see if there is a reasonable amount of time I can enforce a delay. 2 frames appears to not be sufficient.
User avatar
Tony Li
Posts: 22112
Joined: Thu Jul 18, 2013 1:27 pm

Re: Mismatch between portraits and dialogue when stopping a conversation and starting a new one

Post by Tony Li »

If 2 frames isn't sufficient, maybe it's due to show/hide animations on the dialogue UI or the subtitle panel. If used, they typically last longer than 2 frames.

Temporarily set the Dialogue Manager's Other Settings > Debug Level to Info. This will log each line as it's spoken. You can review the Console window to confirm that it's actually sending the correct line to the dialogue UI. If you confirm that the correct lines are going to the dialogue UI, then the issue is with the dialogue UI. If it's simpler in this case, you could just write your own implementation of the IDialogueUI C# interface, add it to a UI GameObject, and assign that GameObject to the Dialogue Manager. There's a starter script in Plugins / Pixel Crushers / Dialogue System / Templates / Scripts / TemplateDialogueUI.cs. Or you could try temporarily removing the animation to see if the animation delay is what's contributing to the issue.
Voltage3000
Posts: 14
Joined: Mon Nov 27, 2023 8:28 pm

Re: Mismatch between portraits and dialogue when stopping a conversation and starting a new one

Post by Voltage3000 »

Removing the animation appears to have solved the issue. Thanks Tony!
Post Reply