Premise: I really like Quest Machine & Dialogue System but they do not bring me joy (yet).
Plea: Sorry I definitely do not want that to come across as an attack! What I would like is if you could please help me understand some of what I'm doing wrong and make me find joy in these systems again!
Basically this is born entirely out of my own frustration with trying to get these things to work the way I need them to. I find that for a couple plugins with so many resources it's still incredibly frustrating that I can't wrangle them without me crying myself to sleep. Ok melodrama out of the way, you obviously know the system inside & out (and are EXTREMELY helpful on the forums I should add) but for the life of me it just doesn't click.
I'm going to post a basic picture of our setup, along with some pain points, and if you can jump in and offer some wise words, or even a WTF are you doing, it would be very much appreciated.
(diagram attached)
Apologies if none of that makes sense, please do ask away about any specific point!
Pain points:
1. UI
I want a text message style format so the textline example is perfect. I also want I want specific dialogues to go to specific UI elements. I also want to have many conversations with the same actor and keep all specific actor conversations in that same place. I tried so many different ways to get conversations routing to different UI areas the only thing that ever yielded results was the combination of StartConversation with an actor transform that has an OverrideDialogueUI script on it. It's awful, I hate it, please laugh/cry/help.
I also had the worst time with StartConversation immediately putting text in those (textline) UI objects, as it's part of our UI to bring them up when you're ready to read them. If the elements were active at the time StartConversation occurs then it's all good the text goes in the right place, if not then it automatically creates a new UI object to dump the text in and that is not at all what I want. So my hideous workaround is to keep all the actor textline ui elements active but not on the screen, and then moving them into place when the player wants to read them. It's awful, I hate it, please... well you know.
2. Quest Event
Why do I track quests in our own tracker? Because I couldn't figure out how to stick a bunch of quest specific data into quests. I saw that there was something about getting it all in there via a json string, but ultimately I want to be able to use the editor to set a bunch of values, and if it's all there in a json string then it's not very designer friendly. I'm sure I'm completely off base here and I would love for it to be wholly encompassed within QM but actually I'm at peace with this bit, this is just the reasoning why I track my own quests.
What I hated was trying to read the status of a quest. There's a litany of different methods throughout the forums / documentation and the only one that ever worked for me was QuestLog.GetQuestStatus. I could just check this on a timer and be done with it, but I'd rather know specifically when the quest is updated. Again I struggled to find anything that I could hook into, so instead I've got an OnExecute event at a specific conversation node to tell my quest tracker "go and query QM for the new quest status". Ditto to it being awful.
3. Read Quest Data
I have no idea why I find it so difficult to read data out of QM.
It seems to me that there are so many avenues to getting quests up and running that it's so confusing as to what's going on.
Does it go in the quest journal, do I read data from the quest journal, is quest journal just a ui thing or is it necessary for the quest to be activated from the dialogue system, do I need a quest giver, can I just run this all via scripts instead, why do some methods seemingly update the quest scriptable object and some the runtime instance of the quest scriptable object. At this point I'm so overwhelmed with the terminology it's kinda lost all meaning and I'm struggling to even explain the setup to the team.
My gut feeling is that so much of it is focused on making simple to use UI components and simple flow to them for users to play with quickly that it somehow is not simple at all to get to the meat of it underneath.
Again QuestLog.GetQuestStatus is the only thing I could get working in the end, and even then it's just a status, so everything else is up to my tracker. In that dream world I'd love for QM to handle everything. Big I hate it ditto, sorry for the rant
Misc Things
- We're obviously using both Quest Machine AND Dialogue System, and to be completely honest I find that there's not enough specific information on how those two work together. Having "Quests" available in Dialogue System makes searching for the right answer even more frustrating as there's terminology overlap.
- Textline dialogue UI
- I do not want it to clear the conversation UI, I want to retain it on screen as an permanent log between the player and that actor.
- How do I get it to not erase what's already been output to the UI component?
- OR how do I retrieve the data in what's already been put there so I can rebuild it on demand?
- Once I've got all the dialogue flowing into UI object, how do I determine what conversation each bit of dialogue comes from? Ie I want to put a conversation spacer in the UI, think gmail email history flowing from top to bottom, or even just a simple line element separating the different conversations.
- All our quest machine dialogue comes from the dialogue system. How do I go about presenting the player with multiple choices for a reward (for example, I'm mostly just talking about defining buttons for custom actions etc) when it's got this bridged plugin setup? If it was just using QM then you'd do multiple reward buttons and that would go through to the quest journal, but when it's coming from DS I have no idea. Ugh I hope that makes sense!
- I fully realise what a sh*tstorm of a post this is, so if it's helpful I can totally strip out our QM & DM setup to a separate scene showing off all the nonsense. Just let me know.
- Finally, thank you! You're a star and I wish I knew your system as intimately as you do, then of course I wouldn't be writing this and would be working on world peace instead.
(I've probably some stuff... let's start with this )
Cheers,
Simon
Please Fix My Terrible Dialogue System & Quest Machine Combined Setup
Please Fix My Terrible Dialogue System & Quest Machine Combined Setup
- Attachments
-
- QMDS.png (269.85 KiB) Viewed 442 times
Re: Please Fix My Terrible Dialogue System & Quest Machine Combined Setup
Hi Simon,
Off the top, here are my high level recommendations:
You may find it simpler in the long run to write a custom implementation of IDialogueUI that does exactly what you want and no more.
The Textline project and SMS Dialogue UI record which subtitles have been shown for a conversation. When you resume the conversation, it repopulates the dialogue UI with those subtitles. If you need to stick with Textline/SMS Dialogue UI, it's possible to turn off that feature. But, for your custom needs, I suspect it may be more straightforward to just drop in your own code.
To read the state of a quest, call QuestMachine.GetQuestState(). In general, the QuestMachine class is your one-stop shop for all Quest Machine info. Example:
Whenever a quest changes state, it also sends a "Quest State Changed" message via the Message System. (To avoid typos, use QuestMachineMessages.QuestStateChangedMessage instead of the literal string "Quest State Changed". This API reference link also explains what info the message provides.) Your scripts can implement IMessageHandler to listen for messages. Example:
Instead of polling the quest state on a timer, use code similar to the example above to be notified when a quest state changes.
How you actually get the reward into the player's inventory depends on your inventory solution. For example, if you're using Opsive's Ultimate Inventory System (UIS), you can use UIS Lua Functions in the dialogue entry nodes' Script field. (The green arrows on the response nodes indicate that the nodes' Script fields contain something.) For example, the Script field for the "Magic sword" response might be:
If you have questions about any of this, please let me know. And if you are able to put together a mock-up of how you want your dialogue UI to look, we can discuss whether it would be more straightforward to adapt the SMS Dialogue UI prefab or write your own IDialogueUI implementation. (The DS Templates/Scripts folder has a starter script for implementing IDialogueUI, which just has a few methods such as ShowSubtitle and ShowResponses.)
Off the top, here are my high level recommendations:
- Manage all quests through Quest Machine. The only exception is using the QM/DS integration's Lua functions in conversations. (Don't use the DS QuestLog class, or DS QuestLogWindow or QuestTracker UIs.)
- In your QM quests' Offer Text and Dialogue Text, only use "Start Conversation" Quest Content elements to start DS conversations instead of using Quest Machine's dialogue UI.
- Use the SMS Dialogue UI prefab instead of Textline. They use the same underlying code, but Textline is a complete project with extra stuff that will just complicate things. Or possibly implement IDialogueUI instead (see below).
I'm not sure exactly what you want to do. Would you please provide a mockup of what the screen might look like?
You may find it simpler in the long run to write a custom implementation of IDialogueUI that does exactly what you want and no more.
The Textline project and SMS Dialogue UI record which subtitles have been shown for a conversation. When you resume the conversation, it repopulates the dialogue UI with those subtitles. If you need to stick with Textline/SMS Dialogue UI, it's possible to turn off that feature. But, for your custom needs, I suspect it may be more straightforward to just drop in your own code.
What extra quest-specific data do you need to add to quests? If it's just strings, you can add them to the quest's Labels list, which you can edit in the main quest info section. This is a list of strings that you can use for your own purposes.
To read the state of a quest, call QuestMachine.GetQuestState(). In general, the QuestMachine class is your one-stop shop for all Quest Machine info. Example:
Code: Select all
QuestState currentState = QuestMachine.GetState("peskyRabbits");
Code: Select all
public class MyQuestTracker : MonoBehaviour, IMessageHandler
{
// Using Awake for brevity; alternatively use OnEnable() and OnDisable() to properly remove listener when disabled.
void Awake()
{ // Listen for all Quest State Changed messages:
MessageSystem.AddListener(this, QuestMachineMessages.QuestStateChangedMessage, string.Empty);
}
void OnMessage(MessageArgs messageArgs)
{
string questID = messageArgs.parameter;
if (messageArgs.values[0] == null) // If first value is null, it's the main quest state, not a quest node state.
{
QuestState currentState = (QuestState)messageArgs.values[1];
Debug.Log($"Quest {questID} changed to state {currentState}");
}
}
}
Present the rewards in dialogue entry nodes in your DS conversation. Example:schmoey wrote: ↑Mon Mar 29, 2021 12:46 pmAll our quest machine dialogue comes from the dialogue system. How do I go about presenting the player with multiple choices for a reward (for example, I'm mostly just talking about defining buttons for custom actions etc) when it's got this bridged plugin setup?
How you actually get the reward into the player's inventory depends on your inventory solution. For example, if you're using Opsive's Ultimate Inventory System (UIS), you can use UIS Lua Functions in the dialogue entry nodes' Script field. (The green arrows on the response nodes indicate that the nodes' Script fields contain something.) For example, the Script field for the "Magic sword" response might be:
- Script: uisAddItem("Magic sword", 1, "", "")
If you have questions about any of this, please let me know. And if you are able to put together a mock-up of how you want your dialogue UI to look, we can discuss whether it would be more straightforward to adapt the SMS Dialogue UI prefab or write your own IDialogueUI implementation. (The DS Templates/Scripts folder has a starter script for implementing IDialogueUI, which just has a few methods such as ShowSubtitle and ShowResponses.)
Re: Please Fix My Terrible Dialogue System & Quest Machine Combined Setup
First of all, thank you!! I meant to reply sooner but got a bit swamped with work
I've been chewing through the bits you wrote and am definitely closer to something I'm happy with.
Apologies if I'm doubling over on some stuff from last time, but here's where I'm at problem wise!
Managing Quest Content through Quest Machine
Getting very close with this one. The last bit of remaining of content I ideally want in a quest is a list of strings. These are just ids used to reference a database of items.
The specific use case of these in a quest is to pass the quest with the minimum number of these items achieved. What I want to do is be able to set a bunch of specific conditions (ie encapsulating them within the QM quest and not my own quest database) and be able to reference them from outside the QM system.
For instance in the picture below the 4 nodes are the conditions that need to be met to succeed that quest. In a different system I need to be able to to get information about those specific nodes, at the very least a string id (points to our item db), and ideally an int as well to cover the amount needed. This is fine so that I can fill some custom UI with info about that item. Ideally I'd be able to set the node conditions state by script too but I realise if not I can use the message system.
Even as I'm writing this I think maybe I've been looking at it from the wrong angle. It occurs to me that you might suggest setting those values through the conditions, and then using one of the text (dialogue / journal / hud) write out the line that my UI system would read from. If that's the case then how would I go about retrieving the data for exactly those 4 nodes?
Starting a Dialogue / Quest
I may not have got what you meant about start conversation before, but I know why I'm using StartConversation in the first place... because I want to direct each actors dialogue to a specific bit of UI, and the only way I could get that going is to go StartConversation -> goes to the corresponding Actor transform with Dialogue Actor set -> goes to the corresponding UI section set in Override Dialogue UI.
I would very much like initiate a dialogue through a QM quest instead of a dialogue that calls StartConversation, but I have not been able to figure out how to get a quest to begin and start routing it's corresponding conversation data through the Dialogue Actor -> Override Dialogue UI setup. Whether I start a quest manually through a message, or Autostart conditions etc it obviously knows that the dialogue text is coming from a DS conversation but it would typically try and print it in one of the default UI widgets that I've probably long disabled somewhere.
Either way I want it going the same way it does when I start the dialogue then run StartConversation. So that's why currently I have to check for my own external conditions (sacrificing QM autostart functionality) -> run the conversation when those conditions are met -> conversation presents offer options to player to accept quest -> the accept button runs SetQuestState(questId, "active").
I strongly feel like there's some combination there to get it working from the QM side but I can't figure it out!
Custom Dialogue UI
I've started hacking away at a custom UI dialogue as per your suggestion (thank you again). To give you a clearer idea of what I'm trying to get done here's a couple pictures. I'd love any pointers you've got, or whether it's even possible. Essentially I'm trying to group conversation content without it clearing.
A list of actors on the left and when one is selected it shows all the conversations happening with that actor.
Here I've run three different conversations all with the same actor and I'd just have some spacer or line in the between each as a visual separator.
If conversation 3 progresses then obviously that part of the dialogue adds the next part of the conversation there, hence the grouping mentality.
This just shows off when selecting an actor that has no conversations running. Select the first actor again and it would appear like the first screenshot.
What do you think??
-- Oops, er, totally forgot to mention one of my issues here is that I can't seem to get the conversation linked to a subtitle. If that were even the right way to go about it... then at the very least I could rebuild the UI dialogue according to the conversation that each subtitle is in.
Thanks again you're a life saver! If we can get something going with the custom UI dialogue that is by far the biggest win, the other stuff I can make compromises on.
Cheers,
Simon
I've been chewing through the bits you wrote and am definitely closer to something I'm happy with.
Apologies if I'm doubling over on some stuff from last time, but here's where I'm at problem wise!
Managing Quest Content through Quest Machine
Getting very close with this one. The last bit of remaining of content I ideally want in a quest is a list of strings. These are just ids used to reference a database of items.
The specific use case of these in a quest is to pass the quest with the minimum number of these items achieved. What I want to do is be able to set a bunch of specific conditions (ie encapsulating them within the QM quest and not my own quest database) and be able to reference them from outside the QM system.
For instance in the picture below the 4 nodes are the conditions that need to be met to succeed that quest. In a different system I need to be able to to get information about those specific nodes, at the very least a string id (points to our item db), and ideally an int as well to cover the amount needed. This is fine so that I can fill some custom UI with info about that item. Ideally I'd be able to set the node conditions state by script too but I realise if not I can use the message system.
Even as I'm writing this I think maybe I've been looking at it from the wrong angle. It occurs to me that you might suggest setting those values through the conditions, and then using one of the text (dialogue / journal / hud) write out the line that my UI system would read from. If that's the case then how would I go about retrieving the data for exactly those 4 nodes?
Starting a Dialogue / Quest
I may not have got what you meant about start conversation before, but I know why I'm using StartConversation in the first place... because I want to direct each actors dialogue to a specific bit of UI, and the only way I could get that going is to go StartConversation -> goes to the corresponding Actor transform with Dialogue Actor set -> goes to the corresponding UI section set in Override Dialogue UI.
I would very much like initiate a dialogue through a QM quest instead of a dialogue that calls StartConversation, but I have not been able to figure out how to get a quest to begin and start routing it's corresponding conversation data through the Dialogue Actor -> Override Dialogue UI setup. Whether I start a quest manually through a message, or Autostart conditions etc it obviously knows that the dialogue text is coming from a DS conversation but it would typically try and print it in one of the default UI widgets that I've probably long disabled somewhere.
Either way I want it going the same way it does when I start the dialogue then run StartConversation. So that's why currently I have to check for my own external conditions (sacrificing QM autostart functionality) -> run the conversation when those conditions are met -> conversation presents offer options to player to accept quest -> the accept button runs SetQuestState(questId, "active").
I strongly feel like there's some combination there to get it working from the QM side but I can't figure it out!
Custom Dialogue UI
I've started hacking away at a custom UI dialogue as per your suggestion (thank you again). To give you a clearer idea of what I'm trying to get done here's a couple pictures. I'd love any pointers you've got, or whether it's even possible. Essentially I'm trying to group conversation content without it clearing.
A list of actors on the left and when one is selected it shows all the conversations happening with that actor.
Here I've run three different conversations all with the same actor and I'd just have some spacer or line in the between each as a visual separator.
If conversation 3 progresses then obviously that part of the dialogue adds the next part of the conversation there, hence the grouping mentality.
This just shows off when selecting an actor that has no conversations running. Select the first actor again and it would appear like the first screenshot.
What do you think??
-- Oops, er, totally forgot to mention one of my issues here is that I can't seem to get the conversation linked to a subtitle. If that were even the right way to go about it... then at the very least I could rebuild the UI dialogue according to the conversation that each subtitle is in.
Thanks again you're a life saver! If we can get something going with the custom UI dialogue that is by far the biggest win, the other stuff I can make compromises on.
Cheers,
Simon
Re: Please Fix My Terrible Dialogue System & Quest Machine Combined Setup
Hi,
1. Store the item amounts in quest counters. You can set up a counter to synchronize its value with an external script such as your item database. Set the counter's update mode to Data Sync, and configure your item database to send a message whenever an item amount changes:
It's fairly easy to read a quest counter in C#:
2. Or write a custom quest condition that reads from your item database. Quest Machine's Templates folder has a commented starter script. (Take a look at any of the inventory system integrations for example code.)
If I understand correctly, you really just need to implement a custom quest condition that checks your item database, which is exactly the kind of thing Quest Machine is designed for.
However, if you need to get the values of those quest nodes' conditions, do something like:
When you talk to a Quest Giver (e.g., call QuestGiver.StartDialogueWithPlayer()) and the quest returns a 'Dialogue System Conversation' quest content, it starts a Dialogue System conversation in which the actor is the player and the conversant is the Quest Giver GameObject. Can you put the Override Dialogue UI on the Quest Giver GameObject? The Dialogue System will check the actor and conversant GameObjects for Override Dialogue UI components.
Alternatively, I'm thinking of adding an optional Conversant field to the 'Dialogue System Conversation' quest content so you can specify a different conversant than the quest giver.
Instead of maintaining several parallel dialogue UIs, what if the entire thing is one dialogue UI with multiple panels? Internally, it could be ordered logically kind of like this:
In your dialogue UI code's ShowSubtitle() method, look at the Subtitle's speakerInfo/listenerInfo and dialogueEntry.conversationID to determine which subpanel to add the subtitle to. Example:
Here are two different ideas:
1. Store the item amounts in quest counters. You can set up a counter to synchronize its value with an external script such as your item database. Set the counter's update mode to Data Sync, and configure your item database to send a message whenever an item amount changes:
Code: Select all
MessageSystem.SendMessage(this, DataSynchronizer.DataSourceValueChangedMessage, "potatoes", inventoryDatabase.GetAmount(Potato)
Code: Select all
int numPotatoes = quest.GetCounter("potatoes").currentValue;
If I understand correctly, you really just need to implement a custom quest condition that checks your item database, which is exactly the kind of thing Quest Machine is designed for.
However, if you need to get the values of those quest nodes' conditions, do something like:
Code: Select all
QuestNode questNode = quest.GetNode("Has Potato x 3");
bool hasEnough = questNode.areConditionsMet;
This may become moot depending on my answer to your next question, but I'll provide some info here anyway.
When you talk to a Quest Giver (e.g., call QuestGiver.StartDialogueWithPlayer()) and the quest returns a 'Dialogue System Conversation' quest content, it starts a Dialogue System conversation in which the actor is the player and the conversant is the Quest Giver GameObject. Can you put the Override Dialogue UI on the Quest Giver GameObject? The Dialogue System will check the actor and conversant GameObjects for Override Dialogue UI components.
Alternatively, I'm thinking of adding an optional Conversant field to the 'Dialogue System Conversation' quest content so you can specify a different conversant than the quest giver.
You'll want to keep writing your own dialogue UI code for this. But there may be a simpler approach:
Instead of maintaining several parallel dialogue UIs, what if the entire thing is one dialogue UI with multiple panels? Internally, it could be ordered logically kind of like this:
- This guy
- Conversation 1
- Entries...
- Conversation 2
- Entries...
- Conversation 3
- Entries...
- Conversation 1
- Important person
- Conversation 1
- Entries...
- Conversation 2
- Entries...
- Conversation 1
In your dialogue UI code's ShowSubtitle() method, look at the Subtitle's speakerInfo/listenerInfo and dialogueEntry.conversationID to determine which subpanel to add the subtitle to. Example:
Code: Select all
string characterName = subtitle.speakerInfo.isNPC ? subtitle.speakerInfo.Name : subtitle.listenerInfo.Name;
string conversationTitle = DialogueManager.masterDatabase.GetConversation(subtitle.dialogueEntry.conversationID).Title;
// Now put the subtitle's formattedText.text into the subpanel characterName.conversationTitle.
I'm not sure what you mean, but by context I think I may have answered it above. (?)