#DialogManagement
Explore tagged Tumblr posts
Text
Conversational Turn Management: Keeping Multi-Turn Dialogues Coherent
In extended conversations, agents must manage the flow of dialogue, deciding when to ask questions, give updates, or take initiative.
This involves:
Discourse structure tracking
Intent recognition across turns
Turn-taking strategies (e.g., pausing, prompting)
Well-managed turns make agents feel more natural and less robotic. See how dialogue flow is engineered in modern AI agents.
Classify each user input by dialogue act (e.g., question, request, correction) to improve turn predictions.
1 note
·
View note
Text
Im Universum von Dragon's Dogma 2, einem Ort, an dem Abenteuer und Gefahr hinter jeder Ecke lauern, finden sich Spieler von Vasallen umgeben – NPC-Alliierte mit nützlichen Einsichten, aber auch einer Neigung zu wiederholendem Dialog. Hier kommt der ShutUpPawns Mod ins Spiel, eine von der Community getriebene Lösung, die das Spielerlebnis durch das Stummschalten unnötiger Vasallen-Kommentare verbessert. Entwickelt vom Modder emoose, ermöglicht ShutUpPawns den Spielern, ihr Hörerlebnis anzupassen, um zwischen immersiver Anleitung und ruhiger Stille die perfekte Balance zu finden. Ein besonders gefeiertes Feature des Mods ist sein differenzierter Umgang mit dem Dialogmanagement. Anstatt die Vasallen komplett zum Schweigen zu bringen, erlaubt es Spielern, bestimmte Dialogzeilen selektiv zu blockieren. Diese Anpassung erstreckt sich auf verschiedene Spielinteraktionen, einschließlich der oft unnötigen Abklatschen nach dem Kampf – ein kleines Detail, das für einige den Spielfluss stört. Der ShutUpPawns Mod nutzt das REFramework und hebt die Findigkeit der Modding-Community hervor, vorhandene Tools zu nutzen, um das Spielerlebnis in Dragon's Dogma 2 zu verbessern. Mit der Entwicklung des Mods wurden Updates eingeführt, die fortgeschrittene Funktionen wie Geofencing für Dialoge ermöglichen, sodass bestimmte Gespräche nur an spezifischen Spielorten stattfinden, sowie ein System zur Verwaltung der Dialoghäufigkeit basierend auf vom Spieler definierten Parametern. Diese Verbesserungen spiegeln das Engagement wider, auf das Feedback der Community zu reagieren und das Spielerlebnis durch benutzerfreundliche Schnittstellen und anpassbare Einstellungen zu verbessern. Die begeisterte Aufnahme von ShutUpPawns innerhalb der Gaming-Community, belegt durch zahlreiche Empfehlungen auf Nexus Mods, unterstreicht die Nachfrage nach personalisierten Spielerlebnissen. Es stellt einen wichtigen Schritt in Richtung immersiverem und spielerzentriertem Spieldesign dar und ermöglicht Abenteurern in Gransys, die Spielgeschichte und das World-Building auf ihre eigene Art zu erleben. Für diejenigen, die sich diesen Mod näher anschauen möchten, ist ein manueller Installationsprozess notwendig. Doch die Mühe lohnt sich für ein persönlicheres und potenziell angenehmeres Spielerlebnis, frei von den repetitiven Kommentaren, die von der immersiven Welt von Dragon's Dogma 2 ablenken können. Weitere Informationen und Ressourcen zum Thema Modding und Spieleverbesserung finden sich in einem aufschlussreichen Artikel auf PC Gamer, der erläutert, wie Mods wie ShutUpPawns bedeutende Beiträge zur Gaming-Community leisten. Zusätzlich bietet die Analyse des Matchmaking-Systems von Call of Duty einen tiefen Einblick in die Komplexität von Online-Spielumgebungen. Entdecke mehr über ShutUpPawns auf PC Gamer
0 notes
Note
How on earth did you make such a nice dialogue system? Your other posts are really informative so I figured I'd ask how you coded it, there aren't really any sort of tutorials on this for Unity...
I’m not sure I could detail all of it in a single post, but I’ll do an overview of the structure of the system. If you want me to go into the specifics of any part of the system, just send me another ask and I’ll gladly answer.
Data structures: So if you don’t know about ScriptableObject, I urge you to go learn about it because it can be a very powerful tool. To explain quickly: when you extend MonoBehaviour, your class becomes a component that you can put on game objects, but when you extend ScriptableObject, the class becomes an asset. This is useful because you can have an asset that you can modify straight in the inspector (and for more freedom, you can also create a custom inspector for it).
In my case, I created 2 of those ScriptableObject. The first one is a DialogBlock, which is a container for everything that happens every time the player advance the dialog (text, animations, sounds, etc.). It looks something like this:
The important point here is that every DialogBlock knows what the next DialogBlock is. If the DialogBlock is a question (represented by a bool), then I can put a different DialogBlock for every answer.
My other ScriptableObject is simply a Dialog, which represents a starting point of a dialog. It only contains a reference to the first DialogBlock. This is useful for progression because a DialogBlock with no “Next Dialog Block” (meaning that the conversation will end at this block) can have a “Next Dialog” reference. This will make it so the NPC will change its Dialog reference and have a different conversation starting point the next time you talk to it.
DialogManager & LocalizationManager: For a lot of more global tasks, I use manager classes (singletons). Let’s say you have a NPC component on a game object and this component has a reference to a Dialog asset. When you initiate the conversation, all you have to do is pass the Dialog reference to the DialogManager and it will handle everything. This means that when the conversation starts, the manager will handle showing the UI and setting up the camera as well as processing every DialogBlock every time the player advances the conversation. Now, this is a manager, it shouldn’t actually DO everything. Instead, it should call the appropriate objects that will handle it.
As for the LocalizationManager, I have a text file for every level, so it listens to a callback of the SceneManager and loads up the correct localization file when changing level. This is why the DialogBlock asset doesn’t contain a Text variable and instead contains a Key, this is to go fetch the text from the localization dictionary.
Scrolling text: A simple coroutine can do the trick if you use
yield return new WaitForSeconds(1 / (# of characters per seconds) );
As I said, it is a bit more involved for rich text. Before I start writing, I need to parse it and remove all the markup tags and keep them stored with their position in the string so I can add them correctly. Learning about Regex will prove useful at this part. This is the result I want (let’s say I want to write the word “Word” with a markup tag):
W
Wo
Wor
Word
There is also the problem of formatting as pointed out in the comments:
But this is something I left for later so I can’t really help you more than that. @stijnidek gave a good explanation here so that could get you started.
Cameras: Like my previous post, I used Cinemachine Base Rig for the cameras inside the DialogBlock. For the initial camera positioning, I just did a simple algorithm that tried to imitate how the camera works in Ocarina of Time.
Shader effects: I used this tutorial, maybe it could help you too.
I hope this can get you started. As I said, this is more of an overview of the structure, but you need help for any of the subjects presented (or anything else really), just ask away and I’ll try to explain what I came up with.
Also, if anything I explain here feels wrong to you, feel free to point it out. I am by no means an expert, so a new perspective on things can only help me out too.
40 notes
·
View notes
Text
Fachspezialist Dialogmanagement 50%
http://dlvr.it/QnMNZS
0 notes
Text
Friendly Aliens
So while working on the Enemy AI I began to work on friendly aliens the player would be able to talk to while they travel through the world, here’s a snippet of the code.
public class AlienRoaming : Interactibles { #region Fields private new Transform transform = null; // objects transform public Animator m_Animator = null; // animator private NavMeshAgent m_nav_mesh_agent = null; // nav mesh private Vector3 m_desired_position = Vector3.zero; // cache the next position public string[] m_dialog_keys = null; // keys to find the dialog on the file private Dialog m_dialog = null; // cache the dialog to display public Transform m_Location_object = null; // alien origin [SerializeField] private float m_min_movement, m_max_movement = 0f; // max and min movement [SerializeField] [Range(0, 50)] private float m_max_distance_from_object = 5f; // distance from the origin private float m_seconds_to_wait = 3f; // seconds that the alien will wait before moving again private int m_times_patroled = 0; // how many times from point to point private float m_timer_to_wait = 0f; // timer to count the seconds private float m_distance = 0f; // distance from the origin public IKControl m_IK_control = null; // ik control to animate the alien to look at the player #endregion #region Monobehaviour Functions void OnEnable() { // pause and resume funcs GameManager.OnPause += OnPause; GameManager.OnResume += OnResume; } void OnDisable() { // pause and resume funcs GameManager.OnPause -= OnPause; GameManager.OnResume -= OnResume; } void Awake() { // get references transform = GetComponent(); m_nav_mesh_agent = GetComponent(); } public void Start() { // setup the animator m_Animator.SetBool("Is_Attacking", false); m_Animator.SetBool("Is_Moving", true); // setting start position transform.position = m_Location_object.position; getNewDestination(); // grab all the dialogues by the keys List s = new List(); foreach ( string k in m_dialog_keys ) { s.Add ( DialogueHandler.GetDialogueByID ( k ) ); } // then generate the dialog with the speech m_dialog = new Dialog ( s.ToArray(), FontToDialog.FORREST ); } public void Update() { // check if the game is not running if(GameManager._Instance.MGameState != GameState.RUNNING) { return; } // calculate distance from the origin m_distance = Vector3.Distance ( m_Location_object.position, transform.position ); if (m_distance > m_max_distance_from_object) m_nav_mesh_agent.destination = m_Location_object.position; if (m_nav_mesh_agent.remainingDistance <= m_nav_mesh_agent.stoppingDistance && !m_nav_mesh_agent.pathPending) { if (m_times_patroled >= 3) { m_Animator.SetBool("Is_Moving", false); m_timer_to_wait += Time.deltaTime; if (m_timer_to_wait >= m_seconds_to_wait) { m_timer_to_wait = 0f; m_times_patroled = 0; } return; } else { m_Animator.SetBool("Is_Moving", true); } m_times_patroled++; getNewDestination(); } } void OnTriggerEnter(Collider other) { if (other.gameObject == GameObject.FindGameObjectWithTag("Player")) { m_nav_mesh_agent.Stop(); m_Animator.SetBool("Is_Moving", false); // Debug.Log("Player Is Within Range"); // found the player, look at the player // ik stuff if (m_IK_control != null) { if ( m_IK_control.lookObj == null ) m_IK_control.AssignLookObj ( other.transform ); m_IK_control.ikActive = true; } } } void OnTriggerExit(Collider other) { if (other.gameObject == GameObject.FindGameObjectWithTag("Player")) { m_nav_mesh_agent.Resume(); m_Animator.SetBool("Is_Moving", true); //Debug.Log("Player Has Left Range"); // found the player, look at the player // ik stuff if (m_IK_control != null) { m_IK_control.ikActive = false; } } } #endregion #region Custom Functions public override InteractTypeReturn Interact(Transform player) { m_Animator.SetBool("Is_Moving", false); DialogManager._Instance.AddDialogToQueue(m_dialog); return InteractTypeReturn.TALKING; } //Generate New Position Randomly void getNewDestination() { m_nav_mesh_agent.destination = (transform.position + new Vector3(Random.Range(m_min_movement, m_max_movement), 0, Random.Range(m_min_movement, m_max_movement))); ; } //Assign New Position void getNewDestination(Vector3 pos) { setNewDestination(pos); } //Sets The Next Destination void setNewDestination(Vector3 pos) { m_desired_position = pos; } //Sets The Next Nav Mesh Destination void setNavMeshDestination() { m_nav_mesh_agent.destination = m_desired_position; } void OnDrawGizmos() { // draw the location object so it can be seen from the scene view if ( m_Location_object == null ) return; Gizmos.color = Color.red; Gizmos.DrawWireSphere ( m_Location_object.position, 1 ); } #endregion #region Pause and Resume Callbacks void OnPause() { // pause alien m_Animator.SetBool("Is_Moving", false); m_nav_mesh_agent.Stop(); } void OnResume() { // resume alien m_nav_mesh_agent.Resume(); if (m_nav_mesh_agent.remainingDistance > m_nav_mesh_agent.stoppingDistance) m_Animator.SetBool("Is_Moving", true); } #endregion }
0 notes
Note
Very cool stuff with the dialogue system, with each holding a reference to the next DialogBlock. Your explanation of the structure really helped me out, I have always tried to use an array of strings which ends up being a dead-end ... so if I understand correctly, the DialogBlocks are all attached to the NPC and just reference each other by being on the same GameObject?
That’s about right. To be more precise, they don’t reference each other because they are on the same GameObject, they reference each other because of the values contained in their asset file. Remember that ScriptableObjects are asset files, just like AudioClips or Textures. Usually, the inspector would show the import settings for assets, but in the case of ScriptableObjects, they show the public variables of the class you created. Therefore, setting the references between the DialogBlocks is just like tweaking import settings: you do it once and the asset is set for the whole project.
A little trick for simplicity is to define the class like this:
This will create a menu item to easily create the asset:
With all the DialogBlocks linked between each other correctly, all you ever need is one Dialog set to a NPC (remember that Dialog is the class I use to reference a first DialogBlock of a conversation) and the DialogManager will handle looking into the DialogBlock asset data to find what the next one will be.
18 notes
·
View notes
Text
Questing!
So after having a conversation with the team we decided to tackle the Questing system that the player would follow in order to progress through the game.
Me and Vitor sat down together to code it so we could both learn how the system would work, here’s the code reference.
[System.Serializable] public class QuestDatabase { #region Fields public Quest m_current_quest = null; // current narrative quest that the player is doing public Quest m_next_quest = null; // next quest on the queue for the player to do public List<Quest> m_quest_list = new List<Quest>(); // list of all quests for the player to do #endregion
#region Constructor public QuestDatabase() { m_quest_list = new List<Quest>(); m_quest_list = m_quest_list.OrderBy(o => o.m_Quest_id).ToList(); } #endregion
#region Custom Functions public void UpdateDatabase() { UpdateQuest(); TriggerQuest(); }
private void UpdateQuest () { if ( m_current_quest == null ) return;
switch ( m_current_quest.m_Quest_id ) { case ( int ) NarrativeQuests.TUTORIAL_FIND_SCANNER: // quest 00: find the scanner if ( Bag._Instance.m_scanner != null ) { // quest complete FinishedQuest(); } break; case ( int ) NarrativeQuests.TUTORIAL_FIX_SWITCHES: // quest 01: fix the switches CircuitBoardTutorial cb = m_current_quest.m_Game_Object.GetComponent<CircuitBoardTutorial>();
if (cb.MHasFinished && cb.MHasStarted) {
m_current_quest.m_Game_Object.SetActive(false); //Debug.Log(m_next_quest.m_Quest_id); FinishedQuest(); //Debug.Log(m_next_quest.m_Quest_id); } break; } }
private void TriggerQuest() { if (m_current_quest != null) return;
switch (m_next_quest.m_Quest_id) { case ( int ) NarrativeQuests.TUTORIAL_FIND_SCANNER: if ( Gameplay._Instance.CanPlayerControlCharacter() && Gameplay._Instance.CanPlayerControlCamera () && ( DialogManager._Instance.MHasDialogLeft == false ) ) { TriggeringNextQuest(); } break; case ( int ) NarrativeQuests.TUTORIAL_FIX_SWITCHES: if (Bag._Instance.m_scanner != null && (NotificationManager._Instance.MIsScreenPanelActive == false) && (!DialogManager._Instance.MHasDialogLeft) && (Gameplay._Instance.MHasUsedScanner == true)) { TriggeringNextQuest(); } break; } }
private void AssignQuest(Quest quest) { }
private void FinishedQuest () { // the current quest is now done m_current_quest.m_Is_quest_done = true; if (m_current_quest.m_Next_quest != null) { // if the quest is linked then we can load the next on the queue m_current_quest = m_current_quest.m_Next_quest; } else { // there is no current quest, player might have to trigger it m_current_quest = null;
// hide the quest panel on the UI QuestHandler._Instance.HideQuestPanel(); } }
private void TriggeringNextQuest() { // move on to the next quest m_current_quest = m_next_quest; Quest q = m_quest_list[m_current_quest.m_Quest_id + 1]; if (q != null) m_next_quest = q; else { // the player might have completed the game // check for that }
// update the UI QuestHandler._Instance.ShowQuestPanel(); QuestHandler._Instance.UpdateQuestPanelTitle(m_current_quest.m_Quest_name); }
public void SkipCurrentQuest() { // get the current quest and check if there is a quest assigned to it
if (m_current_quest == null) TriggeringNextQuest();
FinishedQuest(); } #endregion }
It didn’t take us long to code since we both did it together, and it works how we both wanted it to so that’s always a plus :D.
0 notes