Graphical Scripting Language
From C4 Engine Wiki
The C4 Engine contains a powerful scripting language that can be used to implement event sequences taking place in response to some kind of trigger in a world. A script in C4 does not require the use of any textual programming language. Instead, a script is shown as a graphical representation of the actions to be performed and their interdependencies. Scripts in C4 support local and global variables, conditional execution, loops, and expression evaluation.
For a comparison to the Kismet scripting system implemented in the Unreal Engine, see Comparison of Scripting Languages.
For information about creating custom script methods, see Defining a Custom Method.
For information about the use of expressions in scripts, see Expression Evaluation in Scripts.
Contents |
Adding a Script to the World
The image to the right shows the Script Editor. A script can be assigned to any node in a world and edited by attaching the script controller to it as follows.- In the World Editor, select the node to which you would like to assign a script.
- Choose Get Info from the Node menu or just press Ctrl-I (Cmd-I on the Mac).
- Click on the Controller tab in the Node Info dialog.
- Select the Script controller from the list of available controllers.
- Click on the Edit Script button that appears to open the Script Editor.
A script can also be assigned to any item inside a panel effect. For more information, see Using the Panel Editor.
An existing script can be edited by following the same steps above, except that the script controller will already be selected in the Node Info window.
Script Execution
The graphical representation shown in the Script Editor is known as a control flow graph. The nodes in this graph are called methods, and the edges in this graph are called fibers.
A method is shown as a box in the Script Editor, and it represents a single action in the script. What a particular method actually does is determined by the C++ code that defines that method. The C++ code for many methods is built into the core engine, and the game code can implement new C++ code for any number of custom methods.
A method can produce both a boolean result and a separate output value that can be stored in a script variable. The boolean result is used for conditional execution, and the output value may be used as an input for another method.
A fiber is a directed edge shown as a curve with an arrowhead at one end, and it represents the flow of execution from one action to another. The method from which the fiber starts is called the start node for the fiber, and the method to which the fiber points is called the finish node for the fiber. Each fiber possesses an execution mode that is one of the following values, and this mode controls conditional execution.
- Execute always. The fiber is followed regardless of the boolean result produced by its start node. These fibers are colored black.
- Execute on condition true. The fiber is followed only if the boolean result produced by its start node was true. These fibers are colored green.
- Execute on condition false. The fiber is followed only if the boolean result produced by its start node was false. These fibers are colored red.
A fiber that represents a loop in the script is shown as a dashed curve. Looping fibers (also known as back edges) are detected automatically by the Script Editor and cannot be specified as looping or not looping by the user.
When a script begins execution, the methods having no incoming fibers are visited first and executed. These methods are colored green in the Script Editor. When a method finishes executing, each of its outgoing fibers is signaled as either live or dead depending on the boolean result of the method and the execution mode of the fiber. Any fibers that are not dead looping fibers are signaled as ready. A subsequent method is executed whenever (a) all of its incoming non-looping fibers are ready or (b) any of its looping incoming fibers are ready. If any of the fibers are live, then the next method is executed normally. If all of the fibers are dead, then the next method is skipped, but its non-looping outgoing fibers are still signaled as in the ready and dead state. This model allows execution to continue past points where different conditionally executed paths join back together.
Multiple fibers leaving a particular method can be thought of as executing in parallel from a high-level perspective. Since they are actually executed serially at the low level in a cooperatively scheduled fashion, the edges representing control flow in a script are reminiscent of operating system fibers, and that is where they get their name. The order in which methods immediately succeeding a completed method are executed is undefined, except for the restriction that all incoming non-looping fibers must be ready.
Methods are colored red in the case that the user creates a closed loop having no incoming fibers from methods not belonging to a cycle in the graph. Methods in such a loop can never be executed, so the red color gives an indication of dead code. This situation can be corrected by adding a null method to the graph and drawing a fiber between it and the first method that should execute in the loop.
Editing a Script
The tabbed lists on the left side of the Script Editor show the methods which are available for use in a script. The Basic and Standard tabs contain methods that are implemented by the core engine, and the Custom tab contains methods that are implemented by the game code.
When the Script Editor is opened for the first time for a particular node or panel item, the script viewport is blank. New methods can be added to the script by selecting them from the lists and clicking in the viewport. The editor won't let you place two methods on top of each other, and the cursor will change to indicate where it's possible to place a new method.
Fibers are added to a script by using the Draw Fiber tool at the bottom of the editor window. When that tool is selected, clicking in one method and dragging into another method will cause a new fiber to be created beginning at the first method and ending at the second. You cannot create multiple fibers between the same two methods. The execution mode of a fiber can be changed by selecting the Cycle Fiber Condition command from the Script menu or using the shortcut Ctrl-F (Cmd-F on the Mac).
The physical position of a method in the viewport is irrelevant. The execution flow structure of a script is determined entirely by the fiber connections. Methods may be placed in any convenient location.
Script Editor Tools
There are five tool buttons at the bottom of the Script Editor window, and they have the following uses. In addition to clicking on the tool button, each of these tools can also be selected by pressing the shortcut number key as shown in the table. (The shortcuts were chosen to be the same as the corresponding tools in the World Editor.)
Method Settings
Each method may expose a group of configurable settings that can be accessed in a Method Info window. To edit them, select a single method and select Get Info from the Script menu or use the shortcut Ctrl-I (Cmd-I on the Mac). Double-clicking on a method will also open its Method Info window. The layout of the Method Info window depends on the type of the selected method. In all cases, the configurable settings for the method are displayed on the right side of the Method Info window. A column entitled "Input Variables" is also shown that let's you specify that a variable should be used as the input for a setting instead of any constant value entered in the window. (See below for more information about variables in scripts.)
Most methods operate on some node in the scene, called the target of the method. The target node may be chosen from the list in the upper-left corner of the Method Info window. The target node may be any of the following nodes.
- The target node of the script controller itself. This is the same node that the script is attached to.
- The trigger node that was activated and caused the script to execute.
- The node that activated the trigger node (the activator node).
- Any node that is connected to the target node of the controller.
Once a target node has been selected for a method, it is displayed in parentheses under the method's name in the script viewport.
Call Controller Function Method
If a method is of type "Call Controller Function", then a list of available controller functions for the selected target node is displayed on the left side of the Method Info window. Once a function is selected, its configurable settings are displayed in the list on the right side of the window. If a function outputs a value, then a field for designating an output variable also appears in the lower-left corner of the window.
Change Settings Method
If a method is of type "Change Settings", then a list of setting categories for the object referenced by the selected target node is displayed on the left side of the Method Info window. (Most nodes types have only one category.) Once a category is selected, the object's configurable settings are displayed in the list on the right side of the window. Any settings that are shown to be in an indeterminant state are not affected when the method is executed. All of the settings can be reset into the indeterminant state by clicking the Clear Settings button.
Evaluate Expression Method
If a method is of type "Evaluate Expression", then an expression can be entered in the Method Info window. See Expression Evaluation in Scripts for more information about expressions.
Variables
A script may define any number of variables, each having one of the following types.
| Type | Description |
| boolean | The value true or false. |
| integer | A 32-bit signed integer. |
| float | A 32-bit floating-point number. |
| string | A character string. |
| color | A four-component RGBA color. |
| vector | A three-dimensional vector. |
| point | A three-dimensional point. |
A new variable is created by clicking the New button below the variable list on the left side of the Script Editor window. After clicking the New button, a dialog appears that lets you enter the following information about the new variable.
- The variable name. A variable name can contain any of the characters A-Z, a-z, 0-9, or _ (underscore). A variable name cannot begin with a number.
- The variable type. This can be any of the types listed above.
- The variable scope. The scope can be either Script or Object. If a variable has the Script scope, then its value is reset to its initial value each time the script is run. If a variable has the Object scope, then it is shared among all instances of the script and retains its value from one run to the next in a manner analogous to static local variables in C++.
- The variable's initial value.
After a variable is created, it can be changed by double-clicking on it in the variable list.
Whenever a variable is designated to receive the output value for a method, the name of the variable followed by an equals sign is displayed in the method's box in the script viewport. This gives a visual indication that the variable is being set to the value produced by the method. The output value of a method is implicitly converted to the type of the variable to which it is assigned, if necessary.
The value of a variable is used as an input for a method by entering the variable's name under the “Input Variables” column for a particular setting in the Method Info box. If the variable's type does not match the expected type for the setting, then its value is implicitly converted to the needed type.
Built-in Script Methods
There are several types of script methods that are defined in the engine module. An application module can define any number of its own custom script methods as well, and they will show up in the palette on the left side of the script editor window.
The following table describes the operation of the methods built into the engine that appear under the Basic and Standard tabs in the editor.
| Method | Description | Duration |
| Null | Performs no operation. This method is useful for joining multiple fibers of execution at the end of a loop or as a starting node for the script when a loop begins at the first method that performs some action. | Completes immediately. |
| Evaluate Expression | Evaluates a text expression and outputs the result. (If the expression contains a syntax error, then the method is colored bright yellow in the Script Editor.) | Completes immediately. |
| Call Controller Function | Calls a function of the controller attached to the method's target node. The specific settings depend on the purpose of the function itself. | Completes when the function indicates that is has finished what it's doing. |
| Change Settings | Changes one or more settings for the object referenced by the target node. | Completes immediately. |
| Activate Controller | Activates the controller attached to the method's target node. The effect depends on the controller being activated. In the case that the target node has a script controller attached to it, the script is executed. | Completes immediately. |
| Get External Variable | Retrieves the value of a specific variable belonging to the script controller attached to the target node of the method. This method is useful for reading values from other scripts in the world and storing them in variables local to the running script. If the variable being retrieved has object scope, then its current value is returned, but if it has script scope, then its initial value is always returned. | Completes immediately. |
| Set External Variable | Sets the value of a specific variable belonging to the script controller attached to the target node of the method. This method is useful for copying values from variables local to the running script to object-scope variables of other scripts in the world. Only variables having object scope can be modified using this method. | Completes immediately. |
| Delay | Waits for a specified amount of time. | Completes after the specified time has elapsed. |
| Wake Controller | Awakens a controller. | Completes immediately. |
| Sleep Controller | Puts a controller to sleep. | Completes immediately. |
| Get Controller Wake State | Returns a boolean value indicating whether a controller is currently awake. | Completes immediately. |
| Enable Node | Enables its target node. | Completes immediately. |
| Disable Node | Disables its target node. | Completes immediately. |
| Get Node Enable State | Returns a boolean value indicating whether the target node is currently enabled. | Completes immediately. |
| Delete Node | Deletes its target node. The node to which the script is attached, or any of its ancestors, cannot be deleted with this method. | Completes immediately. |
| Enable Interactivity | Enables interactivity for its target node. | Completes immediately. |
| Disable Interactivity | Disables interactivity for its target node. | Completes immediately. |
| Get Node Interactivity State | Returns a boolean value indicating whether the target node is currently interactive. If the target node does not possess the Interactivity property, then the return value is false. | Completes immediately. |
| Random Integer | Generates a random integer in a specified range and outputs the value. The boolean result is set to true if the value is not zero and false otherwise. | Completes immediately. |
| Random Float | Generates a random floating-point number in a specified range and outputs the value. The boolean result is set to true if the value is not zero and false otherwise. Note that in this case, a false result is extremely unlikely because the return value would have to be exactly 0.0. | Completes immediately. |
| Play Ambient Sound | Plays a sound resource without 3D spatialization. | Completes when the sound has finished playing. |
| Show Geometry | Shows the target node. Has no effect if the target is not a geometry node. | Completes immediately. |
| Hide Geometry | Hides the target node. Has no effect if the target is not a geometry node. | Completes immediately. |
| Get Geometry Visibility State | Returns a boolean value indicating whether the target node is visible. If the target node is not a geometry node, then the return value is false. | Completes immediately. |
| Play Sound Source | Plays the target node. Has no effect if the target is not a source node. | If the source was already playing or the source is looping, then the method completes immediately. Otherwise, the method completes when the source finishes playing. |
| Stop Sound Source | Stops its target node. Has no effect if the target is not a source node. | Completes immediately. |
| Set Material Color | Changes the color of one of the material attributes assigned to its target node. Has no effect if the target is not a geometry node. Since materials are shared, all geometry nodes using the same material, not necessarily just the target, are affected. Thus, to change a material color for only one geometry node, it should use a unique material. This method can change any material attribute that includes a color, such as diffuse, specular, or emission. | Completes immediately. |
| Get String Length | Outputs the length of an input string. | Completes immediately. |
