This page contains an assortment of solutions developed for and by other Dialogue System users.
You can find many more free extras at: http://www.pixelcrushers.com/dialogue-system-extras/
You will also find more solution on the Pixel Crushers forum: http://pixelcrushers.com/phpbb/
In the Dialogue Editor, you can add event handlers to a UnityEvent on dialogue entries called OnExecute(). It works similarly to adding an event handler to a Unity UI Button's OnClick() event.
However, since the dialogue database exists independently of whatever scene is open, you can't assign a scene object to the event handler. Instead, you must assign an asset file from your project, such as a ScriptableObject asset. (For more information on creating ScriptableObjects, see Unity's tutorial: https://unity3d.com/learn/tutorials/modules/beginner/live-training-archive/scriptable-objects )
In the example below, we'll create a ScriptableObject asset that provides a method to play audio clips. Then we'll assign that method to a dialogue entry's OnExecute() event.
First, create a new C# script named TestScriptableObject
containing this code:
To keep the example short and clear, the code doesn't do any error checking.
Next, in the Project view select Create > TestScriptableObject. This will create an asset file from the script:
Inspect your new asset file. You'll be able to assign audio clips to it:
Finally, inspect a dialogue entry's OnExecute() event, click the "+" button, and assign TestScriptableObject. Select the method you want to execute (PlayAudioClip) and specify the index of the audio clip that you want to play.
In the example above, the dialogue entry plays the audio clip "Bonecrush".
This is a contrived example since you can easily use sequencer commands to accomplish the same thing. The purpose of this example, however, is to demonstrate how to expose your own code to the OnExecute() event. Your code could just as easily, for example, use GameObject.Find() to locate a GameObject in the current scene and perform operations on it.
See the Tutorial Example for an example of how to write a tutorial conversation. In brief, create a conversation that uses sequencer commands with the @Message(message) syntax to wait for messages. As the player completes gameplay objectives in the tutorial, call Sequencer.Message(
message)
to notify the waiting sequencer command.
You'll often want to disable player control during regular conversations but leave them enabled during tutorial conversations. To do this, add a Condition to your Set Component Enabled On Dialogue Event (or equivalent) component. Here are some example steps:
This way it will only disable your player movement components if the Conversant is NOT "Tutorial".
To add extra handling, such as a voiceover (VO), to alerts, add a Lua observer to Variable["Alert"]
:
In OnAlertChanged(), you can check the value of Variable['Alert']
and play the right VO based on its value, or you can set an additional Variable[]
element with, say, the filename of the audio clip to play. For example, if you set Variable["AudioClip"]
:
If you use the value of Variable['Alert']
, you'll only be notified if the value has changed between the end of one conversation and the end of another. If the value doesn't change – for example, if you set Alert to the same value again – you won't get another notification.
The first parameter passed to your observer is a LuaWatchItem:
After Variable["Alert"]
has been cleared by the Dialogue System's automatic end-of-conversation processing, you can call luaWatchItem.Check()
to tell the observer to manually re-check the variable, which will update the observer's cached value to a blank string. One way is by using a coroutine:
You could also run the message as a bark instead of an alert by using BarkOnDialogueEvent set to trigger OnConversationEnd. In the bark dialogue entry's sequence, you can use the Audio() sequencer command.
If you want to perform additional checks on dialogue entries as a conversation progresses, you can use an IsDialogueEntryValid Delegate.
For example, conversations can involve other NPCs in addition to the main actor and conversant. You may want to verify that the other NPCs are close enough to the PC before allowing the line to be spoken.
If you haven't stored this information in the Dialogue System's Lua environment, it won't be available to the dialogue entry's Condition script. In this case, set a delegate that checks the proximity of the entry's speaker to the player.
The Dialogue System registers a function RandomElement(string) with Lua. This returns a random element in a string of elements separated by horizontal bar characters (|).
You can add the LuaGetElement.cs script below to the Dialogue Manager to register a new function GetElement(index, string). This function returns the element at the specified index, which starts at 1.
For example, the Dialogue Text below will display "Such a lovely toddler!"
Such a lovely [lua(GetElement(2, "baby|toddler|teenager"))]!
The script below (WatchLuaValue.cs) logs a message to the console whenever a specified Lua expression changes. You can use it to watch the values of variables, quest states, etc. To use it, add it to the Dialogue Manager object and set Lua Expression. The default frequency is EveryUpdate, which may cause a some overhead depending on the complexity of your Lua expression. If you only need to check the value every dialogue entry or at the end of conversations, change the frequency.
The Cancel trigger (key and/or button) configured in the Dialogue Manager always cancels the current subtitle.
In some cases, you might want to make a subtitle absolutely mandatory to sit through. For example, say you want to force an audio clip to play to completion in a particular dialogue entry, whereas the player is allowed to skip audio in all other dialogue entries.
To do this, you'll have to temporarily disable the cancel key. A relatively easy solution is to use a custom sequencer command.
Save this in your project as SequencerCommandSetCancelKey.cs:
Then in your sequence use these commands:
SetCancelKey(false); AudioWait(myClip)->Message(Done); SetCancelKey(true)@Message(Done)
This sequence:
The Dialogue System provides several options for integrating HUD systems with the Quest System. In addition to the methods described in the Quest System, you can use these techniques to run external code when quests change:
Accepting Quests:
Players usually accept quests in conversations. This is described in Working with Quests in Conversations and, in this case, you either use the User Script field or the Sequence field.
The User Script field runs Lua statements. To use it, you need to register your C# or UnityScript function with the Lua environment. You can examine Scripts/Supplemental/Aurora/TemplateNWScript.cs
for an example that registers a function named "NWScript()" with the Lua environment.
The Sequence field runs sequencer commands. It's very easy to add your own How to Write Custom Sequencer Commands. You can add a Sequencer command that runs your external function.
If the player can accept quests outside of conversations, you can add a Quest State Observer that will call a function if the quest state changes. See: Setting Quest State Observers
Abandoning Quests:
If you're using the built-in quest log window system, the player can use the quest log window to abandon quests (if the quest is marked as Abandonable). When a quest is abandoned, it sends an OnQuestTrackingDisabled() message to the Dialogue Manager GameObject. To see an example of how this message is used, examine Scripts/Supplemental/QuestTracker.cs
. If the player has other ways to abandon a quest, add a Quest State Observer.
If you're using KGFMapSystem, it already includes HUD integration in the form of KGFMapIcon().
Unity GUI doesn't provide an easy way to scale fonts to the current resolution. You can attach this script to your GUIRoot to handle scaling. Add each style whose font you want to scale to the Styles list.
The following subclass of DiakonForgeDialogueUI allows you to add a list of dfTweens to your NPC subtitle and response menu panels. For example, you could add tweens that grow the panel when showing it (the intro), and shrink the panel away when hiding it (the outro).
This recipe shows how to add a custom table to the Lua environment and include it in saved game data.
Say your player has an inventory, and you want to keep track of which item is in each slot. You can define a Lua table named Inventory[]
in your dialogue database's Global User Script field, available on the Dialogue Editor's Database tab:
Inventory = {}
For the sake of this example, say it has three fields: Slot1, Slot2, and Slot3. Each field will point to an element in the Item[]
table. For example, you could assign a sword item to Slot1:
Unfortunately for our example, the Save/Load System only records specific content in the Lua environment. This keeps saved-game data small.
However, we can add a special method called a delegate to record extra data. Write a method that returns the Lua code necessary to repopulate the Inventory[]
table when loading a game:
This might, for example, return a string such as:
To tell the PersistentDataManager to use this method, assign it as the GetCustomSaveData delegate:
Now, whenever the PersistentDataManager gathers data to save a game, it will also append the string returned by GetInventorySaveData().
This recipe contains one approach to handle switching between multiple PCs using the Dialogue System.
Write a conversation titled "Select Character":
[0] Player: START [1] Game: Select your character. [2] Player: Dave [END] [3] Player: Syd [END] [4] Player: Wendy [END]
How you start this conversation depends on your game design. You could start it from a menu by using a PlayMaker action or calling DialogueManager.StartConversation()
in a script. Or perhaps it starts when the player walks into a "changing room," using a Conversation Trigger set to OnTriggerEnter.
Use sequencer commands in dialogue entries 2-4 above to change the active PC. How you change the active PC again depends on how PCs work in your game. For this example, let's say that every PC has two sets of scripts:
You can use the SetEnabled() sequencer command to enable some scripts and disable others.
Or, you can use the SendMessage() command to call a method on a script. (Maybe you have a SwitchCharacter script.)
Or, you can write a custom sequencer command that handles the switch.
Let's say you decide to write a CharacterSwitcher script and put it on a dedicated GameObject named CharacterSwitcher, something like this:
public class CharacterSwitcher : MonoBehaviour { public GameObject[] characters; // Assign all PCs to this array. public void SwitchTo(string characterName) { foreach (var character in characters) { character.SendMessage("OnPlayerControl", string.Equals(character.name, characterName)); } } }
Then use this sequencer command on Dialogue Entry 2 (Dave):
SendMessage(SwitchTo, Dave, CharacterSwitcher)
This will end up sending "OnPlayerControl(true)" to Dave, and "OnPlayerControl(false)" to Syd and Wendy.
Now the PCs need to do something when they receive "OnPlayerControl". Maybe another script ControlSwitcher on each PC, such as:
public class ControlSwitcher : MonoBehaviour { public MonoBehavior[] playerControlScripts; // Assign player control scripts. public MonoBehavior[] aiControlScripts; // Assign AI control scripts. public void OnPlayerControl(bool isPlayerControl) { foreach (var playerControlScript in playerControlScripts) { playerControlScript.enabled = isPlayerControl; } foreach (var aiControlScript in iaControlScripts) { aiControlScript.enabled = !isPlayerControl; } } }
UnityBarkUIOnGUI was made to be extensible by creating a subclass. By default, barks appear immediately and fade out. Put this subclass (UnityBarkUIOnGUIFadeIn.cs) on your barker if you want barks to also fade in. If your barker already has a UnityBarkUIOnGUI component, remove that. If it doesn't have one, you don't need to do anything extra.