How to use @Message sequence with script based conversation?

Announcements, support questions, and discussion for the Dialogue System.
IAF
Posts: 7
Joined: Wed Aug 17, 2022 8:47 pm

How to use @Message sequence with script based conversation?

Post by IAF »

Hello,

I'm trying to create VN dialogues with "Dialogue system for Unity", "Dialogue system Extras".

Although I create C# and json based dialogue system referenced topic "Executing from script"
https://www.pixelcrushers.com/phpbb/vie ... 83f3af455f
But, there are not working Message sequence system like as @Message.

Can you tell me what I'm missing?

-- below is main part of my c# and json script --

[My C# code]
// A is start class

Code: Select all

public class A
{
    private void Start(){
        GameManager.Instance.ScenarioList.Add("Prologue_Main_Start");
        PlayScenario().Forget();
    }
}
// preloaded Singleton

Code: Select all

public class GameManager : SingletonMonoBehaviour<GameManager>
{

    // ----- Variables for scenario -----
    // Increment when start conversation
    private int conversationID = 0;

    // Read file name List
    public List<string> ScenarioList = new List<string>();

    // Now loading file name
    public string nowLoadingScenario;

    // if during conversation, true, if not, false
    public bool scenarioProgressing;

    // -----  -----
    public ResourceStore globalResource = new ResourceStore();
    public ResourceStore sceneResource = new ResourceStore();


    void Start()
    {
        DontDestroyOnLoad(this);
    }

    public void SetScenarioLabel(string addressableLabel)
    {
        nowLoadingScenario = addressableLabel;
    }

    public async UniTask StartConversation()
    {
        conversationID++;

        // Flag set
        GameManager.Instance.scenarioProgressing = true;

        // Create database:
        var database = ScriptableObject.CreateInstance<DialogueDatabase>();

        // Create a template, which provides helper methods for creating database content:
        var template = Template.FromDefault();

        // Our actors defined in dialogue database:
        int aldoID = 1;
        int richardID = 2;
        int lucyID = 3;

        // Create a conversation: [ID 0=START]
        var conversationTitle = nowLoadingScenario;
        var conversation = template.CreateConversation(conversationID, conversationTitle);
        conversation.ActorID = aldoID;
        conversation.ConversantID = richardID;
        database.conversations.Add(conversation);

        // START node: (Every conversation starts with a START node with ID 0)
        int entryID = 0;
        var node = template.CreateDialogueEntry(entryID++, conversation.id, "START");
        node.Sequence = "None()"; // START node usually shouldn't play a sequence.
        conversation.dialogueEntries.Add(node);

        // Node 1: Only execute Continue(). if without this, then fault occured.
        var previousNode = node;
        node = template.CreateDialogueEntry(entryID++, conversation.id, string.Empty);
        node.Sequence = "Continue();";
        conversation.dialogueEntries.Add(node);
        // Link from START:
        var link = new Link(conversation.id, previousNode.id, conversation.id, node.id);
        previousNode.outgoingLinks.Add(link);


        // Add it to the runtime environment and play it:
        DialogueManager.AddDatabase(database);
        DialogueManager.StartConversation(conversationTitle);

        var handle = Addressables.LoadAssetAsync<TextAsset>(nowLoadingScenario);
        var jsonStr = handle.WaitForCompletion().ToString();

        var jsons = JsonConvert.DeserializeObject<Dictionary<string, object>[]>(jsonStr);
        Addressables.Release(handle);

        foreach (var x in jsons)
        {
            previousNode = node;
            node = template.CreateDialogueEntry(entryID++, conversation.id, string.Empty);
            node.ActorID = lucyID; // Lucy speaks this line.
            node.ConversantID = richardID;

            int Done = 0;
            string nowSequence = "";

            foreach (var y in x)
            {
                string nextSeq = "";
                switch (y.Key)
                {
                    case "T":
                        node.DialogueText = y.Value.ToString();
                        break;
                    case "A" or "A_WS":
                        nextSeq = "AddScenario("
                            + nowLoadingScenario + ","
                            + y.Value.ToString() + ")";
                        break;
                    case "E" or "E_WR":
                        nextSeq = "EndScenario()";
                        break;
                }
                if(y.Key.Contains("WR"))
                {
                    AddSequence(ref nowSequence, nextSeq, Done);
                }else if (y.Key.Contains("WS"))
                {
                    AddSequence(ref nowSequence, nextSeq,null, ++Done);
                }
                else if(!string.IsNullOrEmpty(nextSeq))
                {
                    AddSequence(ref nowSequence, nextSeq);
                }
            }
            if (!string.IsNullOrEmpty(nowSequence) && nowSequence != "")
            {
                node.Sequence = nowSequence;
                Debug.Log($"node.Sequence:{node.Sequence}");
            }

            conversation.dialogueEntries.Add(node);
            // Link from previous node:
            link = new Link(conversation.id, previousNode.id, conversation.id, node.id);
            previousNode.outgoingLinks.Add(link);
        }

        while (GameManager.Instance.scenarioProgressing)
        {
            await UniTask.DelayFrame(1);
        }
        DialogueManager.StopConversation();

    }

    private void AddSequence(ref string nowSeq, string add,
        int? withReceive = null,int? withSend=null )
    {
        string newSequence = "";
        if (string.IsNullOrEmpty(nowSeq))
        {
            newSequence = add;
        }
        else
        {
            newSequence = nowSeq + add;
        }

        if (withReceive != null)
        {
            newSequence += "@" +  $"Message(Done{(int)withReceive})";
        }
        if (withSend != null)
        {
            newSequence += $"->Message(Done{(int)withSend})";
        }
        
        newSequence += ";";
        nowSeq = newSequence;
    }
}


[My Json (Prologue_Main_Start.json)]
The subject is load next json(Start01.json) and continue to next conversation.

case 1: completable

Code: Select all

[
  {
    "T": "Text",
    "A": "Start01"
  },
  {
    "E": ""
  }
]
Generated Sequence:
AddScenario(Prologue_Main_Start,Start01);
EndScenario();
This is possible, but there are restrictions like can not delay ... :oops:

case 2: not completable

Code: Select all

[
  {
    "T": "Text",
    "A_WS": "Start01",
	"E_WR":""
  }
]
Generated Sequence:
AddScenario(Prologue_Main_Start,Start01)->Message(Done1);EndScenario()@Message(Done1);
"@Message" can not receive ... :oops:

Best Regards.
IAF
IAF
Posts: 7
Joined: Wed Aug 17, 2022 8:47 pm

Re: How to use @Message sequence with script based conversation?

Post by IAF »

I forgot to attach two custom sequence class, put it.

[Custom Sequence AddScenario]

Code: Select all

namespace PixelCrushers.DialogueSystem.VisualNovelFramework
{

    /// <summary>
    /// Add json file name to GameManager.Instance.ScenarioList
    /// </summary>
    /// 0 : now loading file name string(Addressable)
    /// 1 : Jump file name string(Addressable)
    public class SequencerCommandAddScenario : SequencerCommand
    {
        private void Awake()
        {
            string nextLabel = "";
            // Make address optional only for the same hierarchy
            if (!GetParameter(1).ToString().Contains("_"))
            {
                var strs = GetParameter(0).Split("_");
                nextLabel += strs[0] + "_" + strs[1] + "_";
            }
            nextLabel += GetParameter(1).ToString();
            GameManager.Instance.ScenarioList.Add(nextLabel);
        }
    }
}
[Custom Sequence EndScenario]

Code: Select all

namespace PixelCrushers.DialogueSystem.VisualNovelFramework
{
    /// <summary>
    /// Manages Flag of conversation progressing.
    /// </summary>
    public class SequencerCommandEndScenario : SequencerCommand
    {
        private void Awake()
        {
            GameManager.Instance.scenarioProgressing = false;
         }
    }
}
Best regards.
User avatar
Tony Li
Posts: 22145
Joined: Thu Jul 18, 2013 1:27 pm

Re: How to use @Message sequence with script based conversation?

Post by Tony Li »

Hi,

SequencerCommandAddScenario() runs immediately in Awake, so instead of this:

Code: Select all

AddScenario(Prologue_Main_Start,Start01)->Message(Done1);EndScenario()@Message(Done1);
you can do this, which has the same effect:

Code: Select all

AddScenario(Prologue_Main_Start,Start01);
EndScenario()@Message(Done1)
It may help to temporarily set the Dialogue Manager GameObject's Other Settings > Debug Level to Info. This will log Dialogue System activity. This may help you determine what's going on under the hood.
IAF
Posts: 7
Joined: Wed Aug 17, 2022 8:47 pm

Re: How to use @Message sequence with script based conversation?

Post by IAF »

Thanks reply !

I tryed to change my settings:
> It may help to temporarily set the Dialogue Manager GameObject's Other Settings > Debug Level to Info. This will log Dialogue System activity. This may help you determine what's going on under the hood.

Apparently, sequencer plays generated sequence,
but, waiting is not work yet.
debugmessages.png
debugmessages.png (124.89 KiB) Viewed 550 times
Debugger shows continuously(not wait 10 sec), :
Dialogue System: Sequencer.Play( AddScenario(Prologue_Main_Start, Start01)->Message(Done1) )
Dialogue System: Sequencer.Play( Delay(10.0)@Message(Done1)->Message(Done2) )
Dialogue System: Sequencer.Play( EndScenario()@Message(Done2) )

What's I'm missing ... ?

Change my script below ( For ease of identification, add Delay(10.0) sequence)

[C#code]

Code: Select all

                switch (y.Key)
                {
                    case "T":
                        node.DialogueText = y.Value.ToString();
                        break;
                    case "A" or "A_WS":
                        nextSeq = "AddScenario("
                            + nowLoadingScenario + ","
                            + y.Value.ToString() + ")";
                        break;
                    case "E" or "E_WR":
                        nextSeq = "EndScenario()";
                        break;
                    case "D" or "D_WRWS":
                        nextSeq = "Delay(10.0)";
                            break;
                    case "N" or "N_WR":
                        nextSeq = "Continue()";
                            break;
                }
                if (y.Key.Contains("WRWS"))
                {
                    AddSequence(ref nowSequence, nextSeq, Done, ++Done);
                }
                else if(y.Key.Contains("WR"))
                {
                    AddSequence(ref nowSequence, nextSeq, Done);
                }else if (y.Key.Contains("WS"))
                {
                    AddSequence(ref nowSequence, nextSeq,null, ++Done);
                }
                else if(!string.IsNullOrEmpty(nextSeq))
                {
                    AddSequence(ref nowSequence, nextSeq);
                }
[myJson]

Code: Select all

[
  {
    "T": "Text",
    "A_WS": "Start01",
    "D_WRWS":"",
    "E_WR":""
  }
]
Thanks reading.
User avatar
Tony Li
Posts: 22145
Joined: Thu Jul 18, 2013 1:27 pm

Re: How to use @Message sequence with script based conversation?

Post by Tony Li »

Hi,

Each sequencer command will appear twice in the Console window.

The first time, it will report when it enters the sequencer's queue. Example:

Code: Select all

Dialogue System: Sequencer.Play( Delay(10.0)@Message(Done1)->Message(Done2) )
When it exits the queue, it will report in this format:

Code: Select all

Dialogue System: Sequencer: Delay(10)
Do you see the second report for the commands?


Here is another example. This example is not from your code above. But I think it will help to clarify how the sequencer works. Let's say your Sequence is: Audio(hello)@2

The first time, it will report when it enters the sequencer's queue. For example, something like:

Code: Select all

[16:37:51] Dialogue System: Sequencer.Play( Audio(hello)@2 )
When it exits the queue and starts to play, it will report in this format:

Code: Select all

[16:37:53] Dialogue System: Sequencer: Audio(hello(AudioClip))
IAF
Posts: 7
Joined: Wed Aug 17, 2022 8:47 pm

Re: How to use @Message sequence with script based conversation?

Post by IAF »

Hi, tony.

At the previous my post, debug messages only show:
Dialogue System: Sequencer.Play( ... )
But never show :
Dialogue System: Sequencer: ( ... ))

Above attach file show lower end of debug messages, not continue.

Then, tryed your suggestion :
> Here is another example. This example is not from your code above. But I think it will help to clarify how the sequencer works. Let's say your Sequence is: Audio(hello)@2

result:
debugMessage2(Audio(hello)).png
debugMessage2(Audio(hello)).png (92.27 KiB) Viewed 539 times
In debug window,
shows "Dialogue System: Sequencer.Play( Audio(hello)@2 )"
-- wait 2sec --
shows "Dialogue System: Sequencer: Audio(hello, )"

wait 2seconds! Propely!


I tried other pattern 1:
Sequence sets "Audio(hello)@10->Message(Done1);Audio(hell)@Message(Done1)->Message(Done2);"

In debug window,
shows "Dialogue System: Sequencer.Play( Audio(hello)->Message(Done1) )"
and "Dialogue System: Sequencer.Play( Audio(hell)@Message(Done1)->Message(Done2) )"
-- wait 10 sec --
shows "Dialogue System: Sequencer: Audio(hello, )"
and "Dialogue System: Sequencer: Audio(hell, )"

worked correctlly!


I tried other pattern 2:
Sequence sets ""Audio(hello)->Message(Done1);Audio(hell)@Message(Done1)->Message(Done2);"
(delete @10)

In debug window,
shows "Dialogue System: Sequencer.Play( Audio(hello)->Message(Done1) )"
and "Dialogue System: Sequencer: Audio(hello, )"
and "Dialogue System: Sequencer.Play( Audio(hell)@Message(Done1)->Message(Done2) )"
and "Dialogue System: Sequencer: Audio(hell, )"
this works sequentially too!

I tried posted like a sequence pattern again:
Sequence sets "AddScenario(Prologue_Main_Start,Start01)->Message(Done1);Delay(10.0)@Message(Done1)->Message(Done2);EndScenario()@Message(Done2);"
it shows "Dialogue System: Sequencer.Play( AddScenario(Prologue_Main_Start, Start01)->Message(Done1) )"
and "Dialogue System: Sequencer.Play( Delay(10.0)@Message(Done1)->Message(Done2) )"
and "Dialogue System: Sequencer.Play( EndScenario()@Message(Done2) )"

But never shows Sequencer ( ... ) ... :oops:
User avatar
Tony Li
Posts: 22145
Joined: Thu Jul 18, 2013 1:27 pm

Re: How to use @Message sequence with script based conversation?

Post by Tony Li »

Are you pausing the Dialogue System?

Can you email a reproduction project to tony (at) pixelcrushers.com? If so, please include step-by-step instructions that explain how to reproduce the issue.
IAF
Posts: 7
Joined: Wed Aug 17, 2022 8:47 pm

Re: How to use @Message sequence with script based conversation?

Post by IAF »

I tried additional patterns,

At the result,
I think it is custom sequencer issue.

[case 1 use embedded sequencer only]
Edited my code :

Code: Select all

            foreach (var y in x)
            {
                string nextSeq = "";
                switch (y.Key)
                {
                    case "T":
                        node.DialogueText = y.Value.ToString();
                        break;
                    //case "A" or "A_WS":
                    //    nextSeq = "AddScenario("
                    //        + nowLoadingScenario + ","
                    //        + y.Value.ToString() + ")";
                    //    break;
                    case "A" or "A_WS":
                        nextSeq = "Audio(A)";
                        break;
                    //case "E" or "E_WR":
                    //    nextSeq = "EndScenario()";
                    //    break;
                    case "E" or "E_WR":
                        nextSeq = "Audio(E)";
                        break;
                    case "D" or "D_WRWS":
                        nextSeq = "Delay(10.0)";
                        break;
                }
Generated Sequence:
Audio(A)->Message(Done1);Delay(10.0)@Message(Done1)->Message(Done2);Audio(E)@Message(Done2);

Result is:
debugMessage4_script-based(Audio(h)).png
debugMessage4_script-based(Audio(h)).png (90.65 KiB) Viewed 534 times

It works properly !


[case 2:use custom sequencer( its content sets to blank )]

Code: Select all

                    case "T":
                        node.DialogueText = y.Value.ToString();
                        break;
                    case "A" or "A_WS":
                        nextSeq = "AddScenario("
                            + nowLoadingScenario + ","
                            + y.Value.ToString() + ")";
                        break;
                    //case "A" or "A_WS":
                    //    nextSeq = "Audio(A)";
                    //    break;
                    case "E" or "E_WR":
                        nextSeq = "EndScenario()";
                        break;
                    //case "E" or "E_WR":
                    //    nextSeq = "Audio(E)";
                    //    break;
                    case "D" or "D_WRWS":
                        nextSeq = "Delay(10.0)";
                        break;

Code: Select all

    public class SequencerCommandEndScenario : SequencerCommand
    {
        private void Awake()
        {
            //GameManager.Instance.scenarioProgressing = false;
        }
    }

Code: Select all

    public class SequencerCommandAddScenario : SequencerCommand
    {
        private void Awake()
        {
            //string nextLabel = "";
            //if (!GetParameter(1).ToString().Contains("_"))
            //{
            //    var strs = GetParameter(0).Split("_");
            //    nextLabel += strs[0] + "_" + strs[1] + "_";
            //}
            //nextLabel += GetParameter(1).ToString();
            //GameManager.Instance.ScenarioList.Add(nextLabel);

        }
    }
Result is:
debugMessage5_script-based(custom).png
debugMessage5_script-based(custom).png (93.76 KiB) Viewed 534 times

It not work properly...
Regards.
IAF
Posts: 7
Joined: Wed Aug 17, 2022 8:47 pm

Re: How to use @Message sequence with script based conversation?

Post by IAF »

Hi , tony.

> Are you pausing the Dialogue System?
During conversation, I Dont.
But ... It is possible that it has stopped without knowing

I sent download link by a e-mail.
Please check later.

Best Regards.
User avatar
Tony Li
Posts: 22145
Joined: Thu Jul 18, 2013 1:27 pm

Re: How to use @Message sequence with script based conversation?

Post by Tony Li »

Hi,

Thank you for sending the reproduction project.

All sequencer commands must call Stop(). Otherwise the command will not finish, and it will not send the ->Message(message).

Add Stop() to your custom sequencer commands.
Post Reply