Page 1 of 1

Variable changes ignored during runtime

Posted: Thu Aug 08, 2019 7:45 am
by Interceptor
Hello there,

I am very sorry in case I've just created any kind of duplicite question here, but I was not able to resolve this curious issue of mine nor find any mentioning of it here or in documentation.

The issue I am facing is quite simple:

I use one variable that stores an info whether the NPC was spoken to before or not, quite simple. It's being checked on very first line of the NPC to decide what greeting line should be used, and obviously, it is set to true once "first met" greeting of the NPC is performed. (just to make things clear, I have inserted one empty node with None() sequence between Start node and these two greetings, just to make sure condition gets evaluated)

When the game is started in Unity Editor, "NPC met" variable is set to false as it should be, and as my character approaches the NPC, conversation starts and it evalutes the condition the right way, so the "first met" greeting plays just as intended. This line also sets the variable to true (can visually confirm that from Variables tab). However, when the conversation ends, and I attempt to begin the conversation again, it again starts with "first met" greeting even though the "NPC met" variable is set to true (again, can visually confirm that). Interestingly enough, when I stop the game and start it again, the conversation starts with "hello again" line as it should, since the variable lives on and stays true, again, as it should be.

What am I missing?

Re: Variable changes ignored during runtime

Posted: Thu Aug 08, 2019 9:30 am
by Tony Li
Hello,

Do you mean the Watches tab?

The Variables tab shows the design-time state of the variable -- that is, its value in the dialogue database asset file. This should not change during play.

When the game starts, the Dialogue System will store the value of each variable in Lua. You can check and set the Lua variable in nodes' Conditions and Script fields. For example, to set the Lua variable "first met" to true, you can use the "..." dropdowns to set the Script field to:

Code: Select all

Variable["first_met"] = true
In the Dialogue Editor's Variables tab, "first met" should still be false. But the Watches tab will show that "Variable > first met" is true.

Similarly, you can use the "..." dropdowns to set the Conditions field to:

Code: Select all

Variable["first_met"] == true
to check if "first met" is true.

When you exit play mode, the Lua environment will be cleared. So the next time you enter play mode, the "first met" Lua variable should be false again, which is its original value in the dialogue database asset.

---

However, if you've set up the save system and are using an Auto Save Load component, it's possible that it's saving the current state of the variables when you exit play mode. And when you re-enter play mode, it would load the saved state of the variables. I'm not sure that this is what's happening, but I thought I'd mention it as a possibility.

Re: Variable changes ignored during runtime

Posted: Thu Aug 08, 2019 1:19 pm
by Interceptor
Alright then, seems like I need to explain the whole thing. I've simplyfied my question above as I wanted to avoid writing horribly complex one and save you from reading a freaking novel and rather concentrate on the core of my issue, but your answer indicates I pretty much understand things from the API in a wrong way.

What I am trying to do:

I'm creating a global variables system - system that stores certain acomplishments in a game a allows other systems, such as dialogue system, to react accordingly.

Since I am a big fan of Scriptable Object-based architecture in Unity, I use a scriptable object called Globals, which contains an array of global variables. Single global variable itself is a scriptable object as well, so the array in Globals is an array of assets of type Global (maybe I should rename Global class to avoid confusing?).

For better understanding, this is how it looks.
Image

Global (single global variable) scriptable object looks like this:
Image

I have written a custom saver (based on your Saving system) to save a state of all these Global scriptable objects in Globals array so that they can be reloaded to the state they were saved in at any given time, and it works quite well now (was a though nut to crack as an array of scriptable objects can't be serialized so that it will serialize along with all their properties, but the workaround of copying this array into list of structs containing analogic fields, serializing it and after deserialization, copying each struct content into its respective counterpart in Global scriptable object seems to work alright).

The problematic part begins with linking these globals into Dialogue System. I have written a wrapper method what basically takes a Global variable scriptable object, takes its name and value properties and send them to the Dialogue System liek this:

Code: Select all

	public void InsertGlobalVariableIntoDialogue(string name, string value, FieldType type)
	{
		var database = GameObject.Find("Dialogue Manager").GetComponent<DialogueSystemController>().initialDatabase;
		var template = Template.FromDefault();
		var variableID = template.GetNextVariableID(database);
		var variable = template.CreateVariable(variableID, name, value, type);
		bool add = true;
		foreach (Variable dialogueVariable in database.variables)
		{
			if (dialogueVariable.Name == name)
			{
				add = false;

				if (type == FieldType.Text)
				{
					if (dialogueVariable.InitialValue != value)
					{
						dialogueVariable.InitialValue = value;
					}
				}

				if (type == FieldType.Boolean)
				{
					if (dialogueVariable.InitialBoolValue != ToBool(value))
					{
						dialogueVariable.InitialBoolValue = ToBool(value);
					}
				}

				if (type == FieldType.Number)
				{
					if (dialogueVariable.InitialFloatValue.ToString() != value)
					{
						dialogueVariable.InitialFloatValue = float.Parse(value);
					}
				}
			}
		}
		if (add)
		{
			database.variables.Add(variable);
		}
	}
Condition

Code: Select all

if (dialogueVariable.Name == name)
checks whether the variable already is present in Dialogue System variables, and if so, checks whether its value is different or not. If it is, it passes a new value from the scriptable object.

Then, there is an iterative method going through all Global variables and inserting them accordingly:

Code: Select all

		foreach (Global variable in globals) {
			InsertGlobalVariableIntoDialogue(variable.name, variable.value, FieldType.Text);
		}
Note that

Code: Select all

globals
is the above mentioned array of Global scriptable objects (array displayed in picture No.1 above).

It seemed to do what it is supposed to do - after running this code, Dialogue System Variables tab shows exactly what it should:

Image

Now, as I've mentioned before, I use a condition in this small dialogue tree:
Image
to determine what line it should choose. Condition is as simple as:

Code: Select all

Variable["13_PCKnowsSantiago"] == "false"
or true respectivelly for the case our dear Mr. Santiago knows player character already. Also note that I use string variable type, it's much more flexible for other types of global variables (such as NPC attitude - say, negative, neutral, positive or similarily so), but of course, much more vulnerable to mistypes.

To actually change a variable after certain conversation line happens, I do not use any Lua code in Script field, as that would require to write a sort of unwrapper method that would pull changes variables from Dialogue System variables back into my code. I have tried to do so, although it did not work at all. And as you explained in your response above, I guess I can see why,

I instead use On Enable() event, in which I simply call a public method on Global scriptable object to set itself to certain value, like this:
Image

Yes, this does not change a variable value inside Dialogue System. But I don't even have to do that. I don't need to use it anywhere else, I don't actually need any Global variable change to be present inside it in one converstion cycle, they always determine only one condition in one conversation.

So how do I expect to this value change to take effect then? Well, since this changed value is written to the scriptable object, I simply re-run wrapper(or let's call it sync)code showed above on every conversation start and end (well, even start alone would be enough). And it works. Only problem is, it does not take any effect during one runtime seasson. When the variable is set to true, it is synchronized with Dialogue System variables upon conversation ending and/or starting it again. It does take effect when the runtime is stopped and lanuched again,but it does not work in one runtime seasson.

It's really breaking for me to find out Variable tab does not show and actual state of these varables (as I've been relying on it all the time :D ). But it's my fault, I guess "Initial value" field name in Variables tab is quite explicit about what this field actually shows.

I recon all of my several-days-long attempt is quite wrongly performed.

Please, do you have any suggestion on how to approach on what I am trying to achieve? Either using any of above mentioned techniques or completely new approach.

Thank you very much for your help by the way.

Re: Variable changes ignored during runtime

Posted: Thu Aug 08, 2019 5:22 pm
by Tony Li
Treat the dialogue database as read-only at runtime.

This also makes the code shorter and simpler. At runtime, set the variables like this:

Code: Select all

foreach (Global variable in globals) {
    DialogueLua.SetVariable(variable.name, variable.value);
}
Technically, that's all you need. You can enter text like this in nodes' Conditions and Script fields, and in Dialogue System Triggers' Condition and Run Lua Code sections:

Code: Select all

Variable["13_PCKnowsSantiago"] == "false"
If you want to use the "..." dropdown menus, you will need to add the variables to the dialogue database at design time -- not at runtime. Write an editor script similar to your existing code to add the variable.

Re: Variable changes ignored during runtime

Posted: Fri Aug 09, 2019 12:45 pm
by Interceptor
Oh my god, thank you,

Code: Select all

DialogueLua.SetVariable(variable.name, variable.value);
did the job.

Yeah, I've planned to write custom editor script for this since I intend to build some sort of a framework (e.g. user friendly), but I am not very skilled in that field yet.

Thank you again and wish you all the best.

Re: Variable changes ignored during runtime

Posted: Fri Aug 09, 2019 1:04 pm
by Tony Li
Glad to help!