Page 1 of 1

Update database at runtime

Posted: Thu Jul 18, 2019 2:40 pm
by onpocket
Hello Toni,

I've been trying to use the Dialogue System to implement all the dialogue component of a Teleport system. Meaning that the conversations where the player chooses its destination are to be handled by the Dialogue System in cooperation with my custom scripts.

Right now i am trying to implement the system in the following fashion: i have two scripts attached to a gameobject called "Portal", one of them (TeleportComponent.cs) holds the following information (right now simple stuff, just to give me an idea of what sort of variables i will need to hold): ID (int) and all the possible destinations of that specific portal (List<string>); the other (DialogueSystemTeleportController.cs) is the one responsable of generating (Awake method) and adding a conversation holding all the possible destinations from which the player can choose from through the typical Dialogue System UI.

The code of the "DialogueSystemTeleportController.cs" (i have tried to adapt it from https://www.pixelcrushers.com/dialogue_ ... baseInCode ***):

Code: Select all

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

public class DialogueSystemTeleportController : MonoBehaviour
{
    public DialogueDatabase database;
    public TeleportComponent teleporter;

    string conversationTitle;
    Conversation conversation;
    List<string> destinations;


    void Awake()
    {
        Debug.Log(this.name + ": Awake");
        destinations = teleporter.destinationNames;
        var template = Template.FromDefault();

        // Create a conversation
        int conversationID = template.GetNextConversationID(database);
        conversationTitle = "Teleport list conversation (runtime generated) " + teleporter.ID;
        conversation = template.CreateConversation(conversationID, conversationTitle);
        conversation.ActorID = 1;
        conversation.ConversantID = 3;

        // Create conversation nodes
        var startNode = template.CreateDialogueEntry(0, conversation.id, "START");
        startNode.ActorID = 1;
        startNode.ConversantID = 3;
        startNode.Sequence = "None()"; // START node usually shouldn't play a sequence.
        conversation.dialogueEntries.Add(startNode);
        // Actual dialogue node ("Hello") with ID 1: (could have used template.GetNextDialogueEntryID)
        for (int i = 0; i < destinations.Count; i++)
        {
            var newNode = template.CreateDialogueEntry(i + 1, conversation.id, string.Empty);
            newNode.ActorID = 1;
            newNode.ConversantID = 3;
            newNode.DialogueText = destinations[i];
            conversation.dialogueEntries.Add(newNode);
            // Create the link from the START node to the newNode
            var link = new Link(conversation.id, startNode.id, conversation.id, newNode.id);
            startNode.outgoingLinks.Add(link);
        }

        // Add the conversation to the database
        //database.conversations.Add(conversation);
    }

    void OnEnable()
    {
        Debug.Log(this.name + ": OnEnable");
        database.conversations.Add(conversation);
        DialogueManager.AddDatabase(database);
    }

    void OnDisable()
    {
        Debug.Log(this.name + ": OnDisable");
        database.conversations.Remove(conversation);
        DialogueManager.RemoveDatabase(database);
    }

    IEnumerator OnConversationEnd(Transform actor)
    {
        yield return new WaitForSeconds(0.5f);  //makes sure the UI of the previous conversation has faded away!
        DialogueManager.StartConversation(conversationTitle);
    }
}

*** Important note: In contrast with the provided example on the referenced link which is creating an instance of a "DialogueDatabase" scriptable object and adding it to the "DialogueManager" later, my code is actually getting a previously made database and changing it at runtime, in this case generating and adding a conversation. This was made this way since i thought it would be useful to have a database solely dedicated for holding all my custom teleport system conversations, including a first step dialogue conversation, called "Teleport confirmation conversation" pictured in the following image:

Image
"Teleport confirmation conversation" tree.


This basic conversation holds two nodes "Teleport..." and "Cancel", which are pretty self explanatory. If the player decides to teleport and chooses the "Teleport..." node, then the following sequence is executed:

Code: Select all

{{default}}; SetEnabled(DialogueSystemTeleportController,true);
Else, if the player decides to cancel and chooses the "Cancel" node, the dialogue simply ends without anything special happening.

My problem is that the database does not seem to be updated during runtime, meaning that whenever the "OnConversationEnd" is called i get the following warning:

Code: Select all

Dialogue System: Conversation 'Teleport list conversation (runtime generated) 0' not found in database.
UnityEngine.Debug:LogWarning(Object)
PixelCrushers.DialogueSystem.ConversationModel:.ctor(DialogueDatabase, String, Transform, Transform, Boolean, IsDialogueEntryValidDelegate, Int32, Boolean, Boolean) (at Assets/PixelCrushers/Plugins/Pixel Crushers/Dialogue System/Scripts/MVC/Model/Logic/Model/ConversationModel.cs:138)
PixelCrushers.DialogueSystem.DialogueSystemController:StartConversation(String, Transform, Transform, Int32) (at Assets/PixelCrushers/Plugins/Pixel Crushers/Dialogue System/Scripts/Manager/DialogueSystemController.cs:692)
PixelCrushers.DialogueSystem.DialogueSystemController:StartConversation(String, Transform, Transform) (at Assets/PixelCrushers/Plugins/Pixel Crushers/Dialogue System/Scripts/Manager/DialogueSystemController.cs:743)
PixelCrushers.DialogueSystem.DialogueManager:StartConversation(String) (at Assets/PixelCrushers/Plugins/Pixel Crushers/Dialogue System/Scripts/Manager/DialogueManager.cs:466)
<OnConversationEnd>c__Iterator0:MoveNext() (at Assets/_Scripts/DialogueSystemTeleportController.cs:69)
UnityEngine.SetupCoroutine:InvokeMoveNext(IEnumerator, IntPtr)
and nothing happens. Although when i stop the simulation (with the "OnDisable" method commented out), my generated conversation is added to the referenced database.

To conclude this long post, here is my question:
  • Is there any way to update a previously created (OnEditor) database with runtime generated data (in my case, a conversation)? If so, is there any optimal way of doing such thing?

Re: Update database at runtime

Posted: Thu Jul 18, 2019 8:47 pm
by Tony Li
Hi,

Here is an overview of the steps:

1. Instantiate a copy of the dialogue database.
2. Add your conversation to the instantiated database.
3. Add the instantiated database to the Dialogue Manager.
4. Run the conversation.
5. Remove the instantiated database from the Dialogue Manager.

More info:
If your dialogue database exists as an asset in your project, then if you make changes to it in play mode in the Unity editor, those changes will be permanent, as you've seen. To make changes that go away when you exit play mode, work with an in-memory copy of the database created using Instantiate(). Add this line to the beginning of Awake():

Code: Select all

database = Instantiate(database);
and make sure to destroy it when you're done:

Code: Select all

void OnDestroy() { Destroy(database); }
The Dialogue System can only start conversations in databases that you have added to the Dialogue Manager's runtime master database. There are several ways to add a database, including: assign it to the Dialogue Manager's Initial Database field, assign it to an Extra Databases component, or call DialogueManager.AddDatabase() in code as you do in OnEnable().

However, all conversation IDs must be unique across all databases that are currently added to the Dialogue Manager's runtime master database. This line:

Code: Select all

int conversationID = template.GetNextConversationID(database);
will get an ID that is unique inside the database. Since the database doesn't have any conversations yet, template.GetNextConversationID will return 1. But ID 1 is almost certainly used in your main database, too, which causes a conflict. Instead, choose a value that's guaranteed to not be used in another database, such as 10000:

Code: Select all

int conversationID = 10000;
Also, make sure that you don't call DialogueManager.RemoveDatabase() until after the teleport conversation is done.

You can check if the database is loaded by using a Lua Console or the Dialogue Editor's Watches tab. For example, in the Lua Console enter:

Code: Select all

return Conversation
This will return a list of the loaded conversation IDs. Make sure 10000 (or whatever ID you use) is in there.

Re: Update database at runtime

Posted: Mon Jul 22, 2019 4:46 am
by onpocket
Thank you so much! That solved it :)

Re: Update database at runtime

Posted: Mon Jul 22, 2019 8:15 am
by Tony Li
Great! Glad to help.