Arma 3 Scripting

FSM Editor Manual

Topic · Markdown · JSON · original wiki page

FSM stands for Finite State Machine. If this is the first thing you are reading about FSM, please read the FSM article first.

Terminology

Inside this documentation, the following terms are used:

Basic knowledge for designers

There is an information about some warnings and errors every designer should know.

  1. Save as
    • when saving as, you should choose some other extension than *.bifsm to force saving through FSMCompiler (for instance when working with FSMScripted)
  2. Getting the errors/warnings information
    • if there are any errors, saving throu compilation will show them
    • you can use the key F7 (compile) to show the compilation result window
  3. Common Warnings and Errors
    • Warning: State with duplicate name, changing to: xxxxxx_nn
      • every FSM state should have different name. If not, the name will be slightly changed through compilation, which can be checked in the compilation result (fsm scripted for instance)
    • Warning: Condition with duplicate name, changing to: xxxxxx_nn
      • all conditions called from the same state should also have different name
    • Warning: Condition has in degree greater than 1
      • if the same condition is used for more states, the condition and action code will be compiled to more different places in the compilation result. But only one of them can be used during decompilation! So, one cannot freely change the compilation result in text editor (decompilation can indeterministically copy such changes to all places or ignore it at all).
    • Error: Condition has out degree greater than 1
      • impossible, as no one can say, what happens when the condition is true
    • Error: There is an edge between two conditions
      • you cannot concatenate conditions, you should combine their condition to one or insert dummy state in between
  4. Using knees
    • holding ctrl while creating new state/condition, there will be no state/condition, but special little black square item called knee. It's purpose is only to bring possibility for better drawing of FSM. Knees with multiple degrees can yield to warnings and errors as described above.
    • on the picture, there is shown how the knees are eliminated during the compilation. Only the multiple lines are reduced to single one (which was not true before June, 1 2006).

States

Every state can has two editable tabs:

  1. Init code
    • This code is processed when this state is reached.
  2. Precondition
    • This code is processed when conditions are to be tested inside this state, before testing the first condition. (This code is processed every time, the condition starts to be tested, which contrasts to the Init code, which is tested only once the state is reached.)

Conditions are tested in the decreasing order of their priority. This implies that True conditions should have zero priority in order not to shadow other conditions to be tested.

Conditions

Conditions have three editable tabs and priority edit box:

  1. Precondition
    • This code is processed before the condition test.
  2. Condition
    • The condition.
  3. Action
    • This code is processed after the condition was true.
    • Notice: without this feature one should create use condition linking to the state with Init code equal to Action code and one True condition linking to the next state.
  4. Priority is used to define the order to process the conditions. Higher values first.

In FSMScripted, multiline scripts can be used too. The last statement is relevant, as shown in the example:

_vehicle = vehicle _this;
_commander = effectiveCommander _vehicle;
isHidden _commander;

FSM Compiler

FSMCompilers compiles the FSM created (or edited) in FSMEditor into some other format, using compile config.

At present, the following formats and compile configs are possible:

FSM Entity

FSM using FSMEntity functions. This FSM is referred in config, loaded and processed during simulation. This FSM format is currently used for ambient behaviour (butterfly, honeybee, dragonfly).

class CfgNonAIVehicles
{
    class DragonFly : Insect
    {
        model = "dragonfly.p3d";
        flySound[] = { "animals\fly.wss", db-105, 1, 1 };
        fsm[] = { "Dragonfly" };
        straightDistance = 2;
    };
    // ...
}

Thresholds

Each state defines thresholds, which can be used to define which of multiple links from this state is selected. This is done like:

thresholds[] = { { 1, 0, 1 } };

This will store a random value with a minimal value of 0 (second element) and a maximum value of 1 (third element) and store it in slot 1 (first element). Every condition returns a value. This value is compared against the number a defined slot:

threshold = 1;

When the condition value is greater or equal than the value in the slot, this link is selected.

When FSM starts, all threshold slots are initialized to 0.5. Slot value set by one state is then left at that value even for all states to come.

FSM Scripted

FSM using scripted commands and condition. These FSM are not used yet, but were tested on simple FSM (teleport), trigered by radio alpha.

New functions commandFSM and doFSM added:

_unit commandFSM ["FSM filename", destination, target];
_unit doFSM ["FSM filename", destination, target];

in scripts in FSM, following parameters are defined:

to make this application useful, some functions for controlling units on low level needs to be added.

Global Switch FSM

Compilation to the *.cpp code, using switch to navigate through FSM states. The compile uses many FSM attributes, to define function declaration, precondition codes etc. This FSM was first used in seagull.cpp to code autopilot functionality. So, opening seagull.cpp in FSMEditor.

Class Compile FSM

Compilation to the *.cpp, using Fsm class architecture. It creates %(stateName) functions for state initialization and check%(stateName functions for checking conditions. No FSM created by FSMEditor has been compiled and used in engine, but some were coded manually before FSMEditor existence.

How does the FSMCompiler work

We say, that FSMCompiler works as compiler, if it compiles FSM edited in FSMEditor into any other format. And we say, FSMCompiler works as decompiler, if it decompiles compilation result into FSM format, which can FSMEditor read.

In order to make decompilation possible, compilation must write some decompile info into compilation result. Compiler writes this info using xml like tags enclosed, by default, by C/C++ program comments "/.../".

The compilation result has the following structure:

/*%FSM<COMPILE "compileConfigPath, fsmName">*/
/*%FSM<HEAD>*/
/*
item0[] = {"someStateName",0,250,-55.278748,-369.225311,34.721249,-319.225342,0.000000;}
item1[] = {"someOtherStateName",4,218,-55.288883,-274.332214,34.711143,-224.332199,1.000000;}
...
link0[] = {0,1};
link1[] = {0,3};
...
globals[] = {0.000000,1,0,1,65280,640,480,1,25,6316128,1,-147.218781,307.800598,236.949921,-392.713837,482,667,1};
window[] = {0,-1,-1,-1,-1,894,66,837,87,1,500};
*//*%FSM</HEAD>*/

<compilation pass result is here>

/*%FSM</COMPILE>*/

The whole result is enclosed by COMPILE tag, with compileConfig and FSMName specified. In the HEAD tag, there are data to specify GUI representation of FSM, in order to display it is structure the same way, it was last edited and saved in FSMEditor. Each Compile tag uniquely determines the FSM compiled into some file, by its FSMName. File can contain more than one FSM, each enclosed by it is COMPILE tags with different FSMName. It works in such a way, that:

Compile Configs

Compile config is param file, which controls compilation process. It contains three main classes:

All classes or command names should contain some postfix to make their multiple usage possible. For instance, you can use multiple prints inside one class, using for something like print_1, print_2, etc. It is due to paramFile assumption of name uniqueness.

Compile Config - Classes Structure

The classes have the following structure:

example: print_1 = "FSM contains %(numStates) states, from which %(numFinalStates) are final\n";

print_3 = "_finalStateArray = [";
class FinalStates
{
    iffirst_yes = 1;
    print_1 = "%qt(finalStateName)";
    iffirst_no = -1;
    print_2 = ", %qt(finalStateName)"; // not first, so start with comma separator
    iffirst_all = 0;
}
print_4 = "];\n";

Printing decompile tags

In order to allow decompiler to rebuild the FSM from compilation result, each state, condition or other values must be processed during compilation. If compilation uses many passes, than there is no need to use decompile tags printing in each pass. You can control the compile tags printing, using the following:

Remarks

  1. Scripted FSM does not allow values containing newline chars. In order not to force user to write all values (in FSMEditor) into one line, there is modifier clearNewLines. It can be used inside Compile class.
  2. If there is such need to enclose value into quotes, the modifier qt should be used.
    • Wrong usage: print = "StateInfo(\"%(statename)\"";
    • Correct: print = "StateInfo(%qt(statename)";
  3. In order to access the FSM handle from inside the FSM, use _thisFSM.