Page 1 of 2

creating a back button (need guidance)

Posted: Wed Jan 20, 2016 4:17 pm
by Roncin
// Question
My playtester wanted to go backwards through the dialog choices. Any thoughts on how to implement it?


I can create an array of Conversation ID's and node id's and jump to a specific node, but I think it's going to cause a lot of problems.

// Possible Solution Ideas -----------------------------------------------------------------------------------------
Methods I think are relevant:

var dialogueEntry = DialogueManager.CurrentConversationState.subtitle.dialogueEntry;
int conversationID = dialogueEntry.conversationID; //<-- This is the conversation ID.
int dialogueEntryID = dialogueEntry.id; //<-- This is the dialogue entry ID.

DialogueManager.StartConversation("Title", actor, conversant, dialogueEntryID);

Thanks!

// Relevant post-----------------------------------------------------------------------------------------------------


This question by EWalk13 is similar to mine.
Getting Node ID through code
Postby EWalk13 » Fri Oct 30, 2015 3:50 pm
--------------------------
Hi,
EWalk13 wrote:
Hi I was wondering how you get a conversation nodes id through C# code. I need to be able to access these to work in my audio.

Every conversation has a conversation ID, and every dialogue entry in the conversation has a dialogue entry ID. If you set the Dialogue Manager's Debug Level to Info, you'll see lines like this:

CODE: SELECT ALL
Dialogue System: Add Link (Player): ID=12:34 'Hello, world.'

The conversation ID is 12, and the dialogue entry ID is 34.

You can get to any dialogue entry through DialogueManager.MasterDatabase. But I'm guessing you want the current line's IDs. The current dialogue entry is DialogueManager.CurrentConversationState.subtitle.dialogueEntry. So use this:

CODE: SELECT ALL
var dialogueEntry = DialogueManager.CurrentConversationState.subtitle.dialogueEntry;
int conversationID = dialogueEntry.conversationID; //<-- This is the conversation ID.
int dialogueEntryID = dialogueEntry.id; //<-- This is the dialogue entry ID.

If you're making a subclass of your dialogue UI class, such as UnityUIDialogueUI, the ShowSubtitle() method also gives you a reference to the subtitle.

You might also find entrytags helpful for your audio. In code, you can use DialogueManager.MasterDatabase.GetEntrytag().


EWalk13 wrote:
Also, is there a way to enter a conversation and jump right to a specific node and skip the ones above it?

Pass the dialogue entry ID to DialogueManager.StartConversation():

CODE: SELECT ALL
DialogueManager.StartConversation("Title", actor, conversant, dialogueEntryID);

Personally, I don't like working with IDs directly. They're fine, they're stable and reliable, but I feel like the logic is more transparent if I put it inside the dialogue tree's Conditions and Script fields instead. For example, in the Feature Demo, Private Hart's conversation jumps to different sections of the dialogue tree by using Conditions on all the dialogue entries that link from the START entry.

Re: creating a back button (need guidance)

Posted: Thu Jan 21, 2016 10:37 am
by irve
I'd worry more about variables being set/unset than the specifics of jumping. Should you save the Lua variable set on every node?

My current implementation (we have something re-do-ish on some specific nodes) uses the IDs to go back and it seems to be a solid solution.

Re: creating a back button (need guidance)

Posted: Thu Jan 21, 2016 11:55 am
by Tony Li
Andrew - Does your playtester maybe just want a conversation log? Or actually rewind with the ability to click down a different path?

A conversation log is fairly easy. There's an example on the Extras page.

If they want to rewind time, irve has an excellent point about variable values -- and any other game state changes for that matter, such as dialogue entry sequences that activate or deactivate GameObjects. Also, since the Dialogue System evaluates one level ahead to know how to handle continue buttons, you may need to backtrack twice sometimes.

If you're not concerned about that, though, you could write a small script that maintains a stack of conversation and entry IDs. In an OnConversationLine method, push the current one onto the stack. If the player clicks the Back button, pop the top one off the stack and jump to the next entry on the stack:

Code: Select all

using UnityEngine;
using System.Collections.Generic;

namespace PixelCrushers.DialogueSystem
{

    /// <summary>
    /// This script adds the ability to backtrack conversations. To backtrack, call Backtrack(true).
    /// The bool parameter specifies whether to backtrack to an NPC line, which is what you usually
    /// want to do; otherwise you'll keep backtracking to the same response menu instead of going
    /// back to a previous NPC line.
    /// </summary>
    public class Backtracker : MonoBehaviour
    {

        public bool debug;

        public struct IDRecord
        {
            public int conversationID;
            public int entryID;
            public bool isNPC;

            public IDRecord(int conversationID, int entryID, bool isNPC)
            {
                this.conversationID = conversationID;
                this.entryID = entryID;
                this.isNPC = isNPC;
            }
        }

        private Stack<IDRecord> stack = new Stack<IDRecord>();
        private Transform conversationActor;
        private Transform conversationConversant;
        private bool isBacktracking = false;

        public void OnConversationStart(Transform actor)
        {
            if (!isBacktracking)
            {
                // If we're really starting a new conversation (not just backtracking), initialize:
                stack.Clear();
                conversationActor = DialogueManager.CurrentActor;
                conversationConversant = DialogueManager.CurrentConversant;
                if (debug) Debug.Log("Backtracker: Starting a new conversation. Clearing stack.");
            }
        }

        public void OnConversationLine(Subtitle subtitle)
        {
            // Record the current entry:
            var isNPC = subtitle.speakerInfo.characterType == CharacterType.NPC;
            stack.Push(new IDRecord(subtitle.dialogueEntry.conversationID, subtitle.dialogueEntry.id, isNPC));
            if (debug) Debug.Log("Backtracker: Recording dialogue entry " + subtitle.dialogueEntry.conversationID + ":" + subtitle.dialogueEntry.id + " on stack: '" + subtitle.formattedText.text + "' (" + subtitle.speakerInfo.characterType + ").");
        }

        // Call this method to go back:
        public void Backtrack(bool toPreviousNPCLine)
        {
            if (stack.Count < 2) return;
            stack.Pop(); // Pop current entry.
            var destination = stack.Pop(); // Pop previous entry.
            if (toPreviousNPCLine)
            {
                while (!destination.isNPC && stack.Count > 0)
                {
                    destination = stack.Pop(); // Keep popping until we get an NPC line.
                }
                if (!destination.isNPC) return;
            }
            var title = DialogueManager.MasterDatabase.GetConversation(destination.conversationID).Title;
            DialogueManager.StopConversation();
            isBacktracking = true;
            if (debug) Debug.Log("Backtracker: Backtracking to '" + title + "' entry " + destination.entryID);
            DialogueManager.StartConversation(title, conversationActor, conversationConversant, destination.entryID);
            isBacktracking = false;
        }
    }
} 
EDIT: The original version had a couple issues. The version above works and is included in the example package below.

Re: creating a back button (need guidance)

Posted: Thu Jan 21, 2016 10:38 pm
by Roncin
@irve
Thanks irve, you have confirmed my suspicion that undoing the variables changes is going to be the challenge. Thanks!

@Toni
My playtester felt they didn't know the ramifications of a choice before they took it and wanted to go back and choose the other option. So yes, actually rewind.
Thanks for the tips and code! I learned a lot. I used push and pop in my assembly days, but didn't know they existed in c#.
Given I have a small number of variables I would need to undo, this is a good base. I should be able to work with the code you supplied.

Questions:
In lines 33 34 it looks like you doing 2 pops in a row? Is that because the Dialogue System is evaluating on level ahead?

In line 38-41 these look like conditional outcomes but I don't see the test condition?

Thanks Tony, much obliged.

AR

Re: creating a back button (need guidance)

Posted: Thu Jan 21, 2016 10:54 pm
by Tony Li
Roncin wrote:In lines 33 34 it looks like you doing 2 pops in a row? Is that because the Dialogue System is evaluating on level ahead?
The first pop grabs the current dialogue entry. The second pop grabs the previous dialogue entry, which is the one we want to backtrack to.
Roncin wrote:In line 38-41 these look like conditional outcomes but I don't see the test condition?
The script has an OnConversationStart method that clears the entire stack in preparation for a new conversation.

However, we backtrack by stopping the conversation and then restarting it at a previous dialogue entry. This causes the OnConversationStart method to be called. In this case, we don't want to clear the entire stack, because we'd lose all history after the first rewind.

To get around this, OnConversationStart checks the Boolean variable "isBacktracking". If this variable is true, it doesn't clear the stack because we're not actually starting a new conversation; we're just backtracking the current conversation.

Re: creating a back button (need guidance)

Posted: Wed Jan 27, 2016 9:19 pm
by Tony Li
I updated the code above. Here's a working example: BacktrackingExample_2016-01-27.unitypackage. It's also on the Dialogue System Extras page.

Re: creating a back button (need guidance)

Posted: Thu Feb 07, 2019 6:51 am
by DragoonHP
Hey Tony

So the example doesn't work in the latest version and it fails with. The example starts but pressing spacebar doesn't work.

Code: Select all

NullReferenceException: Object reference not set to an instance of an object
PixelCrushers.DialogueSystem.Demo.SimpleController.Update () (at Assets/Plugins/Pixel Crushers/Dialogue System/Scripts/Demo Scripts/SimpleController.cs:118)
And when I tried to put it in another project, it doesn't work. OnConversationStart and OnConversationLine triggers don't work. This is how I have set it up
Image

Also, is there a way to implement a true back button where I can rewind time?

Re: creating a back button (need guidance)

Posted: Thu Feb 07, 2019 8:51 am
by Tony Li
Hi,

The Backtracking example was created in version 1.x. You'd need to run it through the 1.x to 2.x updater (Tools > Pixel Crushers > Dialogue System > Tools > Run 1x To 2x Updater). I just updated the example to 2.x. You can download the updated example on the Extras page. It's not the greatest example because the conversation only has two nodes (not much to backtrack), but the important part -- the backtracking script -- works for any conversation length.

Re: creating a back button (need guidance)

Posted: Thu Feb 07, 2019 9:46 am
by DragoonHP
Thanks. With the update, the example works together.

But when I place it in the VN example (from the extras page), it doesn't work because the OnConversationStart and OnConversationLine never start. Am I doing something wrong? Is there any other setting I need to tweak

Also, the back button completely skips the player line, even when "Always Force Response" is unchecked

Re: creating a back button (need guidance)

Posted: Thu Feb 07, 2019 10:17 am
by Tony Li
DragoonHP wrote: Thu Feb 07, 2019 9:46 amBut when I place it in the VN example (from the extras page), it doesn't work because the OnConversationStart and OnConversationLine never start. Am I doing something wrong? Is there any other setting I need to tweak
Did you put it on the Dialogue Manager? It's guaranteed to receive OnConversationStart and OnConversationLine.
DragoonHP wrote: Thu Feb 07, 2019 9:46 amAlso, the back button completely skips the player line, even when "Always Force Response" is unchecked
The Backtrack method (which is called by the Backtrack button in the example) has a checkbox. Untick it to allow backtracking to the player lines, too.