Asynchronous game engine
async/await
The ROT.Engine
API documented on this page is provided for backwards compatibility only. In a modern JavaScript, you shall use the async/await
facility to coordinate individual asynchronous actors. The main game loop can be implemented (without ROT.Engine
) in a trivial manner (the mainLoop
function):
async function mainLoop() {
while (1) {
let actor = scheduler.next();
if (!actor) { break; }
await actor.act();
SHOW(output.join(""));
}
}
let scheduler = new ROT.Scheduler.Simple();
let output = [];
let actor1 = { // sample actor: pauses the execution when dead
lives: 3,
act: function() {
output.push(".");
this.lives--;
if (!this.lives) {
scheduler.remove(actor1);
scheduler.add(actor2);
return new Promise(resolve => setTimeout(resolve, 500)); // pause
}
}
}
let actor2 = {
act: function() { output.push("@"); }
}
scheduler.add(actor1, true);
mainLoop();
Client-side JavaScript is non-blocking: all long-running operations are asynchronous. The ROT.Engine
loop is well suited to orchestrating the possibly-async coordination of various game actors.
To use the engine, you first need a scheduler that stores your actors and manages their priority. This scheduler is passed to ROT.Engine
's constructor. Once the engine is started, it correctly calls the act()
method on proper actors (picked by the scheduler). It is possible to recursively stop (lock) the engine, should some operation (such as displaying a dialog or waiting for user input) block the execution. Once all lock levels are unlocked, the engine continues its execution.
var scheduler = new ROT.Scheduler.Simple();
var engine = new ROT.Engine(scheduler);
var output = [];
/* sample actor: pauses the execution when dead */
var actor1 = {
lives: 3,
act: function() {
output.push(".");
this.lives--;
if (!this.lives) {
scheduler.remove(actor1);
engine.lock(); /* pause execution */
setTimeout(unlock, 500); /* wait for 500ms */
}
}
}
scheduler.add(actor1, true);
var unlock = function() { /* called asynchronously */
var actor2 = {
act: function() {
output.push("@");
}
}
output = [];
scheduler.add(actor2, false); /* add second (non-repeating) actor */
engine.unlock(); /* continue execution */
SHOW(output.join(""));
}
engine.start();
SHOW(output.join(""));
Promises
ROT.Engine
is ready for a promise-based async control flow: if any actor returns a "thenable" from its act()
method, the engine locks itself and waits for the thenable to get resolved (via its then()
method). This feature is independent on the Promise implementation used; feel free to supply your own, use a library or leverage your browser's native Promises.
var scheduler = new ROT.Scheduler.Simple();
var engine = new ROT.Engine(scheduler);
var output = [];
/* sample actor: pauses the execution when dead */
var actor = {
lives: 3,
act: function() {
var done = null;
var promise = {
then: function(cb) { done = cb; }
}
output.push(".");
SHOW(output.join(""));
this.lives--;
/* if alive, wait for 500ms for next turn */
if (this.lives) {
setTimeout(function() { done(); }, 500);
}
return promise;
}
}
scheduler.add(actor, true);
engine.start();