No scripting is required in the Dialogue System. However, the Dialogue System does offer a general-purpose scripting language called Lua that provides a very powerful method of controlling the flow of conversations, checking and changing quest states, and more. In most cases, you can use simple point-and-click menus.
The Dialogue System uses the data model established by Chat Mapper, another professional dialogue authoring tool commonly used in the industry. Information about all actors, items, locations, variables, and conversations is stored in Lua tables. You can control conversations by specifying Lua conditions and scripts, typically by using point-and-click menus. For those who are interested, the Chat Mapper manual has more background info about Lua and the tables that the Dialogue System uses, in the section titled Scripting with Lua.
At runtime, the Dialogue System treats your dialogue database as read-only. It loads the database values into Lua where the current values can be checked and changed during play.
You can use Lua in the following areas of the Dialogue System:
In most places where you can enter Lua code manually (if you prefer), you can also click a '...' button to switch the field into point-and-click mode. In this mode, you can use dropdown menus without having to type any code:
Click '+' to add new conditions or actions. Click Revert to cancel or Apply to apply your selections, which will close the dropdown menus and write the Lua code for you:
Note: As a convenience, if variable names contain "." such as "Global.Temperature", the wizards will show them in submenus – for example, the dropdown will have a submenu named Global containing an item named Temperature.
If you prefer to write Lua code directly, you'll find that it's similar to C# or UnityScript. For more information on Lua, see www.lua.org. There are a few syntax differences to be aware of:
Operator | C#/UnityScript | Lua | Lua Example |
---|---|---|---|
Not equal | != | ~= | Quest["Kill_5_Rats"].State ~= "success" |
Logical AND | && | and | Variable["HasCoke"] and Variable["HasMentos"] |
Logical OR | || | or | (Actor["Player"].Age > 21) or Item["Beverage"].IsNonalcoholic |
String concatenation | + | .. | Actor["Player"].FullTitle = "Dread Lord " .. Actor["Player"].Name .. " of Skull Island" |
if | if(x==y) | if (x==y) (blank space req'd) | if (x == y) then foo() end |
Comment | // | – | – This is a comment. |
Special Exception: LuaInterpreter "and not"
The default Lua implementation that ships with the Dialogue System is LuaInterpreter. This is a fast, lightweight C# implementation of Lua. However, it does not parse one valid Lua syntax:
X and not Y
. You can work around this limit by modifying your syntax slightly or by switching to a different Lua implementation.
Note that Lua, like C# and UnityScript, is case-sensitive.
Here are some examples that you can use in a dialogue entry's Conditions field or the Condition section of a Dialogue System Trigger component.
Check if a Boolean variable has been set true (note the use of double equals "==" to test equality):
Variable["flippedSwitch"] == true
Check if a quest is in a specific state:
CurrentQuestState("Kill 5 Rats") == "success"
Check if the player is at least 21 and has at least one beer:
(Actor["Player"].Age >= 21) and (Item["Beer"].Count > 0)
Here are some examples of setting values. You can use them in a dialogue entry's Script field or in the Lua Code action of a Dialogue System Trigger
Set variables (note the use of single equals "=" for assignment):
Variable["spokeToKing"] = true; Variable["Gold"] = Variable["Gold"] + 50
Set a quest to a specific state:
SetQuestState("Kill 5 Rats", "active")
Combining two strings (using Lua's '..' notation):
Actor["Player"].FullTitle = "Dread Lord " .. Actor["Player"].Name .. " of Skull Island"
A table in Lua is similar to an array or dictionary in C# or UnityScript. The following Dialogue Systemspecific tables are available to your Lua scripts:
Table | Description | Example |
---|---|---|
Actor[] | Two-dimensional table containing all actors. | Actor["Player"].Age |
Item[] | Two-dimensional table containing all items/quests. | Item["Anvil"].Weight |
Quest[] | An alias for the Item[] table. | — |
Location[] | Two-dimensional table containing all locations. | Location["Starbase"].Description |
Conversation[] | Two-dimensional table containing all conversations, indexed by ID. | Conversation[9].Title |
Variable[] | One-dimensional table containing user variables. | Variable["Alert"] = "Hello!" |
In this Lua code:
Actor["Player"].Age = 21
the string "Player" is the key or index. The Actor["Player"] part is the table element. This is sometimes called a record in Lua guides. The Age part is the field. The code above sets the player's Age field to 21.
The Dialogue System also manages these variables in the Variable[] table:
Variable | Description |
---|---|
Variable["Alert"] | Set to tell the Dialogue System display an alert message. |
Variable["Actor"] | The name of the actor in the current conversation (if any) |
Variable["Conversant"] | The name of the conversant in the current conversation (if any) |
Variable["ActorIndex"] | The actor's index in the Actor[] Lua table |
Variable["ConversantIndex"] | The conversant's index in the Actor[] Lua table |
The variables Variable["Actor"] and Variable["Conversant"] contain the display names of the participants. The variables Variable["ActorIndex"] and Variable["ConversantIndex"] are the indices in the Lua Actor[] table. You can use them in your dialogue entries' Conditions and Script fields. For example, to check if the current conversant is at least 21 years old:
Actor[ Variable["ConversantIndex"] ].Age >= 21
Table keys and field names are generically called indices. The Dialogue System follows a convention established by Chat Mapper:
In Chat Mapper tables, replace all space characters, hyphens, forward slashes, quote characters, and parentheses with underscores in indices.*
Here are some examples:
In the Dialogue Database | In Lua |
---|---|
A variable named Kissed the Frog | Variable["Kissed_the_Frog"] |
An actor named Mister Big | Actor["Mister_Big"] |
The actor's Favorite Color field | Actor["Mister_Big"].Favorite_Color |
A variable named Kiss/Kill | Variable["Kiss_Kill"] |
The Dialogue System adds several useful functions to Lua, including:
These Chat Mapper functions are available in Lua to record statuses and relationship values. The descriptions of these functions come from the Chat Mapper documentation:
Chat Mapper can track a status that is defined between any two assets, which is referred to as a mutual status. To set or access a mutual status, use the following two functions:
Examples:
SetStatus( Actor["Player"], Location["Apartment_3B"], "Home" )
GetStatus( Actor["Player"], Location["Apartment_3B"] ) == "Home"
Chat Mapper can also track numerical relationship values between any two actors. Relationships can be used to control NPC responses based on a relationship value. These are the relationship fuinctions:
Examples:
SetRelationship( Actor["Princess"], Actor["Player"], "Lust", 50 )
DecRelationship( Actor["Jim"], Actor["Player"], "Trust", 2 )
GetRelationship( Actor["Jim"], Actor["Player"], "Trust" ) < 1
The Dialogue System does not support the Chat Mapper functions below, which have have no meaning outside Chat Mapper:
To show or hide an alert message:
Variables of type Actor, Item, or Location store the ID of an Asset (Actor, Item, or Location). This is a number, not the asset's Name field. To convert an ID to a Name, use:
Use ChangeActorName() to change
where:
Example:
ChangeActorName("Mysterious Stranger", "Mephistopheles")
Notes:
If you add the component Conversation Position Stack to your Dialogue Manager, it will make three new Lua functions available:
You can use these functions to dip into side conversations and then return to the original conversation at the point you left it.
Note: If you use PushConversationPosition() in a group node, it will return to the node that preceded the group. This allows the preceding node (usually an NPC node) to play its subtitle and re-evaluation the group's conditions.
To get and set quest states (described in more detail in Quests):
While you can technically set quest states by setting the Quest[] Lua table directly, it's better to use these functions because they also update the quest tracker HUD and send OnQuestStateChange messages that your scripts can listen for.
To update the quest tracker in Lua – for example, after updating a counter variable – use this Lua function:
The Conditional() function checks a condition. If true, it returns a return value; otherwise it returns a blank string.
where:
Example:
The functions GetEntryText(), GetEntryBool(), and GetEntryNumber() retrieve the values of dialogue entry fields from the dialogue database. Dialogue entry fields are not included in the Lua environment, but you can these functions to get their values directly from the database. They retrieve entries from the currently-active conversation (if a conversation is active).
where:
The function GetLocalizedText() retrieves localized versions of fields in the Actor, Item, Quest, or Location tables. For information about setting up localized fields, see Localization.
where:
GetLocalizedText() Example:
Say your actors have a "Nickname" field and a Spanish-localized field named "Nickname es". To use the localized version in a conversation, use the [lua(...)] tag to call GetLocalizedText():
The function GetTextTableValue() retrieves the localized translation of a text table field. It uses the text table assigned to the Dialogue Manager's Localization Settings section. If that text table doesn't have the specified field, it checks for a UILocalizationManager component and uses its text table.
The function RandomElement() returns a random element from a list. It's commonly used in [lua()] tags to add variety to your dialogue text.
where string is a string of elements separated by the horizontal bar character ('|').
RandomElement() Example:
Say you have an NPC that greets the character with a random honorific title. In your dialogue database, click on the Variables tab and define a variable named Honorifics that contains this list:
"Esteemed|Revered|Great|Magnificent"
Or, to set it in Lua code:
Variable["Honorifics"] = "Esteemed|Revered|Great|Magnificent"
In the dialogue entry, set the text to:
The result is that the NPC will randomly use one of the element in the Honorifics list, such as "Greetings, Esteemed One!" or "Greetings, Great One!"
The source string doesn't have to be set at design time. You can generate it dynamically during gameplay. You can use RandomElement() for other purposes, too, such giving the player a random item upon completion of a quest, or even choosing a random quest out of a list.
The function RandomizeNextEntry() tells the next NPC dialogue entry to choose randomly randomly from the list of valid NPC dialogue entries instead of using the first one in the list. It's equivalent to the RandomizeNextEntry() sequencer command, giving you the choice of whether you want to use a sequencer command or a Lua function. It takes no arguments:
The RandomizeNextEntryNoDuplicate() function works the same way but will try to avoid choosing the same entry it chose the last time RandomizeNextEntryNoDuplicate() was called on this entry. It's equivalent to the RandomizeNextEntry() sequencer command with a parameter of true
. It takes no arguments:
The Dialogue System includes Lua functions that synchronize variables and quest states to all clients using Unity Networking's High Level API (UNET HLAPI). To make these functions available:
USE_UNET
The functions above set a variable locally and on all other clients. Use in place of Variable["foo"] = value when you want all clients to receive the value of the variable.
The functions above set a quest state or quest entry state on all clients. Use in place of SetQuestState() and SetQuestEntryState().
For details on the scripting distinction between database fields and Lua values, see Dialogue Database Fields and Lua Values.
The PixelCrushers.DialogueSystem.DialogueLua class is the easiest way to access Lua data.
To get and set data in the standard Dialogue System Lua tables – Actor[]
, Item[]
, Location[]
and Variable[]
– use these methods:
Method | Description | Example |
---|---|---|
GetVariable() | Get the value of a variable* | bool hasBox = DialogueLua.GetVariable("hasBox").asBool |
SetVariable() | Set the value of a variable | DialogueLua.SetVariable("hasBox", true); |
GetActorField() | Get the value of a field in an actor | int xp = DialogueLua.GetActorField("Player", "XP").asInt; |
SetActorField() | Set the value of a field in an actor | DialogueLua.SetActorField("Player", "Intoxicated", true); |
GetItemField() | Get the value of a field in an item | float weight = DialogueLua.GetItemField("Anvil", "Weight").asFloat; |
SetItemField() | Set the value of a field in an item | DialogueLua.SetItemField("Anvil", "Cost", 50); |
GetQuestField() | Get the value of a field in a quest. Equivalent to GetItemField() | string state = DialogueLua.GetQuestField("Kill 5 Rats", "State").asString; |
SetQuestField() | Set the value of a field in a quest. Equivalent to SetItemField() | DialogueLua.SetQuestField("Kill 5 Rats", "State", "success"); |
GetLocationField() | Get the value of a field in a location | string desc = DialogueLua.GetLocationField("Moonbase", "Description").asString; |
SetLocationField() | Set the value of a field in a location | DialogueLua.SetLocationField("Moonbase", "Description", "A desolate lunar spaceport."); |
The DialogueLua class automatically converts table indices' spaces and hyphens to underscores. When using the DialogueLua class, you can ignore the note in Important Note About Table Indices. (However, when bypassing the DialogueLua class and using the Lua class directly, you must remember to convert spaces and hyphens to underscores yourself. You can use the DialogueLua.StringToTableIndex() function to do this.)
The DialogueLua.GetXXX()
methods return a Lua.Result
value. To get a basic data type, use properties such as asString
, asInt
, asFloat
, and asBool
. For example:
The Actor[]
, Item[]
, and Location[]
tables are two-dimensional, meaning each element in the table has fields. Use the DialogueLua.GetXXXField()
methods to access their fields.
The Variable[]
table, on the other hand, is one-dimensional. The elements are regular data types (Boolean, string, number, etc.), so you cannot use the DialogueLua.GetXXXField()
methods. Instead, use DialogueLua.GetVariable()
.
Note: DialogueLua.GetVariable() has overloaded methods in which you can specify a default value if the variable isn't defined. Using these overloaded methods, you don't need to use .asXXX
at the end. Examples:
The PixelCrushers.DialogueSystem.QuestLog class provides simpler functions to work with quests. (See Quests.)
To get the version of a field for the current language, use the localized versions of these methods:
Method | Description |
---|---|
GetLocalizedActorField() | Get the value of a localized field in an actor (e.g., "Description es" for Spanish) |
SetLocalizedActorField() | Set the value of a localized field in an actor |
GetLocalizedItemField() | Get the value of a localized field in an item |
SetLocalizedItemField() | Set the value of a localized field in an item |
GetLocalizedQuestField() | Get the value of a localized field in a quest. Equivalent to GetLocalizedItemField() |
SetLocalizedQuestField() | Set the value of a localized field in a quest. Equivalent to SetLocalizedItemField() |
GetLocalizedLocationField() | Get the value of a localized field in a location |
SetLocalizedLocationField() | Set the value of a localized field in a location |
For example:
The PixelCrushers.DialogueSystem.Lua class provides lower-level access to the Lua environment through these functions:
Method | Description | Example |
---|---|---|
Run() | Run Lua code and return a Lua.Result structure | int nextYear = Lua.Run("return Actor['Player'].Age + 1").AsInt; |
IsTrue() | Run Lua code and return true if the Lua result is true, otherwise false | if (Lua.IsTrue("Actor['Player'].Age >= 21")) {...} |
RegisterFunction() | Register a C# method as a Lua function | Lua.RegisterFunction("Exists", null, typeof(MyClass).GetMethod("Exists")); (*see below) |
Remember that Lua.Run()
returns a Lua.Result
that can be a Boolean, string, number, or table. To get the Boolean value, you need to add ".AsBool", as in this example:
Similarly, if your variable is a number, you can do this:
Since Lua.IsTrue()
always returns a Boolean value (true
or false
), you don't need to add as
XXX to the end:
(Video Tutorial: How to Connect Your Own Code to Lua)
The RegisterFunction()
method lets you tie your own C# code into the Lua environment. This is extremely handy to add new capabilities to your dialogue entry Conditions and Scripts. Use the corresponding UnregisterFunction()
method to unregister the C# method from Lua.
You can also add these functions to the "..." Lua dropdown wizards. See: Custom Lua Function Info For Dropdown Wizards.
For example, say you want a dialogue entry to be available only when a certain GameObject named "White Elephant" is in the scene. You want to be able to set the dialogue entry's Conditions field to this:
GameObjectExists("White Elephant")
Simply add this class to your scene:
Note that we passed an empty string to GameObjectExists in the OnEnable method. This is how we specify what parameters the Lua function expects. The valid parameter types are listed in Data Types Allowed in Lua Functions.
If you're writing your own Lua functions, the best way to start is to use the Starter Template Script for Custom Lua Functions.
IMPORTANT: C# methods registered with Lua must use double
for numbers, not float
or int
.
Only use these types in your Lua functions' parameters and return values:
Type | Notes |
---|---|
double | LuaInterpreter uses doubles for all number types; you can cast to (int), (float), etc inside your function if necessary |
bool | Use as normal |
string | Use as normal |
Important Notes:
double
. In your C# methods, use double
instead of float
or int
.You can find a starter template script in Templates ► TemplateCustomLua.cs. To add your own Lua functions, make a copy of this template and customize it as indicated by the comments in the script.
To make your custom Lua functions accessible in the "..." dropdown wizards (such as Conditions and Script fields), create a Custom Lua Function Info asset. Right-click in the Project view and select Create → Pixel Crushers → Dialogue System → Custom Lua Function Info.
If you want a function to appear in the Conditions wizard, add it to the Condition Functions section. If you want it to appear in the Script wizard, add it to the Script Functions section.
For each function, specify the function name and the number and types of parameters. For Condition Functions, also specify the return value type.
In some cases, you may want to be notified when the value of a Lua variable changes.
To monitor a Lua variable:
For example, to monitor a variable named "gold":
Note: If the same value is assigned to a variable (i.e., the value hasn't changed), the VariableChanged event will not be invoked.
Tip: If you want to monitor all variables in your dialogue database (which is probably overkill in most situations):
Using Language.Lua.Assignment.MonitoredVariables is very efficient. It has virtually no performance impact and will raise the C# event as soon as the variable changes.
If you want to monitor other Lua info, the DialogueManager class lets you manage observers on Lua expressions using these methods:
Method | Description | Example |
---|---|---|
DialogueManager.AddLuaObserver() | Add an observer on a Lua expression | DialogueManager.AddLuaObserver("Variable['Credits']", LuaWatchFrequency.EveryDialogueEntry, OnCreditsChanged); |
DialogueManager.RemoveLuaObserver() | Remove an observer | DialogueManager.RemoveLuaObserver("Variable['Credits']"); |
DialogueManager.RemoveAllObservers() | Remove all observers | DialogueManager.RemoveAllObservers(); |
AddLuaObserver() adds an observer on a Lua expression that will be checked on a specified frequency. The frequency can be EveryUpdate, EveryDialogueEntry, or EndOfConversation. If the expression changes, the Dialogue System will invoke a delegate that takes the form:
Example:
Note: For best performance, limit the number of observers you set, especially when the frequency is EveryUpdate. Each observer requires an extra Lua call to evaluate the current state of the Lua expression.
RemoveLuaObserver() removes an observer. The observer is identified by the Lua expression, which should exactly match the same string that you passed to AddLuaObserver()
. RemoveAllLuaObservers() removes all observers.
The Lua Console component adds an in-game console in which you can run Lua expressions. By default, the console is mapped to ~+L but you can change the key binding.
The Dialogue System uses a customized, public domain fork of LuaInterpreter that does not implement the following functions:
The Dialogue System provides a PixelCrushers.DialogueSystem.Lua wrapper class which isolates the actual Lua implementation from the rest of the Dialogue System. If you want to use a different Lua implementation, you only need to replace these classes:
Alternative support is built-in for NLua. (See below.)
The Dialogue System also supports using NLua. To use NLua:
<< Welcome to the Dialogue System for Unity! | Cutscene Sequences >>