What is Runaway?

I’ve been working on Runaway since (at least) 2019, and talking about it in vague terms for about that long. But what is Runaway, really?

Before getting started, let’s clear up some terminology. Runaway is a game engine, named after Run (the game series) and Away3D (the 3D rendering library). The Runway is a tunnel in Run 3 that hasn’t been released. Despite the similar names, there’s no connection.

Overview of Runaway

A game engine is a collection of code designed to help people write games. You may have heard of engines such as Unity, Unreal, or GameMaker. Haxe – the language I named this blog after – has engines such as HaxeFlixel, HaxePunk, and/or Armory. All of these are designed to serve as a solid foundation for making games, saving you the time of re-writing your physics and rendering code each time.

Which is an odd thing for me to worry about, since I’ve never been afraid to re-write my physics and rendering code, or anything else really. Each game in the Run series was re-written from the ground up, and Runaway is its own ground-up rewrite. Though it’s taken years, I’ve learned a lot each time, and I’m building Runaway because I finally feel ready to build a standalone game engine, separate from the games themselves.

Entity-component-systems

So what sets Runaway apart from all the other engines? The big difference is, it uses an entity-component-system model. This model isn’t strictly better or worse than conventional engines, but to me it feels more elegant. (And if I’m being honest, the aesthetics are what won me over.) Practically speaking, the model lends itself to loose coupling, meaning Runaway should be especially versatile.

As the name implies, there are three important pieces here:

  • Entities are, you know, things. They can be anything from the tangible (characters, obstacles, items) to the vague (load triggers, score trackers), depending on what components they have.
  • Components are properties of entities. Things like position, size, shape, AI, abilities, and appearance. Each component is a small piece of data, and entities can have as many as needed. Note that components are nothing but passive data storage. They don’t act or update on their own.
  • Systems run the actual code that updates entities and components. Each system looks for a certain set of components, and updates only the entities with that set.

That’s the magic of the ECS model: what you are (an entity’s components) determines what you do (a system’s code).

Alice and Bob

Let’s walk through an example to see this in action.

var alice = new Entity();
var bob = new Entity();

Right now, Alice and Bob have nothing but a unique ID (Alice is 0 and Bob is 1). They aren’t characters yet. I’d describe them as “floating in a void”, but they can’t even do that because they don’t have positions. Let’s fix that.

alice.add(new Position(0, 0, 0));
bob.add(new Position(5, 0, 0));

Now that we gave them Position components, they’re floating in an endless void, doing nothing. How about a race?

alice.add(new Velocity(0, 0, 0));
alice.add(new Acceleration(0, 0, 1));
bob.add(new Velocity(0, 0, 10));

With the addition of new components, they immediately spring into action! Bob takes a commanding lead, at a speed of 10 units per second. Alice, meanwhile, accelerates slowly at 1 unit per second. Even after 5 seconds, she’s only moving at half Bob’s speed and has traveled only 12.5 units, compared to Bob’s 50. Bob will continue to widen the gap between them over the next several seconds, but his speed is fixed. As Alice continues to accelerate, it’s only a matter of time before she overtakes him.

But where is the code for this? Alice and Bob’s positions are changing every frame, even though Position, Velocity, and Acceleration components are nothing but data. It’s because Runaway has a class called MotionSystem, that was waiting all this time for these components to show up.

class MotionSystem extends System {
    @:update private function accelerate(acceleration:Acceleration, velocity:Velocity, time:Float):Void {
        velocity.x += acceleration.x * time;
        velocity.y += acceleration.y * time;
        velocity.z += acceleration.z * time;
    }
    
    @:update private function move(velocity:Velocity, position:Position, time:Float):Void {
        position.x += velocity.x * time;
        position.y += velocity.y * time;
        position.z += velocity.z * time;
    }
}

For those unfamiliar with Haxe, these are two “functions” named accelerate and move. The accelerate function takes three “arguments”: acceleration, velocity, and time, and it modifies velocity. The move function takes velocity, position, and time as arguments, and it modifies position.

Because these functions are marked @:update, they will automatically run once per entity per frame, but only if the entity in question has the correct components. Ignore time (it’s always available), but the other arguments must match. That means having a Position component isn’t enough, because move also requires Velocity. (Just Velocity wouldn’t be enough either.)

Now that Alice and Bob have both components, the move function automatically updates Position each frame. Alice also has an Acceleration component, so she meets the criteria for the accelerate function, and therefore accelerates as well.

While the physics described here aren’t anything impressive, the important thing to notice is how easy it is to add (or not add) functionality to an entity. Acceleration is baked into most physics engines, but in Runaway, it’s totally optional.

This is why I consider Runaway to be flexible. I can write extremely specific code, tailored to all kinds of specific situations, and then pick and choose which to apply to which entity.

Runaway’s current status

As of 2021, Runaway is being used in a single game: Run 1’s HTML5 port. It has all the features needed for that simple game, and a few more, but it’s also missing things I’ll need going forwards.

One of the improvements I need to make is the loading system. The current system requires pre-determined levels, such as the 50 levels in Run 1. But Infinite Mode generates levels on the fly, with difficulty based on your performance in the previous level, and that requires something more flexible.

I do hope to release the engine at some point, just not for a while. First I want to complete multiple games, including at least one not in the Run series, to be sure it’s stable and versatile enough to compete with the existing engines.

3 thoughts on “What is Runaway?”

  1. Do you know where I can play a version of run 3 that has angel missions working? I tried your website but I couldn’t get it to work.

  2. Are there any new updates on run 3 and or your hinted Run 4 possibly, also where can i play the latest version of run 3 because i know that you haven’t updated the mobile version and you have decided not to continue work on that.

Leave a Reply

Your email address will not be published. Required fields are marked *