GameFlow API

The main objectives of the GameFlow API (Application Programming Interface) are extensibility and communication with other scripts. In concrete, the possibilites offered by the current version are the following:

  • Creating your own custom action and condition blocks.
  • Building block sequences programmatically, like templates.
  • Searching for GameFlow blocks in order to access their public properties and methods.
  • Executing actions directly from your scripts.
  • Listening and reaction to GameFlow events from your scripts.

The usage of the API does not requiere expert knowledge of C#, but having at least some notions is recommended for an easier understanding of the companion examples.


Custom actions

In order to create a custom action, two C# scripts are required:

  1. A script extending class Action (or any of its subclasses) for defining the action properties and implementing the action functionality.
  2. An Editor script extending class ActionEditor (or any of its subclasses) for implementing the action user interface to be shown in the Inspector.

Wizard

In order to accelerate the creation of the base code for those C# scripts, GameFlow provides a code generation wizard that you can find under the Assets > Create > GameFlow > Action… menu option.

CreateAction

In this window we can decide the namespace, name and type of the action class, if we want the action to be executed also in Edit mode and the destination folders of the two scripts to be generated once we click on the Create button.

Regarding the type of action (Action Type), the available options are the following:

  • Action: Instant action that starts and ends in the current frame.
  • Time Action: action based on time whose execution can be extended for several frames.
  • Macro Action: action that can implement a differentiated execution in time of edition. ** Function: action that generates a value as a result.

Once the scripts are generated and the compilation is finished, our new action should appear already available in the action selection window:

MyAction1

And of course we can add it to our programs as if it were any other action available as standard:

MyAction2

Generated code

The code generated for the action script would be this:

using UnityEngine;
using GameFlow;

// An example showing how to implement a basic custom Action.

namespace MyNamespace {

// Help summary is localized according to current system language.
[Help("en", "Action summary.", "context-help-url")]
[Help("es", "Resumen acción.", "url-ayuda-contextual")]

// Prevent the Action from appearing in the Add Component menu.
[AddComponentMenu("")]

public class MyAction : Action {

	// Declare a Variable-friendly property for the action:
	[SerializeField]
	string _yourName;
	[SerializeField]
	Variable _yourNameVar;

	// Define a convenience property getter
	string yourName {
		// Link string value and Variable reference through an extension method
		get { return _yourNameVar.GetValue(_yourName); }
	}

	// Code implementing any setup required by the action
	protected override void OnSetup() {
	}

	// Code implementing the effect of the action
	protected override void OnExecute() {
		// Do something with the declared property
		Debug.Log("Hello, " + yourName);
	}

}

}

The first thing we find in this code is an example of the mechanism used in GameFlow to add Variables support to the actions. As we can see, this mechanism is based on the use of the GetValue() extension method, which only deals with the final value that the action must use, taking into account whether the user specified a Variable or, on the contrary, wrote a value directly in the specific field to which the property is associated.

Regarding the method OnExecute() it is important to explain that it will be executed once for each frame in which the action is still running, which is equivalent to a single time for actions of instant execution, since the action begins and ends in the same frame.

Also indicate that the OnSetup() method will always be executed before the OnExecute() method but it will do so only in the first frame, making it a perfect method for values ​​initialization purposes that the action will need for its later execution.

Let’s see below the generated code for the script corresponding to the Editor part:

using GameFlow;
using UnityEditor;

namespace MyNamespace {

[CustomEditor(typeof(MyAction))]
public class MyActionEditor : ActionEditor {

	// Declare properties exactly as defined in the Action subclass
	protected SerializedProperty _yourName;
	protected SerializedProperty _yourNameVar;

	// Action user interface
	protected override void OnActionGUI() {
		// Draws a Variable-friendly text field for the property in the Inspector
		PropertyField("Your Name", _yourName, _yourNameVar);
	}

}

}

In this case it is very important to follow the indications of the comments in terms of defining the SerializedProperty members with exactly the same name that were defined in the other script, since GameFlow automatically takes care of initializing the properties saving us the writing of a code that It can be heavy. That is the reason why in this code the properties are only declared.

It is also important to note that both the variety of the PropertyField() method used in this example and all the others (there are many) are documented in the ActionEditor entry of the GameFlow reference.

Action Subclasses

The GameFlow API offers a series of subclasses of Action that add certain properties and useful methods, and that can be used to accelerate the writing of own actions. They are the following:

Class Description
DrawGizmosAction Action used for drawing Gizmos in the Editor
Function Action that returns a result
LimitValueAction Action that makes use of a value intended to be limited
ListAction Action that makes use of a List
ListIndexAction Action that makes use of a List and an Index property
ListIndexFunction Similar to ListIndexAction but with an additional return value
ListItemAction Action that makes use of a List and an item specifier
ListItemFunction Similar to ListItemAction, but with an additional return value
ListItemIndexAction Action that makes use of a List, an item specifier and an Index property
ListItemIndexFunction Similar to ListItemIndexAction, but with an additional return value
NumericParameterAction Action that makes use of a numeric Parameter
NumericVariableAction Action that makes use of a numeric Variable
ParameterAction Action that makes use of a Parameter
TimeAction Time-based Action
TimeFunction Time-based Action that returns a value once finished
TransformAction Action that makes use of a Transform and some frequently used and related properties
TransformAction2D Action that makes use of a Transform (for 2D use) and some frequently used and related properties
ValueAction Action that makes use of a Value property
VariableAction Action that makes use of a Variable

ActionEditor Subclasses

In the same way, the API also offers a subclass of ActionEditor for each of the subclasses from which our scripts can inherit to accelerate the writing of the code that will manage the user interface of the action. They are the following:

Subclasses
DrawGizmosActionEditor, FunctionEditor, LimitValueActionEditor, ListActionEditor, ListIndexActionEditor, ListIndexFunctionEditor, ListItemActionEditor, ListItemFunctionEditor, ListItemIndexActionEditor, ListItemIndexFunctionEditor, NumericParameterActionEditor, NumericVariableActionEditor, ParameterActionEditor, TimeActionEditor, TimeFunctionEditor, TransformActionEditor, TransformAction2DEditor, ValueActionEditor, VariableActionEditor

In the reference of each of these subclasses, the different methods of utility that each class provides are detailed.

Execution at Edit time

The game execution engine of GameFlow is unique in its kind because it allows execution in both Play and Edit modes, which makes possible things like macros or editor utilities written entirely with GameFlow.

In this way, a GameFlow action can:

  • Execute the same code either in Play or Edit modes, which would be the default behavior.
  • Execute a code in Play mode and a different code -that will have access to the API UnityEditor to be able to use things like the Undo class- in Edit mode.
  • Execute code only in Play mode or only in Edit mode.

In this section we will see how to prepare our actions to be able to provide them with a specific execution in Play mode, and we will do so by studying the code that the action creation wizard generates when we indicate that we want a Macro-type action, specifically the interface that manages the ‘Editor’ part of the action.

using GameFlow;
using UnityEditor;
using UnityEngine;

namespace MyNamespace {

[CustomEditor(typeof(CustomMacroAction))]
public class CustomMacroActionEditor : ActionEditor {

	// Declare properties exactly as defined in the Action subclass
	protected SerializedProperty _yourName;
	protected SerializedProperty _yourNameVar;

	// Action user interface
	protected override void OnActionGUI() {
		// Draw a text field for the property and its linked Variable property
		PropertyField("Your Name", _yourName, _yourNameVar);
	}

	[InitializeOnLoadMethod]
	static void Init() {
		// Set the delegate that will handle the execution of the action in Edit mode
		SetActionDelegate<CustomMacroAction>(OnExecute);
	}

	static void OnExecute(Action action) {
		// You can do any Editor stuff required by the action:
		Undo.RegisterCompleteObjectUndo(action.gameObject, "CustomMacroAction");
		// Note you can still access action public properties by using casting:
		Debug.Log("Hello, " + (action as CustomMacroAction).yourName + " (executed in Edit mode)");
		// And of course you can execute the default (Play mode) code for the action:
		Execute(action);
	}

}

}

There are two fundamental parts to note in this code:

  1. The use of the SetActionDelegate<T>() method within the Init() method to tell GameFlow which method will take care of the execution of the action at edit time.
  2. The OnExecute() method which is where we implement the specific execution that we want for our action when executed at runtime.

As explained in the comments, it is not only possible to use own methods of UnityEditor but we can even return the execution to the subclass of Action, so that we do not have to duplicate code.

Remember also that it is possible to run any program at edit time without having to transform it into a macro or utility using the Execute option in its context menu and that we can repeat the last execution quickly using the keyboard shortcut Alt-0.

For more information about the operation of the mechanism for delegating the execution of actions in Edit mode, consult the entries of the classes Action and ActionEditor in the GameFlow reference.


Custom conditions

In the same way as in the case of own shares, two scripts are required to create a proper condition:

  1. A script that will extend the Condition class (or any of its subclasses), where the definition of the properties and / or comparisons available for the condition will be made, as well as the implementation of the evaluation from which it is obtained the result of the condition.
  2. An Editor script that will extend the ConditionEditor class (or any of its subclasses) where the interface of the condition that will be shown to the user in the Inspector will be implemented.

Wizard

To help in the creation of the C # base code for these scripts, GameFlow has a code generation wizard that can be accessed through the menu option Assets > Create > GameFlow > Condition …

CreateCondition

In this window we can decide the namespace, name of the condition and the destination folders of the two scripts to generate once we click on the Create button.

Once the scripts are generated and the compilation is finished, our new condition should appear already available in the action selection window.

MyCondition1

Note: GameFlow automatically suppresses the word “Condition” in the names of the conditions to avoid redundancy.

Of course we can add our condition in those actions that support conditions as if it were any other condition available as a series:

MyCondition2

Generated code

The code generated for the condition script would be this:

using GameFlow;
using UnityEngine;

// An example showing how to implement a basic custom Condition.

namespace MyNamespace {

// Help summary is localized according to current system language.
[Help("en", "Condition summary.", "context-help-url")]
[Help("es", "Resumen condición.", "url-ayuda-contextual")]

// Prevent the Condition from appearing in the Add Component menu.
[AddComponentMenu("")]

public class CustomCondition : Condition {

	// Declare a Variable-friendly property for the condition
	[SerializeField]
	int _number;
	[SerializeField]
	Variable _numberVar;

	// Define a convenience property getter
	public int number {
		// Link basic-type value and Variable reference through an extension method
		get { return _numberVar.GetValue(_number); }
	}

	public enum Comparison {
		IsZero,
		IsNotZero
	}

	[SerializeField]
	Comparison _comparison;

	// Code implementing the evaluation of the Condition
	protected override bool OnEvaluate() {
		// Evaluate according to specified comparison
		switch (_comparison) {
		case Comparison.IsZero:
			return number == 0;
		case Comparison.IsNotZero:
			return number != 0;
		}
		// Default evaluation
		return false;
	}

}

}

As we can see, the code is quite similar to the one used to create own shares, only that in this case the method to be implemented would be OnEvaluate() and the purpose would be to return the result of the evaluation of the condition based on the value of the properties and the comparison that the user has chosen.

Finally, the code for the script corresponding to the Editor part would be this:

using GameFlow;
using UnityEditor;

namespace MyNamespace {

[CustomEditor(typeof(CustomCondition), true)]
public class CustomConditionEditor : ConditionEditor {

	// Declare properties exactly as defined in the Condition subclass
	protected SerializedProperty _number;
	protected SerializedProperty _numberVar;
	protected SerializedProperty _comparison;

	// Condition user interface
	public override void OnConditionGUI() {
		// Draws a Variable-friendly numeric field in the Inspector
		PropertyField("Number", _number, _numberVar);
		// Draws a label that you can click for additional options
		PopupLabel(_comparison, true);
	}

}

}

We will see that this code is also very similar to the one used for the Editor part of an own action, with the difference that in this case the interface is defined in the method OnConditionGUI() and we use the method PopupLabel() to the selection of the comparison.

Condition Subclasses

The API of GameFlow offers a series of subclasses of Condition that add certain properties and methods of utility, and that can be used to accelerate the writing of own conditions. They are the following:

Class Description
ValueCondition Condition that makes use of a Value property, normally for comparison purposes.

Evaluation at Edit time

Just as actions can also be executed in Edit mode, GameFlow can also perform a differentiated evaluation of a condition depending on whether it is running in Play or Edit mode.

In the following example, we will modify the previous script to implement a different (and meaningless, because it is only shown as an example) evaluation that will be performed only in Edit mode:

using GameFlow;
using UnityEditor;
using UnityEngine;

namespace MyNamespace {

[CustomEditor(typeof(CustomCondition))]
public class CustomConditionEditor : ConditionEditor {

	// Properties as defined in the Action class
	SerializedProperty _number;
	SerializedProperty _numberVar;
	SerializedProperty _comparison;

	// Condition user interface
	public override void OnConditionGUI() {
		// Draws a Variable-friendly numeric field in the Inspector
		PropertyField("Number", _number, _numberVar);
		// Draws a label that you can click for additional options
		PopupLabel(_comparison, true);
	}

	[InitializeOnLoadMethod]
	static void Init() {
		// Set the delegate that will handle the evaluation of the condition in Edit mode
		SetConditionDelegate<CustomCondition>(OnEvaluate);
	}

	static bool OnEvaluate(Condition condition) {
		// Note you can still access condition public properties by using casting:
		return (condition as CustomCondition).number == 1;
	}

}

}

As we can see, it is about using the SetConditionDelegate<T>() method within the Init() method to indicate the method that will take care of the evaluation and implement that method (OnEvaluate() in the example).

For more information about the operation of the delegation mechanism for evaluating the conditions in Edit mode, consult the entries for the classes Condition and ConditionEditor in the GameFlow reference.


Templates

One of the most interesting possibilities of our API is the ability to build scripts ‘template’ programmatically. The steps to follow for this are:

  1. Create a C # script that inherits from GFBehaviour.
  2. Implement the OnInit() method describing the initial configuration of default blocks that we want using methods.

The OnInit() method will be executed automatically every time we add our script to a GameObject, allowing us to use a series of specialized methods to add blocks to the script just as we would in the Editor, thus converting the script into a template .

Let’s see below the methods to use to add the different types of blocks available in GameFlow.

Independent blocks

Are those blocks that do not require to be contained in other blocks to exist and therefore can be seen in the primel level or root level of our script derived from GFBehaviour. In our example (see end) would be the On Start, Note and State Machine blocks.

To add these blocks to the script we use the AddBlock<T>() method in which we must replace T with the name of the class that represents the block. This method will add the block and return a reference to it that we can use for our next operations.

The list of classes that represent independent blocks is the following:

Block Type Class
Program Macro, OnActivate, OnApplicationFocus, OnApplicationInit, OnApplicationPause, OnApplicationQuit, OnAwake, OnClick, OnCollisionEnter, OnCollisionEnter2D, OnCollisionExit, OnCollisionExit2D, OnCollisionStay, OnCollisionStay2D, OnCustomEvent, OnDeactivate, OnDemand, OnDeselect, On Destroy, OnDrawGizmos, OnEndEdit, OnExternalMessage, OnFixedUpdate, OnGameOver, OnGamePause, OnGameResume, OnGameStart, OnLanguageSet, OnLateUpdate, OnMacroKey, OnMouseDown, OnMouseDrag, OnMouseEnter, OnMouseExit, OnMouseOver, OnMouseUp, OnParameterChange, OnPointerDown, OnPointerEnter, OnPointerExit, OnPointerUp, OnProgramFinish, OnSceneLoad, OnSelect, OnStart, OnTimerExpire, OnTriggerEnter, OnTriggerEnter 2D, OnTriggerExit, OnTriggerExit2D, OnTriggerStay, OnTriggerStay2D, OnUpdate, OnValueChange, OnVariableChange
Data List, Localization, Parameter, Variable
Documentation Description, Note, Separator
Tool Command, Force, Key, Path, Pool, Ray, StateMachine, Timer

Actions

The actions are not independent blocks and therefore must be added using the AddAction<T>() method of that program, state or action that will contain it, where T must be replaced by the name of the class that represents the action, which will normally be the name of the action without spaces (for example, the class for the action ‘Set Rotation’ would be SetRotation). The complete list of available actions can be found in the Actions Reference.

Apart from all programs and states, the list of action classes that support the AddAction<T>() method is the following:

Classes
During, For, ForEach, Group, If, Loop, OnState, Repeat, RepeatUntil, While

This method will return a reference to the created action that we can use to, for example, modify some of its properties.

Conditions

Conditions are even less independent blocks than actions, since they can only be added to a few specific actions that support the AddCondition<T> method, where T must be replaced by the name of the class representing the condition to be added. The complete list of available conditions can be found in the Conditions Reference.

The list of action classes that support the AddCondition<T>() method is as follows:

Classes
EvaluateConditions, If, RepeatUntil, While

This method will return a reference to the created condition that we can use to, for example, modify some of its properties.

States

The states (class State) are not independent blocks either, since they can only live within class blocks StateMachine. In this case the method to be used is a simple and direct AddState() without type specification.

This method will return a reference to the created state that we can use to add actions to it.

Example

In the following example we will build a custom template script to which we will add a program, a note and a state machine:

using GameFlow;

namespace MyNamespace {

public class MyGF : GFBehaviour {

	protected override void OnInit() {
		// Add a On Start program with a Log Message action
		Program onStart = AddBlock<OnStart>();
		LogMessage logMessage = onStart.AddAction<LogMessage>();
		logMessage.message = "Hello!";
        // Add a Note
		Note note = AddBlock<Note>();
		note.text = "Just an example";
        // Add a State Machine with a State
		StateMachine sm = AddBlock<StateMachine>();
		sm.AddState("Hola");
	}
}

}

The result of adding our MyGF script to a GameObject would be this:

Image


Communication with GameFlow

The API allows your own scripts to communicate with the GameFlow blocks in case you need to read or write their properties or control their behavior.

Accessing blocks by reference

The most direct way to access a block of GameFlow from your own script is to pass the script a reference to the block. To do this, we will only have to define a member in our script of the desired type and make sure to drag or choose the corresponding object in the editor.

In the following example, our script defines a member of type Variable that we will access to print its value in the console:

using GameFlow;
using UnityEngine;

namespace MyNamespace {

public class MyVar1 : MonoBehaviour {

	public Variable variable;

	void Start() {
        if (variable) {
			Debug.Log("Value = " + variable.stringValue);
        }
	}
}

}

In the Inspector, after the instance of the script MyVar1 we will add a GameFlow component to which we will add a Variable to give it a value (in the example ‘Hello!’) And finally drag it (by clicking on the title and moving the mouse) to the Field ‘Variable’ of the MyVar1 script, remaining as shown in the image:

MyVar1

If everything is correctly configured, when entering Play mode we should see a message in the console with the value of our variable confirming that our script can access the value of the variable.

Accessing blocks by identifier

We also have a second way to access GameFlow blocks that consists of searching the block by its type and identifier (and optionally a search scope) to obtain its reference.

The following are the static search methods available in the Blocks utility class:

Method
FindBlock<T>(string id)
Search for a block of type T with the id specified in all active GameObjects (or that have been active at any time) in the scene.
FindBlock<T>(string id, GameObject go, bool recursive = false)
Search for a block of type T with the specified id but only in the scope of the specified GameObject and optionally (if we specify recursive = true) in its entire hierarchy (that is, in the GameObject children). In this case, it is not taken into account if the GameObject is active or not.

In case of success, these methods will return the reference to the first block that meets the specified search requirements. If no block is found, null is returned.

In the following example we will access a variable again, but this time instead of explicitly passing its reference to it as in the previous example, we will look for it by its name.

using GameFlow;
using UnityEngine;

namespace MyNamespace {

public class FindVar : MonoBehaviour {

	void Start() {
		Variable variable = Blocks.FindBlock<Variable>("Health");
        if (variable) {
			Debug.Log("Value = " + variable.intValue);
        }
	}

}

}

For the example to work, we will need to add a GameFlow component with an Integer variable that we will call “Health”, as shown in the image:

FindVar

If everything is correctly configured, when entering Play mode we should see a message in console with the value of our variable that will confirm that our script finds the variable and can access its value.

Accessing blocks properties

One of the most frequent reasons to access GameFlow blocks from our scripts is to read or write their properties, something that the API also allows.

In the previous examples we have seen how to read the value of two variables using the properties stringValue and intValue, but these properties could also be used to modify the value of the variables just by making an assignment.

So, for example, to change the value of the Variable used in the previous example, we could do:

Variable variable = Blocks.FindBlock<Variable>("Health");
// ...
if (variable) {
	variable.intValue = 500;
}

The properties available for each block class can be found in the API Reference.

Executing methods declared in blocks

In addition to accessing the properties, we also have the ability to invoke the public instance methods of any block to govern their behavior from our scripts.

If for example we wanted to stop a Timer block from which we had previously obtained a reference by searching, it would be enough to invoke the appropriate method:

Timer timer = Blocks.FindBlock<Timer>("MyTimer");
// ...
if (timer) {
    timer.Stop();
}

The available methods for each block class can be found in the API Reference.

Built-in Variables

GameFlow includes a series of pre-defined Variable blocks whose particularity is that of having values that are updated dynamically either at run time or at edit time.

The API offers a simple and unified access to the values of all these variables through the properties of the Builtin utility class:

Property Description Type
activeGameObject Currently selected and focused GameObject GameObject
currentFPS Current frames per second float
day Number of the current day of the month int
defaultLanguage Default language for the application as configured in settings SystemLanguage
deltaTime Time (seconds) that the last frame took to complete float
deviceName Name of the device in use string
frame Number of the current frame int
hour Current hour int
inputAxis Current value for the input axis Vector2
inputAxisX Current value for the horizontal input axis float
inputAxisY Current value for the vertical input axis float
language Current language in use SystemLanguage
mainCamera Reference to the Camera tagged as “Main Camera” Camera
minute Current minute int
month Current month int
mouseDelta Offset from last mouse position Vector2
mouseDeltaX Horizontal offset of the last mouse movement float
mouseDeltaY Vertical offset of the last mouse movement float
mousePosition Current position (x,y) of the mouse cursor Vector2
mousePositionX Current horizontal position of the mouse cursor float
mousePositionY Current vertical position of the mouse cursor float
nativeResolution Native resolution of the screena Vector2
screenCenter Coordinates of the center of the screen Vector2
screenSize Current screen resolution Vector2
second Current second int
year Current year int

So, for example, to obtain the current position of the mouse we could do:

Vector2 mousePos = Builtin.mousePosition;

Direct execution of actions

Another possibility offered by the GameFlow API is to be able to execute the internal code of the actions directly from your own scripts, thus taking advantage of GameFlow not only as an extension for the Editor but also as a function library.

To make this possible, the GameFlow action classes offer one or more static execution methods that by convention are always called Execute(). Therefore, it is only necessary to know the name of the action class to be used and invoke the appropriate method.

If, for example, we wanted to color in Red the GameObject referred to by target, we could do it taking advantage of the action Set Color in this way:

SetColor.Execute(target, Color.red);

The static methods of execution as well as the properties available for each action can be found in the API Reference.

Note: This is a feature that was added late to the API so it is still being implemented. This means that some actions may not yet offer this possibility, but we are working so that in future versions all actions can be executed through static methods.


Event listening

The GameFlow API also exposes a series of subclasses of GFEvent that represent the different events that GameFlow is able to manage. These classes are the following:

Scope Event Classes
Application ApplicationFocusEvent, ApplicationInitEvent, ApplicationPauseEvent, ApplicationQuitEvent
Physics CollisionEnterEvent, CollisionExitEvent, CollisionStayEvent, TriggerEnterEvent, TriggerExitEvent, TriggerStayEvent
User Interface ClickEvent, DeselectEvent, EndEditEvent, PointerDownEvent, PointerEnterEvent, PointerExitEvent, PointerUpEvent, SelectEvent, ValueChangeEvent
Activation ActivationEvent, DeactivationEvent
Customization CustomEvent
Communication ExternalMessageEvent
Game GameOverEvent, GamePauseEvent, GameResumeEvent, GameStartEvent
Keyboard KeyDownEvent, KeyUpEvent
Localization LanguageEvent
Mouse MouseDownEvent, MouseDragEvent, MouseEnterEvent, MouseExitEvent, MouseOverEvent, MouseUpEvent
Timer TimerExpireEvent
Program ProgramFinishEvent
Data VariableChangeEvent, ParameterChangeEvent
Scenes SceneLoadEvent

Event subscription

Any object that implements the IEventListener interface can be added to the list of subscribers that will receive a notification when an event of a certain type is triggered. This allows your own scripts to react to events triggered by GameFlow programs.

The steps to follow to do this are:

  1. Implement in our script the IsListening() and EventReceived() methods of the IEventListener interface to filter and respond to events.
  2. Invoke the AddListener() static method of the type of event to which we want an instance of our script to subscribe.

In the following example, each instance of our MyEventHook script is subscribed to receive notifications of events of type GameStart. Each time one of these events is received, we will post a message on the console as an answer:

using GameFlow;
using UnityEngine;

namespace MyNamespace {

public class MyEventHook : MonoBehaviour, IEventListener {

	void Start() {
		GameStartEvent.AddListener(this);
	}

	public void EventReceived(GFEvent e) {
		Debug.Log("Game started!");
	}

	public bool IsListening() {
		return true;
	}

}

}



« Reference