Simple system which utilizes macros to allow for linear time-dependant scripting in Haxe.
Allows to define scripts that incorporate waiting, as well as delayed execution or waiting for input, without having to resort to callbacks and overall spaghetti code.
openChatBubble("hello there");
sleep(2);
closeChatBubble();
As you can see in the above example, an event is defined in which a chat bubble opens for 2 seconds and then closes. And this is executed without freezing the thread, the code is simply rearranged in such a way that expressions are automatically split up and executed at the correct time.
Some of the main benefits of this system:
- A linear scripting engine without the need to manage states and timers.
- Do the scripting directly in Haxe, with access to the rest of the codebase.
- Scripts are executed coherently, even though the time of execution of each line is dynamic, the scope is still respected, so it is possible to declare variables.
Installation
haxelib install escript
Usage
Define event scripts by extending the Event class.
import escript.Event;
class MyEvent extends Event
{
// The start function contains the script that is run when the event is started.
override function start()
{
// Do things.
doThing();
// Wait for 2 seconds.
sleep(2);
// Do more things.
doMoreThings();
}
}
Then an instance of EventEngine should be used to manage events.
import escript.EventEngine;
var engine: EventEngine = new EventEngine();
// Start new events.
engine.dispatch(new MyEvent());
engine.dispatch(new MyOtherEvent());
engine.dispatch(new RepeatingEvent(), true);
// Then update the engine in the game's update loop.
while (true)
{
engine.update(deltaTime);
}
Scripting
The following methods to control execution are available within the script's environment.
sleep(t): Causes a delay in the script execution for a given timetin seconds.pause(): Pauses a script, suspending execution untilresume()is called on the event object.waitFor(c, ?t): Pauses a script, suspending execution until the given condition callbackc()returnstrue. Optionally may also specify a sample timetin seconds.x = input(?p): Pauses a script, suspending execution untilwrite(d)is called on the event object, which will assign the input datadtox. Can optionally specify a custom input promptpin order to differentiate input requests from the same event.jump(s, ?t): Jumps to the script functionsafter a delay oftin seconds.stop(?cr): Aborts the execution of the current event. Settingcrtotruealso cancels the repeating of the event.
Note: All functions except jump() and stop() can only be called from the top-level of a method inside an Event class.
Event engine callbacks
The following callback functions may be registered on the EventEngine instance, to be notified of changes to an event's state.
onEventDone(e): Invoked when eventehas just finished.onEventPaused(e): Invoked when eventehas been paused.onEventWaitCondition(e): Invoked when eventehas been suspended to wait until a condition is met.onEventWaitInput(e, p): Invoked when eventehas been suspended to wait for input, with input promptp.
Limitations
- All event functions except
jump()andstop()can only be called from the top-level of an event method.
override function start()
{
// This is ok
sleep(1);
}
override function start()
{
if (x < 5)
{
// This is NOT OK
sleep(1);
}
}
override function start()
{
// This on the other hand is fine
if (x < 5)
{
jump(methodOne);
}
else if (x < 10)
{
jump(methodTwo);
}
else
{
stop();
}
}- Calling other functions is possible, but using
sleep()orpause()in a function called from a script will cause the original script to terminate at that point, with execution moving fully to the function that was called.
override function start()
{
x = 1;
foo();
trace(x); // This will NOT run.
}
function foo()
{
x++; // This will run.
sleep(2);
x++; // This will run.
}
So doing this to move from one script method to another is possible, but it should only be used at the end of a script.