rot-js
Version:
A roguelike toolkit in JavaScript
117 lines (93 loc) • 3.59 kB
HTML
<h2>Asynchronous game engine</h2>
<h3>async/await</h3>
<p>The <code>ROT.Engine</code> API documented on this page is provided for backwards compatibility only. In a modern JavaScript, you shall use the <code>async/await</code> facility to coordinate individual asynchronous actors. The main game loop can be implemented (without <code>ROT.Engine</code>) in a trivial manner (the <code>mainLoop</code> function):
<div class="example">
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();
</div>
<p>Client-side JavaScript is non-blocking: all long-running operations are asynchronous. The <code>ROT.Engine</code> loop is well suited to orchestrating the possibly-async coordination of various game actors.</p>
<p>To use the engine, you first need a <a href="#timing/scheduler">scheduler</a> that stores your actors and manages their priority. This scheduler is passed to <code>ROT.Engine</code>'s constructor. Once the engine is started, it correctly calls the <code>act()</code> 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.</p>
<div class="example">
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(""));
</div>
<h3>Promises</h3>
<p><code>ROT.Engine</code> is ready for a promise-based async control flow: if any actor returns a "thenable" from its <code>act()</code> method, the engine locks itself and waits for the thenable to get resolved (via its <code>then()</code> 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.</p>
<div class="example">
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();
</div>