Click or drag to resize

Text Extensions

Here you will learn how you can use the ArticyImporter advanced text processing, which allows you to use variables, object property and more inside your texts.

Primer to the articy text extension

To use the text extensions you just need to add special text fragments inside your strings called Tokens. Your string can have as many tokens as you want and you can even nest them. It doesn't matter where you write the text, like directly in articy:draft and export them to unity; inside your localization excel files or directly in your unity scripts. The syntax is always the same. Almost all strings on your articy:draft objects are capable of utilizing the text extensions (TechnicalName is a noteworthy exception).

Lets look at a simple example inside articy:draft

global variables example

On the left side you see a view with the namespace Session and the property PlayerName. The intent could be to allow the players to change their name at runtime.

On the right side you see the flow with a single DialogueFragment and the spoken text Hello, [Session.PlayerName] which is a mixture of regular text and a token identifiable by the brackets.

When shown in-game, the text would look like this: Hello, Unknown Player

Note Note

If you want to use brackets in your literal text, you need to escape them using a backslash before the bracket.

For example: When you write He continued: "\[...\] it wasn't me! it turns into He continued: "[...] it wasn't me!" otherwise the text extension would try to resolve the part inside the brackets. Remember to escape the backslash if you write it directly inside your c# code in literal strings or use a verbatim string.

In the next sections you see how you can write your texts using tokens; how to format them and even how to add your own processing.

Token definition

A token is a specialized placeholder string surrounded by single brackets telling the text resolver what kind of information you want to display.

Tokens always follow this form inside your strings:

[(Source)(:Formatting)]

The Formatting itself is optional and is used after the source followed by a colon. Later you will find examples on how you can use the formatting itself but first lets look at the options available for Source.

Token Sources

The text extension is capable of gathering informations from many different places using the plugins core features like the database, the type system and localization. But before we look at all the different sources, it makes sense to check how the different data types are resolved and placed into your texts

Strings

Strings are used as is, and their value is just inserted into the target text, but there are some exceptions to this rule:

  • Object representation: If the string contains data about an object, it will insert its localized DisplayName.
  • Localization Key: If the string is a valid localization key, the string will first be localized using the current language before the text is inserted.

Numbers

Numbers are converted to strings and you have a lot of flexibility how with the optional formatting which we see later.

Boolean

Boolean is usually not very helpful for texts, but if you want you can use it. It will insert literal true or false into your text. There is the option to use localization to insert a different text for the boolean value, depending on its source. If you create a loca key (Source).True or (Source).False, it will display the localized value instead of true or false. Additionally you can create the loca keys VariableConstants.Boolean.True and VariableConstants.Boolean.False, which will be always used if no other loca key exists for a boolean value.

Enums

The enum values DisplayName is used, see the formatting for accessing its numeric value.

Global variables

To use global variables inside your scripts you just need to use the namespace and variable name.

[(Namespace).(Variable)]

Examples:

"Hello [Session.PlayerName], you still only have [Inventory.goldCount]g left."

Objects

You can easily access objects and their properties and templates. The general structure when accessing an object is:

[(ID/TechnicalName)<(instanceId)>.(Properties)...]

Depending on the text owner object, you also have access to 2 convenience identifier to access an object. $Self and $Speaker. The later only works on DialogueFragments but both are object references and can be used accordingly.

Examples:

"[Chr_Manfred.DisplayName]"
Here we use the TechnicalName followed by the property name. In the case of DisplayName you can even omit the property altogether, as the object fallback is always back to DisplayName, as mentioned before.

"[Chr_Manfred.Character.BackgroundStory]"
You can also directly access its template by using the feature and property TechnicalName.

"[0x01000001000010C6.Text]"
Its perfectly valid to use the Id instead of the TechnicalName.

"Sorry, but i only have [$Speaker.Inventory.Gold]g left."
This is an example of accessing the Speaker of a DialogueFragment directly.

"[MainCharacter.Character.Companion.Character.HP]"
In this example Companion would be a reference slot containing another Character Entity where we access its template to print the HP value.

"[Guard<15>.NPC.HP]"
Sometimes you have multiple instances of the same entity when using Id or TechnicalName. In this case you can supply its instance id using angle brackets, otherwise it always grabs the first instance.

"I don't talk to a filthy [[DialogueState.Counterpart].Basic.Class]"
One advanced use case is to use the object string representation to access its fields. In this example, DialogueState.Counterpart is a string global variable, which stores an object representation of our current opposite person inside an exemplary dialogue system. Because the string variable can be interpreted as an object, you can directly access its template and property.

"My first item in my Inventory is [Chr_Main.Inventory.ReferenceStrip<0>.DisplayName]"
You can even access reference strips from your templates inside your texts, by using angle brackets to access a specific index inside your list. If you supply -1 you access the last element; if you supply ? it will randomly select an element from the list.

Type information

You can access meta information about your articy:draft types, utilizing the type system. This can be useful if you want for example to create your UI texts with information from your templates.

To access a type where you know the type or template name, like DialogueLine, FlowFragment or Character.

[$Type.(ClassName).(Properties)...]

or alternatively if you want to access the type of an object instance

[(ObjectSource).$Type.(Properties)...]

Examples:

"[$Type.Character.Attributes.Strength.DisplayName]: [[Session.PlayerCharacter].Attributes.Strength]/([$Type.Character.Attributes.Strength.MaxValue])"
In this example we create a text label in our Character sheet for displaying the players strength attribute. In the first token, we access the type information about our Character template with the Feature Attribute and the property Strength and its meta data DisplayName. The last token is used to access again the type information about Strength but this time, we take the MaxValue. This will show the players current Strength and the maximum Strength. For more information about what constraints are available, see ArticyPropertyConstraint.

Methods

For even more complex use cases you can use methods as a source. A method can take any number of parameters and always returns a string. Methods are mostly for your special use cases and custom sources as you see later, but there are some built-in methods that you can use out of the box:

if and not methods

Sometimes you need to add or change text in a text depending on another token. To do that, you can use the if and not methods. For example:

"Hello, how are you[if([Player.IsWearingPoliceUniform], ", Officer", "")]?"

Notice how the first token calls a method called if, passing in 3 parameters: The first parameter is another token, in this case a global variable, checking if the player is wearing the Police Uniform. The second parameter is the string to insert into the text, when the first parameter resolves to true, so if the player is wearing the police uniform, the text ", Officer" is added to the string. The third parameter is therefore what would be added, when the first parameter is false.

The [not()] method works the same, but switches the second and third parameters.

Scripts

When accessing a script property found on a template, you have 2 ways to use it inside your text. You could either execute the script or using the actual script content inside your scripts

"The script text is: [Something.Feature.ScriptProperty]"

Without brackets, the script property is treated the same as RawScript and returns the script as text.

If you want to execute the script you just add brackets at the end of the script property

"The script value is: [Something.Feature.ScriptProperty()]"

Now the script is executed and, in the case of a condition, a boolean is returned. If the script is an instruction, nothing is returned and nothing is added in its place.

Token Formatting

Utilizing formatting in your tokens allow you to change the appearance of your values. This is especially useful for numbers, where you want to change how to display them. The formatting parameter is used exactly like string formatting works in C# and therefore it is best if you review that in the official documentation. But remember that a lot of those formatting options are using the system locale of the machine running your code.

Examples:

"Highscore: [Session.Score:00000000]"
This will add a zero padding to the given number, even if the score would be 42, it will be shown as Highscore: 00000042.

"[Chr_Player.Combat.HitRating:P]"
This will turn the players HitRating stored as 0-1 number into a percentage value. So a value of 0.42 turns into 42%.

"The enum value of [Chr_Player.Character.ClassEnum.DisplayName] is [Chr_Player.Character.ClassEnum:D]"
The first token will show the name of the players class and the second will display its numeric value like The enum value of Bard is 2.

"The secret code is [Riddles.numbersLock:###-###-###]"
Using hashes you can mask how to display the digits found inside your value. Lets say the numbersLock would be the value 123456789, the final text will be displayed as The secret code is 123-456-789.

ArticyText and ArticyTextComponent

Usually when you deal with text, you want ways to present them to the user via the ui. While it would be easy to assign the text on every update tick to your ui labels text property, this could get expensive in the case of resolving and is not really necessary when the text isn't changed at all. For that reason, you can use the ArticyText object. It will automatically observe all token sources inside your text, listens to changes about any and update the resolved string accordingly. It will fire an event in the case the text was changed in any way, which you can directly register to your UI label for example.

C#
class MyScript : MonoBehaviour
{
    public ArticyText scoreText;

    public void Start()
    {
        // Setting the text will automatically send the event to notify about a new change
        // also everytime the Session.PlayerScore is updated, the text is updated automatically.
        scoreText.Text = "Score: [Session.PlayerScore:00000000]";
    }
}

There is also an component for your convenience that exposes an ArticyText and it allows you to use all of its feature without using code at all.

articy text component
Text extension in code

You are not limited in using the text extension only in conjunction with articy:draft objects. If you want you can create custom strings utilizing the same text extension resolving directly inside your C# code.

To do that you use the static utility class ArticyTextExtension. In it you find all methods used to access the plugins text extension features.

Usage

You can use any of the available Resolve methods to access the text resolver.

C#
string output = ArticyTextExtension.Resolve("Hi, [Chr_Manfred]");
Debug.Log(output); // output: "Hi, Manfred"

An additional feature available when using the ArticyTextExtension class is, to have the option in adding additional parameters. This works almost exactly like String.Format

C#
if(playerGold < Shop.SelectedItem.ShopInfo.Cost) 
{
    string output = ArticyTextExtension.Resolve("Sorry but you still need {0}g to buy this [Shop.SelectedItem].", Shop.SelectedItem.ShopInfo.Cost - playerGold);
    // output: "Sorry but you still need 24g to buy this Sword."
}

Here is a more advanced way when you use parameters inside tokens.

C#
int playerCount = 4;

// Global variables
Session.Player1 = "Bob";
Session.Player2 = "Anna";
Session.Player3 = "Jack";
Session.Player4 = "Sue";

// iterating over all players
for(int i = 0; i < playerCount; i++)
{
    string output = ArticyTextExtension.Resolve("Hello, [Session.Player{0}]", i + 1);
    // ...
}

// Output:
// "Hello, Bob"
// "Hello, Anna"
// "Hello, Jack"
// "Hello, Sue"
As you can see, the parameter is first evaluated, inserting the number into the token, and then the token is evaluated. In fact this works as long as the string needs resolving.

Similar to how you can use parameters to insert into tokens, you can of course also nest tokens to create a similar effect

C#
Session.CurrentPlayerIndex = 2;

string output = ArticyTextExtension.Resolve("[Session.Player[Session.CurrentPlayerIndex]], its your turn!");

// Output:
// "Anna, its your turn!"

Note Note

While difficult to show in examples the text extension always make sure to check if any value needs to be localized before using it in any part of the resolving. If you pass a loca key into the Resolve method it will also first be localized before the actually resolving begins.

Manual resolving for object properties

As mentioned before, under the hood the plugin will automatically resolve the string for all articy:draft object properties like DisplayName, SpokenText etc.. But sometimes it is necessary to get the unresolved string for manual resolving especially when you want to insert parameters. To do that, every resolveable text property, has an unresolved counterpart:

Unresolved_(PropertyName)]

For example DisplayName returns an resolved string, Unresolved_DisplayName returns it unresolved.

C#
var obj = ArticyDatabase.GetObject<Character>("Chr_MainCharacter");
ArticyTextExtension.Resolve(obj, obj.Unresolved_DisplayName);

Custom formatting

The text extension is built to provide the proper logic in regular use cases per default. But we also added ways to further customize what and how you want to build your texts.

Custom methods

If the available sources aren't enough, you can register custom methods to use inside your texts. To do that, you first register your custom method

C#
// define the custom method anywhere in your code (doesn't have to be static, but be wary of memory leaks)
static string GetCurrentTime(ArticyTextToken token, string[] args)
{
    return DateTime.Now.ToString();
}

// Register once to the text extension
ArticyTextExtension.AddUserMethod(GetCurrentTime);
and then you can call that method inside your texts

The current time is [GetCurrentTime()].

You can also add method arguments. These can be literal texts, other tokens and even other method calls.

The current time is [GetCurrentTime(\"now\", [GetCurrentLocale()])].

When you look at the example above, the parameters passed into the GetCurrentTime method can be accessed via the string array.

Custom Token resolving

If you need even greater flexibility in how the text extension resolves your string, you can use the ResolveAdvance method. Note this is an advanced use cases and therefore rather complex in its setup.

Generally speaking, you supply a callback that is called when the plugin resolves your string for each token, gives you information said token and expects a string as a result. This is useful if you want the maximum flexibility when it comes to parsing and creating your strings.

See Also