Mismatch between portraits and dialogue when stopping a conversation and starting a new one
Posted: Sun Mar 10, 2024 6:43 pm
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:
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"
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!
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);
}
}
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);
}
}
}
}
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!