Movement Physics of CrossCode

cc_physics_thumbSome people (actually, just one person) asked us on how we modelled the movement physics of CrossCode, e.g. how does the keyboard input influence the movement speed of the player.

Thus, in this post we’ll write about CrossCode’s movement physics.

First of all: These physics are not modelled after reality but rather on how well it played out.

Each movable entity in CrossCode has the following value to describe it’s physical state (written in JavaScript syntax):

{
    // the current position of the entity in 3 dimensional space:
    pos: { x: 0, y: 0, z: 0), 
    // the current velocity in 3 dimensional space:
    velocity: {x: 0, y: 0, z: 0), 
    // the current direction of acceleration:
    accelDir: {x: 0, y: 0, z: 0), 
    // the maximum velocity the entity can achieve through acceleration:
    maxVel: 300, 
    // The speed of acceleration
    accelSpeed: 1.0,
    // the friction that determines how fast the entity will brake on ground:
    friction: 1.0, 
    // airfriciton, same as friction, only valid when entity is above the ground
    airFriction: 0.2
}

About the coordinates: CrossCode has 3 dimensional coordinates, whereas the x and y coordinate are the ground coordinates and z the height coordinate (positive z means above ground).

The movement physics described here only modify the ground coordinates of the entity. We won’t cover the height coordinate modification in this post. (Just write us, if you want to know about these as well).

The genereal idea about the physics is (still similar to impact.js), that entities only modify their velocity directly or indirectly via accelDir, maxVel and friction. The resulting velocity is used to update the entities position, considering collision with the surrounding.

Here is how the accelDir, maxVel and friction determine the velocity:

First of all, all computations depend either on normal friction or airFriction, if the entity is above ground:

var friction = [entity above ground] ?  this.airFriction : this.friction;

We compute the final acceleration vector using accelDir, accelSpeed, maxVel and friction like so:

var accelDir = Vec2.mulF(this.accelDir, this.maxVel * 10 * friction * this.accelSpeed * ig.system.tick, true);

Note, we are using custom functions for vector operations like Vec2.mulF(). I posted those functions here. Also, ig.sytem.tick is the time interval of the current frame (as known from impact.js) and 10 some magic number to balance the friction input value.

So, what we got is an entity with a current velocity and acceleration:

post1_gfx1

Next we compute the friction vector that is used to reduce the current acceleration:

var frictionVel = Vec2.mulF(this.vel, friction * 12 * ig.system.tick, true);

Note: 12 is another some magic number to balance the impact of the friction input value.

We get the following vector:

post1_gfx2

Now comes an interesting step. We will trim down any friction that is in the direction of the current acceleration. That way, the friction won’t slow down acceleration too much and still reduce orthogonal velocity fast enough to move the entity in the direction of the acceleration. Note: we skip this step, if the current velocity is faster than maxVel or in the opposite direction of accelDir. In that case we do want to reduce the speed of velocity, so no friction is trimmed.

    var velLength = Vec2.length(this.vel);
    if(velLength <= this.maxVel){
        var dot = Vec2.dot(this.accelDir, frictionVel);
        if(dot >= 0){
            Vec2.sub(frictionVel, Vec2.mulF(this.accelDir, dot, true));
        }
    }

post1_gfx3

Now, we will apply the acceleration.

this.vel.x += accelDir.x ;
this.vel.y += accelDir.y ;

post1_gfx4

The length of the resulting velocity is reduced to be the maximum of minVel or the previous length (in case the previous velocity was faster than maxVel – remember that maxVel is only the maximal velocity we achieve with acceleration, so it should not apply if the velocity has been already faster than maxVel before applying acceleration).

Vec2.limit(this.vel, 0, Math.max(velLength, this.maxVel));

post1_gfx5

Finally, we reduce the velocity by the previously computed friction:

this.vel.x -= frictionVel.x;
this.vel.y -= frictionVel.y;

post1_gfx6

And that’s the whole thing.

This algorithm might seem weird and not very intuitive. That’s because we just adapted the code until the resulting movement felt right.

Feel free to try the same thing for your game. It might just work for you as well!

12 Comments

Post a Reply to Lachsen Cancel reply

Your email is kept private. Required fields are marked *