Week 7 and 8, Adding physics and render the level in html5 games

back to top

excerpt picture

Two weeks have been necessary to add all these functionalities to the html5 game.

  1. Add physics : done
  2. Add tilemap rendering : done
  3. Collision management : done
  4. Rendering with different viewport dimensions: done

Adding physics

Adding physics was the first step, I added acceleration to the player, including gravity which is a negative y acceleration. This follow the github tutorial which was really well explained. It was not so difficult to transform it to javascript as below :

First input for jumping, the loop checks if you are pressing the jump button, the more you press it the higher is your jump :

if(8===(input & 8)) {//1000 JUMP
        jumpPressedTime++;
        if (PlayerState.JUMPING!=state) {
            jumpingPressed = true;
            jumpPressedTime = 0;
            state = PlayerState.JUMPING;
            entity.dy = MAX_JUMP_SPEED;
            entity.grounded = false;
        } else {
            if (jumpingPressed && jumpPressedTime >= LONG_JUMP_PRESS) {
                jumpingPressed = false;
            } else {
                if (jumpingPressed) {
                    entity.dy = MAX_JUMP_SPEED;
                }
            }
        }   
    }

Then we check the left/right actions to add positive or negative acceleration :

if(2===(input & 2)) {//0010 LEFT        
    facingLeft=true;
    if (PlayerState.JUMPING!=state) {
        state = PlayerState.WALKING;
    }
    accX = -entity.accel;
} else  if(1===(input & 1)) {//0001 RIGHT
    // right is pressed
    facingLeft=false;
    if (PlayerState.JUMPING!=state) {
        state = PlayerState.WALKING;
    }
    accX = entity.accel;
} else {
    if (PlayerState.JUMPING!=state) {
        state = PlayerState.IDLE;
    }
    accX = 0;
}

After we manage inputs, we add gravity and change the velocity

// Setting initial vertical acceleration 
accY = entity.gravity ;


// apply acceleration to change velocity
entity.dx += accX;
entity.dy += accY;

This is it, the gravity and ths user inputs have been added in the player update loop.

Adding tilemap rendering

Tilemap was not a big deal to render, the difficulty was to adapt it to different viewports. The Tilemap was done with Tiled software, and exported in JSON file description. The file was then loaded with an XHtmlRequest as we did for adding audio

for(y = 0 ; y < MAP.th ; y++) {
  for(x = 0 ; x < MAP.tw ; x++) {
    cell = tcell(x, y);
    if (cell) {
      // context.drawImage(img,sx,sy,swidth,sheight,x,y,width,height);
        context.drawImage( lvl_img, lvl_json.tilewidth*(cell-1) , 0, lvl_json.tilewidth, lvl_json.tileheight, 
        (x*TILE-cam.getPos().x)*ScreenRatio,(RefGameHeight+(y-MAP.th)*TILE-cam.getPos().y)*ScreenRatio,TILE*ScreenRatio , TILE*ScreenRatio);

    }
  }
}

As you can see, the cells are rendered in a postiion which depend of the ScreenRatio value and also of the cam.getPos(). All rendering is based on a camera class which represent a square around the player.

The camera claas tracks the player position to update its position, the dimensions of the camera are also related to ScreenRatio value...

Collision management

Collision has not yet been improved, yet it's basically a check of intersection between a boundary rectangle around the player, and a boundary rectangle around all other entities.

All entities have an associated rectangle :

collisionRect= {
  x:   MovableEntities[x].bounds.x,
  y:    MovableEntities[x].bounds.y,
  width:  MovableEntities[x].bounds.width,
  height: MovableEntities[x].bounds.height
};

Then all rectangle checks if it intersects with others :

function intersect(a, b) {
    return !(a.x > b.x+b.width || 
    a.x+a.width < b.x || 
    a.y+a.height < b.y ||
    a.y > b.y+b.height);
 }

It's a lot of calculation which can be reduced by not checking the intersections with the rectangles which obviously cannot collide with the player.

Rendering with different viewport dimensions

As JS stay a nightmare in terms of floating calculation, I decided to fix the ratio between the reference height and the user height.

for(ratio = 0.1;ratio<2;ratio+=0.1) {
    if(gameheight>=RefGameHeight*ratio) 
        ScreenRatio=ratio.toFixed(2);
}

That helped a lot as all positions and dimensions for rendering are related to ScreenRatio value.

Next week, I'm going to include all the game mechanics like death, gameover, and reset so ...

Let's code!

Written on October 30, 2015