Unity Dialogue System - Removing the Choices and Updating their Data

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
We have our data ready to save but there is one last thing we need to do before we start saving our graph, and that's allowing the removal of  choices from our multiple choice nodes. Right now we can add as many choices as we'd want,  but there is no way for us to remove any of them. We'll also be updating the choices list data accordingly. Let's start this off by swapping our Node Choices Type with the choice save data we've created previously. So go to our "DSNode" script and inside swap the "string" type from the "Choices" list with "DSChoiceSaveData" instead. Of course, don't forget to import the "Data.Save" namespace for this class to be recognized. Swap it in our list initialization as well. This will allow us to save the connected nodes and  the text of the choice in our node choices list. Note that our "Single" and "Multiple"  choice nodes scripts also use this variable, so we'll need to update them  to use the new type as well. So go to our "DSSingleChoiceNode" script and  start by importing the "Data.Save" namespace. Then, in it's "Initialize" method, create a  new variable of type "DSChoiceSaveData", to which I'll name "choiceData" and call its constructor. I'll use the Object Initializer here to  pass in the "Text" of "Next Dialogue". When that's done, pass in "choiceData" to the "Choices.Add()" method instead  of the string it currently has. Then, swap the Draw method "string" type from the  "foreach" loop to be "DSChoiceSaveData" as well and pass in "choice.Text" to the "CreatePort" method. Now in our "DSMultipleChoiceNode" script, do the same in our "Initialize" method  after importing the "Data.Save" namespace. So simply create a new variable of type "DSChoiceSaveData", set it's text to be "New Choice" and pass the variable to the "Add" method. In the "Draw" method, in our "addChoiceButton" we'll have to create a new choice data too so simply copy the code above and paste it here. Remove the last "Add" method and then pass in "choiceData.Text"  to the "CreateChoicePort" method. In our output container "foreach" loop,  swap the "string" with "DSChoiceSaveData" and pass in "choice.Text" to the  "CreateChoicePort" method as well. We can now start deleting our choices. We'll be doing that in our "deleteChoiceButton" in the "CreateChoicePort" method by giving it a callback. So pass in an empty parameter callback and  inside we'll start by removing our choice. What we'll need to do is quite simple: If there is more than one choice, we can  press on this button to remove the choice. The "if" is there because I want to always  have a minimum of one choice per node, because if we didn't want custom choices, we  could simply make it a single choice node. If the choice port is connected, then we need to disconnect it, which will remove the edge from the Graph. To finalize it, we'll need to remove the port from the graph itself. (and the choice from the Choices list) So in our callback start by typing  in "if (Choices.Count == 1)" and return if that's the case. With this, if there is more than one  choice, we can remove that choice. Next, let's remove our connections if  our choice port is connected so type in "if (choicePort.connected)", then  we can remove the connections. However, we'll need the "graphView" variable  here, which we only have in our "DSNode" script, as it's a private variable. So let's go back to our "DSNode" script and make the "graphView" variable  be "protected" instead of "private". With this, all classes that inherit from the  "DSNode" class have access to this variable. So back in our multiple choice node, pass in "graphView.DeleteElements(choicePort.connections)". This will remove any connections this port has. We can now remove our choice from the  Choices list and the port from the graphview. However, we don't really have anything here that allows us to know what Choices element we need to remove. What we'll do is quite simple: We'll simply pass in our choiceData to the method and remove it. However, we will do that by using  the port "userData" variable. The "userData" variable is simply a variable in the "VisualElement" class that allows us to give it any data. This means that once we pass it in, we can get the choiceData from any script as long as we have access to the port. This will be useful for us in a bit  when setting the choice node ID. So instead of having a string with  the text of the choice as a parameter, simply pass in a parameter of type  "object" and name it "userData". Then, right under the "choicePort" declaration  type in "choicePort.userData = userData". Under that, create a new variable of type "DSChoiceSaveData", to which I'll name "choiceData" and assign  the "userData" parameter to it, casting it into "DSChoiceSaveData". With this, we now have access to our choiceData variable. Of course, make sure you now pass in the choiceData when we call this method instead of the Text. When that's done, back in our method, update the  old string usage to be "choiceData.Text" instead. All that's left now is to remove the choiceData  from the Choices list and the port from the graph, so in our delete button callback type in  "Choices.Remove(choiceData)". Right under that type in  "graphView.RemoveElement(choicePort)". Go to the "DSSingleChoiceNode" script and pass in the foreach "choice" variable to the "choicePort.userData" variable as well. If we save and go back to Unity, we  should now be able to remove our choices. The edges, or port connections should  also be deleted when a choice is removed. However, there is one thing we still need to do. Whenever we create an edge, or a  connection between a choice and another, we should update the output choice data and set its Node ID to be the ID  of the node it got connected with. We should also remove that ID whenever we  remove that edge, or disconnect the choices. The way we'll be doing that is by  overriding the "graphViewChanged" callback. So let's go to our "DSGraphView" script  to the bottom of our "Callbacks" region. We can make it in two different ways: The first one is by creating a method  that accepts and returns a variable of type "GraphViewChange". This class holds certain variables that we'll need. Then, we simply set the callback to be that method, which has whatever code we want in it. The second one is what we've done so far: we simply override the callback with the necessary parameters. This is what we have used in all of our other callbacks. We'll be going with the second one. So create a new private method  that I'll name "OnGraphViewChanged" and inside type in "graphViewChanged"  and override the callback. This callback accepts one parameter which are the  Graph View Changes, so I'll name it "changes". This callback requires the parameter variable to be returned, so make sure you type in "return changes;" at the bottom. We'll be making use of two variables here: The "edgesToCreate" variable, which will allow us to go through each edge that we've created, get its port and set it's Node ID, and then the "elementsToRemove" variable, which will allow us to go through every  element we've removed from the graph, get the ones that are of type "Edge", get their port and remove their Node ID. So let's start by going through our "edgesToCreate" list. So type in "if (changes.edgesToCreate != null)", and this is simply so that we do not iterate  through a list if there are no elements on it. In here, we'll loop through our list by typing  in "foreach (Edge edge in changes.edgesToCreate)" and inside we'll need to get the next  node and the output port choice data. Thankfully, we can very easily do that with the edge variable. This is because the "Edge" class has an  "input" and "output" variable which gets the input and output port of that edge. Besides that, the "Port" class also has a "node" variable, which gets the node that the port belongs to. So type in "DSNode" and we'll be getting the "nextNode", so "= edge.input", to get the port, ".node". Of course, don't forget to cast it to our "DSNode". Remember that the "input" port is the  port that the choices will connect to. Then, we need to get the "choiceData" of the output port, as we don't really care about the output node. So type in "DSChoiceSaveData" and  make sure you import its namespace, and I'll name it "choiceData". Then, assign the "edge.output.userData" variable to it and cast it to "DSChoiceSaveData". This will get the choiceData we've passed  in when we created our choice port. When that's done we have all we  need, so type in "choiceData.NodeID" and assign the "nextNode.ID" value to it. Our node Choices list will also be updated because this choiceData is referencing to that list element. To remove the Node ID it will be quite similiar. However, because it is "elementsToRemove"  and not "edgesToRemove", we'll need to check if the type of the element is of "Edge" Type. So type in just under our if statement  "if (changes.elementsToRemove != null)", we'll start by creating a variable of type "Type", to which I'll name "edgeType" and assign the value of "typeof(Edge)" to it. Then, iterate through the list by typing in "foreach (GraphElement element in changes.elementsToRemove)" and if the type is not edge type, we continue, so "if (element.GetType() != edgeType)",  we "continue;" to the next element. If it is of type edge, we need to get the  Edge through casting the element so type in "Edge edge = element" and cast it to "Edge". We can now get our choice data through  the edge output port variable so type in "DSChoiceSaveData" and I'll name it "choiceData", equals to "edge.output.userData". Don't forget to cast it. To remove the Node ID we simply type in  "choiceData.NodeID = """. Unless I've attempted it wrong, using  the "RemoveElement" in an edge in our "deleteSelection" callback didn't add  the edge to the "elementsToRemove" list. Because we're using the "DeleteElements" there,  removing an edge from the graph will also add it to the "elementsToRemove" list, which means the Node ID will also reset. Of course, don't forget to call this method in  our constructor for the callback to get overrided. With this done, we should be able  to remove all of our choices, except the last one, and  our node ID's should be set. There is still one thing left to do though and that's updating the choice data text  whenever we update the text field. Thankfully, this is pretty straight forward so  go back to our "DSMultipleChoiceNode" script and in our "CreateChoicePort" method we'll be adding a callback to our "choiceTextField" variable, so pass in "null" for the label and then pass in a callback to which I'll name "callback". Inside, simply type in "choiceData.Text = callback.newValue". Our choice data text should now be updating  every time we type something in our text field. With that done, we now have  everything we need for our choices.
Info
Channel: Indie Wafflus
Views: 306
Rating: undefined out of 5
Keywords: Unity Dialogue System, Unity Dialog System, Unity Graph View, Unity Node, Unity UI Toolkit, Unity Dialogues, Unity Node Based Dialogue System, Dialogue System, Node Based Dialogue System, Unity Dialogs, Unity Visual Elements, Node System, Dialogue System Tutorial, Unity 2D, Unity, Unity Tutorial, Unity Dialogue System Tutorial, Unity 3D, Unity3D, Unity2D
Id: vV-sN0JrrW0
Channel Id: undefined
Length: 13min 10sec (790 seconds)
Published: Tue Sep 14 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.