Dialogue scripting language for Unreal Engine

Related tags

Game Supertalk
Overview

Supertalk

Welcome to Supertalk! This is a simple dialogue scripting language for Unreal Engine created due to frustration with visual dialogue tree workflows.

The plugin is made up of two parts: an editor module and a runtime module. The editor module handles importing supertalk scripts (.sts files) into a form that the engine can use. The runtime module includes a supertalk "player" which handles playback for imported scripts.

Supertalk is currently configured for Unreal Engine 5.0, but can easily be made to work with earlier versions by replacing its usages of TObjectPtr.

Supertalk is not a ready-to-use solution for dialogue systems! It requires a good amount of integration work and comes with no UI. While it does integrate well with blueprint, initial integration with a game requires knowledge of C++ and no support will be offered beyond this documentation.

Known Issues

  • Currently there is no syntax to assign namespaces/keys for dialogue text for i18n. This is planned for some point in the future.
  • Recursive calls into USupertalkPlayer::PlayScript are not currently allowed, though they should technically be possible. This somewhat limits how dynamic scripts can be (you can't change what section is playing based on external data) but this will be fixed in the future.
  • The way the supertalk parser/importer emits errors is a bit odd, haven't quite figured out how to deal with the message log the right way. It generally works fine but might not be implemented the "right" way.

Scripting Syntax + Features

Scripts have the extension .sts and can be imported once this plugin is enabled simply by dragging one into the content browser. Alternatively, you may turn on auto-import which is the more ideal workflow as it will automatically re-import changes made to the script files.

-- Comments are preceded by two dashes

-- Very simple variables can be used. You can reference assets within a script which can be used
-- to define who is speaking a line of dialogue, or if they implement the ISupertalkDisplayInterface
-- they can be embedded within lines of dialogue themselves.
-- In my own game, I have a "speaker" asset that would be used here. Speakers define a character's name,
-- "voice" if there's audio associated with them, and portrait.
Person1 = /Game/MyGame/Characters/Person1
Person2 = /Game/MyGame/Characters/Person2

-- This will play a few lines of dialogue.
Person1: Hello, world!

-- You can use FString::Format syntax to reference variables.
Person2: Hello, {Person2}!

-- Data tables with a row type of FSupertalkTableRow can be used to share textual data across scripts.
-- Rows can be accessed via a "." - types other than data tables may also support this syntax, see the
-- integration guide for details.
MyDataTable = /Game/MyGame/Data/SomeDataTable

Person2: My datatable says {MyDataTable.Key1} and {MyDataTable.Key2}!

-- You can pass just a string as a speaker's name
"Some third person": I'm not predefined in a variable!

-- If you don't have any special characters, you don't need to quote the name (though you may still want to
-- in order to differentiate it from an actual variable).
Person3: I will appear as "Person3".

-- You can pass an alternate name for a predefined speaker
Person1,"An alternate name": I'm still {Person1} but with a different name.

-- You can pass along attributes with a line - each attribute, separated by a comma, is passed as an FName
-- as part of the line of dialogue. I use this to define a position for character portraits to appear, along with
-- which portrait should be displayed.
Person2 [Left, Sad]: I'm sad now :(

-- You can add linebreaks freely without breaking up the dialogue lines after the first one as long as they are
-- indented. Line breaks will only be added to the final output if you have an empty line (a la markdown).
Person1: This sentence will span
         multiple lines in the script
         but will appear as a single
         one when played.

         This will appear as a second
         line to go along with the first.

-- Supertalk supports calling events on blueprints and UFUNCTIONs on C++ UObjects. See the integration guide for an
-- example of how to do this. Supertalk even supports latent actions here (events that take time) and will wait on them
-- if they have been configured appropriately.
> Wait 10
> DoSomethingCool

-- In some cases you may want to do multiple things at a time. For example, display a line of dialogue at the same time
-- that you have a character walk to a new position. You can do this with a "parallel" block. Note that this is not truly
-- parallel in the sense of threading - this simply runs all statements inside it in order *and then* waits for all of them
-- to complete if any are latent.
{
    > WalkToNewPosition
    Person2: I need to say something while someone walks to a new position.
}

-- Supertalk supports passing along a list of choices with a dialogue line. You can then define what happens with each choice.
-- By default you may only pass along a single statement to execute with a choice - if you want to pass multiple you can use
-- parenthesis (*not* curly braces - that would be a parallel block instead) to group multiple statements together.
Person1: Do you like cake or pie?
* Cake
  Person1: You chose cake!
* Pie
  (
      Person1: You chose pie!
      > DoSomethingCool
  )

-- Depending on the game you might find it useful for emit "blank" lines to your integration. This syntax will cause
-- FSupertalkLine::bIsBlankLine to be set to true. You can specify attributes here as well. The primary use-case is, for example,
-- to setup multiple character portraits without requiring them to actually speak.
Person1 [Sad];
Person2 [Happy];

-- Supertalk supports having multiple "sections" in a single script. Only the first section will execute by default, but you can
-- either include a jump (using an arrow ->) or from C++ tell the supertalk player to run an alternate section. Jumps are normal
-- statements like commands or dialogue lines and as such can be used pretty much anywhere.
-- Note that there is *not* any fall-through from one section to another. If you reach the end of a section and there is no jump,
-- the next section will *not* play automatically.
-- The first section of a script will *always* play by default and if you don't explicitly give it a name it'll be named "Default".

-> Section2

# Section2

Person2: Which section do you want to go to?
* 3
  -> Section3
* 4
  -> Section4

# Section3

Person2: I'm in section 3!
-> Section5

# Section4

Person2: I'm in section 4!
-> Section5

# Section5

Person1: That's the end of the supertalk script overview. Goodbye for now!

-- You can end a script's execution either by having no more statements in a section or by jumping to 'None'
-> None

VSCode Syntax Highlighting

A basic syntax highlighting extension for Visual Studio Code can be found in the Extras directory.

image

Integration Guide

This is an overview of the work necessary to integrate Supertalk into a project.

Major Types

All of these types are in the Supertalk module - generally you will not have to use anything in SupertalkEditor as it exists purely to provide the editor with an importer for supertalk script files.

USupertalkPlayer

This is your primary interaction point with Supertalk and implements almost all of the major functionality. It is a very simple "VM" that executes supertalk scripts.

Each Supertalk player contains its own state of execution and list of variables. You can get and set variables on it manually as well.

If you want to expose variables from your game without manually setting variables, you can subclass USupertalkPlayer and implement USupertalkPlayer::GetExternalVariable which will be called if a variable cannot be found.

USupertalkScript

This is the main asset type for Supertalk.

FSupertalkLine

Dialogue lines are emitted via this struct. It contains a value representing who is speaking the line, a value for a name override for the speaker, a list of attributes applied to the line, and the text itself.

Generally you'll want to call FSupertalkLine::FormatText to get the text for display, as this will replace any variable placeholders in the line with data from the Supertalk player.

USupertalkValue

Base class for a "value" which can be anything - an object, text, datatable, etc.

ISupertalkDisplayInterface

Implement on custom UObjects to provide a way to override how Supertalk will display those objects in dialogue lines. You can also override ISupertalkDisplayInterface::GetSupertalkMember in order to provide a way to get sub-values of an object.

FSupertalkTableRow

Any data table used within Supertalk should use this row type, which allows storing text that can be reused in multiple scripts.

FSupertalkLatentFunctionFinalizer

Supertalk uses this object as a way to tell when latent functions are being called. Calling FSupertalkLatentFunctionFinalizer::Complete() will tell the Supertalk player that the action has completed and it may continue. These are also usable in blueprint (to allow for calling latent actions in events) - a blueprint event being called from Supertalk should call USupertalkPlayer::MakeLatentFunction to receive a finalizer and then call USupertalkPlayer::CompleteFunction when it has finished.

If an event is not latent (i.e. it doesn't take up any time) then it should not call MakeLatentFunction at all.

Integrating the Supertalk Player

The Supertalk player is your primary entrypoint into Supertalk - it represents the "state" of a script. You can have any number of them at a time and they can share scripts, but each one maintains its own state completely separate from the others.

You can create a Supertalk player with NewObject<USupertalkPlayer>(). Once created, you can bind events to handle when dialogue lines (or set of choices) are played, and you can register handlers to receive

// In some function somewhere - maybe the initialization for a cutscene class.
USupertalkPlayer* STPlayer = NewObject<USupertalkPlayer>(this);
STPlayer->OnPlayLineEvent.BindUObject(this, &ThisClass::OnPlayLine);
STPlayer->OnPlayChoiceEvent.BindUObject(this, &ThisClass::OnPlayChoice);

// If you want to be able to call functions on the current object (or some other object) from within a script, you need to add a function call receiver.
// Any public UFUNCTION (or blueprint event or function) will be callable, and the syntax for calling a command is the same as the "ke" console command.
STPlayer->AddFunctionCallReceiver(this);

OnPlayLineEvent and OnPlayChoiceEvent are important to implement or else you will not find out when a dialogue line/choice has been played. When you are done playing the dialogue line/choice (which generally entails showing it on-screen and then waiting for user input to continue) you should call the Completed delegate.

For OnPlayChoiceEvent you must also pass an integer representing the index of the selected choice. If an invalid choice (or no choice, if that's allowed) is selected then you should pass INDEX_NONE as the index. Note that this will result in no choice statements being executed - the Supertalk player will simply continue to the next statement after the current set of choices.

VM Internals

The Supertalk parser is only used for asset importing and as such is editor-only. It is a very simple handwritten lexer and parser combo - The primary entrypoint is USupertalkParser::Parse which calls into both the lexer (USupertalkParser::RunLexer) and the parser (USupertalkParser::RunParser). Lexer functions are prefixed by Lx while parser functions are prefixed by Pa. The result of the parser is a USupertalkScript object which can be saved to disk as an asset.

The Supertalk VM/Player is incredibly simple - a script asset is effectively just the AST of the original supertalk script with a little bit of extra processing done on it. The list of possible action types can be found in ESupertalkOperation, while actions themselves are implemented in USupertalkPlayer. Each action can also have additional parameters, implemented by subclassing USupertalkOperationParams. Actions themselves are represented by FSupertalkAction.

The VM contains a list of stacks (FSupertalkStack). Each stack contains a list of actions to be executed and a single active action. Each stack is assigned an id, as is each action. Ids are used throughout the VM to retrieve stacks and to validate that an operation is happening on the correct stack/action. When a script is played the VM will start with a single stack and it will queue all the actions for the initial section of that script.

When a latent action is executed (either due to a dialogue line/choice executing or due to MakeLatentFunction being called) the current stack is paused. When the action is completed (via a Completed delegate call or via FSupertalkLatentFunctionFinalizer::Complete()) the stack is notified and continues.

When a parallel block is encountered a set of new stacks is created - one for each action (or group of actions) inside the block. These stacks are assigned unique ids and the original stack stores these ids in a waiting list (FSupertalkStack::WaitingOn) and pauses itself. When a stack completes (usually due to running out of actions to execute) it removes itself from the original stack's waiting list. Once that waiting list is empty the original stack will resume execution.

Execution of queue blocks (lists of actions to be executed sequentially inside parenthesis) is not given any special handling - actions are simply added to the current stack in the appropriate order.

Section jumps cause the current stack to be emptied and then refilled from the given section. The one case where this doesn't happen is upon a section jump to None, in which case the stack is simply emptied - thus ending that stack's execution.

You might also like...
An Unreal Engine 4 Dungeon Generator
An Unreal Engine 4 Dungeon Generator

DungeonGenerator A (hopefully) handy Unreal Engine 4 Dungeon Generator Read the full docs here: https://github.com/orfeasel/DungeonGenerator/blob/main

Procedural Mesh Modeling Toolkit for Unreal Engine Artists
Procedural Mesh Modeling Toolkit for Unreal Engine Artists

OpenLand Mesh Procedural Mesh Modeling Toolkit for Unreal Engine Artists. Installation Get it via the marketplace 🛍️ For non-commercial projects, you

Creating Unreal Engine infinite landscapes/oceans using the editor shader graph and rendering them using Geometry ClipMap. It also allows to spawn mesh on landscape surface. UE5 required
Creating Unreal Engine infinite landscapes/oceans using the editor shader graph and rendering them using Geometry ClipMap. It also allows to spawn mesh on landscape surface. UE5 required

Procedural Landscapes and Oceans in Unreal Engine 5 using Editor Shader Graph Latest version of this project is available as a plugin for UE 4.26+ on

Unreal Engine Plugin to wrap Nuitrack SDK ( skeleton tracking solution by 3DiVi )
Unreal Engine Plugin to wrap Nuitrack SDK ( skeleton tracking solution by 3DiVi )

Nuitrack for Unreal Engine Unreal Engine plugin to bridge Nuitrack. Nuitrack is a middleware to provide 3d skeleton tracking solution using a depth se

Unreal Engine 4 plugin for SteamVR passthrough camera support

SteamVR Passthrough Plugin This Unreal Engine 4 plugin adds SteamVR passthrough camera support using the OpenVR TrackedCamera API. Example project: ht

HLSL Material for Unreal Engine

HLSL Material for Unreal Engine Ever wanted to write complex material functions directly in HLSL? Now you can! Unreal Engine 4.26, 4.27 and 5.0 are su

Building Escape is a simple room escape game made with Unreal Engine 4.27 and C++.
Building Escape is a simple room escape game made with Unreal Engine 4.27 and C++.

Building-Escape Building Escape is a simple room escape game made with Unreal Engine and C++. The main purpose of the game is to find a way to escape

An Unreal Engine 4 SDK generator using SdkGenny

UE4Genny UE4Genny is an SDK generator for Unreal Engine 4 games. It aims to provide a functional SDK that requires little to no editing after generati

Unreal Engine OpenDRIVE plugin
Unreal Engine OpenDRIVE plugin

Unreal Engine OpenDRIVE plugin This plugin allows you to manipulate your OpenDRIVE road network in Unreal Engine. It is built around esmini's RoadMana

Comments
  • Minor integration and syntax clarification

    Minor integration and syntax clarification

    Not sure if you find this useful, but these are just some things that were not immediately apparent to me when starting out with Supertalk.

    P.S. Thank you for this repository, it's been a wonderful help.

    opened by mshvern 0
Releases(v0.6.0)
  • v0.6.0(Nov 6, 2022)

    • Supertalk source data is now stored inside assets (editor-only)
    • Supertalk assets now show the original file content.
      • Assets from previous versions must be reimported for this to work (this only affects the ability to view source data, old script assets will still work fine)
    • An experimental editor for Supertalk assets has been implemented.
      • This must be enabled in Project Settings > Editor > Supertalk Editor. Restart the editor after changing this setting.
      • This also lets you create Supertalk scripts within the editor without having to import from an external file.
      • The editor is somewhat untested and due to how it works it is possible to lose script data. Be careful when enabling this option!
      • Old script assets must be reimported before they can be edited from within the editor.
    • Script assets can now be exported to text files if they were imported after this version.
    • Script assets are now compiled upon saving (incl. when packaging).
      • Old assets that don't have source data available are an exception to this.
      • This means that referencing a script that fails to compile will fail packaging.
    Source code(tar.gz)
    Source code(zip)
  • v0.5.0(Aug 29, 2022)

    • Property-based variable providers
      • Supports passing any UObject to the Supertalk player and accessing properties on it from scripts.
    • USupertalkObjectValue now lets you access exposed properties.
      • Initial TMap support (only for maps with FString/FName/FText keys)
    • Added an editor-only "notes" field to FSupertalkTableRow
    Source code(tar.gz)
    Source code(zip)
Owner
Sam Bloomberg
Software Engineer at 343 Industries / MS Studios Quality
Sam Bloomberg
A scripting language created mainly for game engines

HCode Hammurabi's Code A scripting language created for the use in games and game engines. Currently supported features and roadmap Structs Functions

null 4 Oct 11, 2020
Mobile Studio tool integration with C# scripting for the Unity game engine.

Mobile Studio Package This project is a Unity package for integrating the Mobile Studio tool suite into game development workflows. This version of th

Arm Software 76 Dec 7, 2022
python scripting engine for the gd editor

Python interpreter embedded into the game Geometry Dash, designed for helping people automate tasks for creating

camila 8 Feb 24, 2022
Full source code for WarriOrb, a Dark-Souls like action platformer - using Unreal Engine 4

WarriOrb source code WarriOrb is a hardcore action platformer where you play as a demon who is trapped in an unlikely body. The game mixes the difficu

Not Yet 247 Dec 30, 2022
Niagara UI Renderer | Free Plugin for Unreal Engine 4

Niagara UI Renderer | Free Plugin for Unreal Engine 4 Niagara UI Plugin adds Niagara Particle System Widget that allows you to render Niagara particle

null 156 Dec 19, 2022
Third-person survival game for Unreal Engine 4 made entirely in C++.

Third-person survival game for Unreal Engine 4 made entirely in C++. Originally built as a 6 section tutorial series, now available as open-source C++ sample project.

Tom Looman 2.8k Dec 30, 2022
RenderStream plugin for Unreal Engine

This project relies on http://disguise.one software to function. For the plugin setup process - please see https://help.disguise.one/Content/Configuri

disguise 41 Dec 19, 2022
Simple CSV localization system for Unreal Engine 4

BYG Localization We wanted to support fan localization for Industries of Titan and found that Unreal's built-in localization system was not exactly wh

Brace Yourself Games 56 Dec 8, 2022
Edycja PianoFall zrobiona na Unreal Engine

PianoFall - Unreal Engine Edition Edycja PianoFall zrobiona na Unreal Engine (mój pierwszy projekt w UE) Obsługa Po uruchomieniu programu i wciśnięciu

Nadwey 4 Jun 17, 2021
Lambda support for Unreal Engine dynamic delegates

DynamicLambda Lambda support for Unreal Engine dynamic delegates This is experimental feature. Now only parametless lambdas are supported To see more

Andrew Derkach 28 Nov 29, 2022