GrayScale – dev log #1

I’ve been posting couple of GIFs around the internet from my GrayScale game but never explained how each thing works. This post was originally written at Snowkit.org. It is a good place to start, if you’re reading this then you would find some luxe-related things interesting. In fact I think I managed to create/discover at least one interesting thing about luxe that I wanted to share with you (physiiiics).

I won’t call myself an expert in programming and this post is in no way a tutorial on “how to do things“. With luxe you can find your own way of coding. I’m just sharing what I discovered and had fun with

Quick intro

GrayScale is a small game I made for GameBoy Jam last year. It was made in HaxeFlixel but I wanted more control over my code and be able to easily create lots of enemies with different abilities. After few hours of googling I stopped by a thing called “Component Entity System” and finally found luxe engine (and so far I love it!). You can read more about the project itself on my blog.

Movement & “fixed time rate

Movement is the core gameplay in here, you can walk, run, dash and jump-attack. I organized each type to extend from one Mover component so each kind of mechanics share the same base (which is really thin so far). With components it’s easy to make characters controlled by player or AI and make them move by similar rules. I can make new enemies that walk around and dash-attack the same way that player does. Movement can be basically used for moving any kind of objects around the world. This way I could use it to move projectiles, doors, platforms etc.

Problems

The first thing I noticed when testing web and native builds was that on native build updates happened much faster than on web. This caused any kind of movement look sluggish in the browser.

Lesson learned #1: Don’t move stuff on update().

Setting fixed_rate = 1/60 on every entity almost did the trick. Now my character moves at the same speed but when the scene becomes more complex, core updates take more time -> my movement calculations are delayed and in effect character moves slower again.

Lesson learned #2: Don’t move stuff onfixedupdate() (unless it suits your needs!)

After consulting Sven on our Snowkit Gitter chat I was directed to take a look at luxe.PhysicsEngine. Now I could directly control when everything is calculated, but I had to make some kind of connection between MyPhysics engine and every mover component in the game. I’m not relying components update() or onfixedupdate() anymore…

Movers

Each mover should be then responsible to add itself to (or remove from) the list of components in PhysicsEngine. The engine then would call step(dt) functions on every component it knows. So instead of using mover’s override function update(dt:Float), I’m calling function step(dt:Float) from the engine.

Mover is basically a glue between character and physics engine. Example code below:

class Mover extends Component  
{
    // Mover can only be added to Actors
    // This shall be direct & typed reference
    var actor:Actor;

    // Override this to add your own tricks
    public function step(dt:Float) {}

    override function onadded()
    {
        actor = cast entity;
        if(actor == null) {
            throw "Mover belongs on an Actor instance";
        } //Actor test

        // Add mover to the list, so it can receive `step(dt)` calls
        Director.physics.add(actor);

        // Notify Actor
        actor.add_mover(this);
    }

    override function onremoved()
    {
        // Notify physics engine that mover is trying to drop from the list
        Director.physics.remove(actor);

        // Notify an Actor too
        actor.remove_mover(this);

        actor = null;
    }
}

So in order to create for example a Walk component:

  • extend Mover
  • override step()
  • add all the other things like maxspeed, acceleration, depending on the movement type

Step function is also important because it directly modifies Actors velocity and only that. One Actor can have many movers and each can be merged together. Actor itself also receives physics engine step() call and after updating each mover it applies velocity to its position and sets it back to (zero,zero,zero). (I’ll talk about Actors below).

Physics engine timing

Standard PhysicsEngine is using the same kind of timing you can find in Entity. Setting fixed_rate = 1/60 doesn’t mean it will always run each update after exactly 0.01(6) ms. Of course it would be enough for most of the time but again: more complex scenery (or slower PC, why not) -> frames drop -> movement calculation are delayed. I started testing it against Luxe.core.current_time to see what I could do. I calculated how much time passed since the last physics update and if it was more than 1/60 ms then I just forced it to call mover.step(dt), multiple times.

Stress testing physics with maaaaany dummies running around. I’m spamming spawning tens walkers when frames drop ugly. Each walker runs a behavior tree of wait/walk sequence with collision testing. I can’t count how many of them are out there.

Stress testing physics with maaaaany dummies running around

The other way of doing it would be calling mover.step(dt) only once with much bigger delta, but that would cause characters to move through the walls… Probably? But still, we have more control over the code with luxe. I’ll just leave it like it is until (if) I find something ugly happening around it later.

Final lesson learned: luxe.PhysicsEngine! It’s yours to extend, you are its master and you may do with it as you will * in Elohim voice * cough.

Actor – my new entity type

Actor what? Let me explain.

Requirements for this game were:

  • pixel style graphics – camera is zoomed 2 or 3 times. It’s possible for entities to appear as if they were placed in half-pixel location during movement. I hate when it happens (pixel world looses its pixeliness) so each actor needs to “snap” to the pixel grid. See the ugly pixels between his legs? SEE?
  • pseudo-3D location – in top-down view any character can be located in basic x/y position. Some characters can jump up to avoid being hit or attack from the air (ground pound!). This can be achieved by utilising unused Z axis in position vector. Adding shadow for each entity at base position.z=0 will help player recognise if the thing is flying or standing on the ground near him. Example of jumping with shadow below:

Every entity placed in the world needs to have that functionality and properties so I thought that I could just create new class, extend luxe.Visual and add those in.

Why not use compnents with all those additional functions/fields? I could just create SnapToGrid and Location3D components but I wanted every static, moveable, playable, visible entity to share the same characteristics without needing to remember to add each component everytime I create new kind of actor.

So, each actor has properties like:

  • pos:Vector – position of an actor snapped to pixel grid (It’s basically pos.x = Math.round( realPos.x );).
  • realPos:Vector – “true” position is still perserved in here so it can move very slowly, half pixel at the time. Without it, an actor wouldn’t move at all if velocity was really low.
  • velocity:Vector – current directional velocity.
  • acceleration:Vector – constant force to be added to velocity (local to the actor, gravity is set in physics engine).
  • force:Vector – one-time push, will be added to velocity and reset back to zero each step

Additionally, a couple of functions to:

  • make sure that it won’t go underground by keeping z >= 0 (should probably move that to separate component, I like the idea of underground worms comming at you )
  • update geometry vertices – in case of jumping, x and y of the actors position are kept the same. Position of vertices are in this case affected by position.z, so that actor’s square looks like flying up and down.
  • applyForce( v:Vector ) with event listeners in case if any other actor wants to push this one away.

BTW, The name “Actor” came to me after playing around with Unreal Engine, which is also component-based game engine. Actor in UE is anything visible/movable/playable in the world that accepts components, so the name just stuck to me naturally. It can be a character, a tree or even bullet.

AI

Haxe Behavior Trees made by @whuop are working great! So far I managed to use it only once though:

  • wait (random time)
  • pick random direction
  • walk straight (random time)

Each red guy is using player’s walk/run component for movement, and this again is why I love component-entity system!

Director – the Singleton

This is probably completely against the whole component-entity system and the whole OOP, but what the hell! This one is ba a cry for help or constructive criticism.

Director knows everything about the game world and physics simulation. I couldn’t find any simple way of accessing tiles from a collider component. Tilemap data was placed in class Level and colliders were attached on Sprites and I couldn’t figure out how to create any direct reference between them. First thing that came to my mind was a singleton. At the begining I made it contain a reference to the currently displayed level (with tilemapdata, layers, tilesets). Later I also added reference to the custom physics engine, so every component can add itself to the list if needed, but more on that later.

I still don’t know if having a singleton around is good idea, but so far It’s doing the job. I can only imagine that it’ll become one crouded piece of multi-purpose functions.

Shaders

I don’t really have anything useful to say about shaders yet…

Shader fail

End of transmission

I still didnt say anything about collisions! That would find its place in the next dev log. Until then I still have some things to do on my list:

  • enemies – something that would actually try to attack you
  • damage/health – obviously every game needs an END state
  • shaders – 2D black holes and wormholes are in my plans so that would probably require me to get some shader whichcraft knowledge
  • world editor – a map that glues all the levels together into something traversible and fun to explore

Okay, I’m done for today. Thanks for reading, you’re awesome!