Making a game in less than 13kb part 3 – final

This is the final part of how Shield Maiden was made. This was a really fun experience, really enjoyed making this game. My only regret was due to js13kGames and Ludum Dare  being the same month I had to split my time between the them. Really nothing I could do about it.

Now, back to the making of.

Music

This was the first item I worked. From my past experience, music can consume quite a lot of the precious 13 kb, so I researched for ways to generate the music at running time. I did found something really interesting, a post that claimed to use just 140 bytes to generate music. I was amazed! Music in just 140 bytes by evaluating functions. Spent a lot of time playing with this new knowledge and this was the thing that kept me interested. If music could be generated in only 140 bytes, 13kb was more than enough to make a full game!

I just added the player, hide it from the user and autostart it on load.

Sound Effects

As with music, having a file for each sound effect consumes a lot of precious space. Fortunately, Jack Rugile had already found a way around it by using as3fxr + jsfxr. By using as3fxr generated some sounds and saved them in a js object:

var sounds = {
 "test" : [0,,0.1812,,0.1349,0.4524,,0.2365,,,,,,0.0819,,,,,1,,,,,0.5],
 "start" : [0,,0.0911,0.4779,0.434,0.5016,,,,,,0.3504,0.6396,,,,,,1,,,,,0.5],
 "dirt" : [3,,0.1337,0.3709,0.0896,0.0379,,0.2476,,,,,,,,0.4619,0.1879,-0.1484,1,,,,,0.5],
 "hp" : [0,,0.3722,,0.407,0.3215,,0.4808,,,,,,0.2295,,0.6552,,,1,,,,,0.5],
 "horse" : [0,,0.3239,,0.4899,0.3072,,0.1272,,,,,,0.2305,,0.7536,,,1,,,,,0.5],
 "treasure": [1,,0.3429,,0.2116,0.2116,,0.2249,,,,,,,,,,,1,,,,,0.5],
 "enemy" : [1,,0.0919,,0.2654,0.5705,,-0.3563,,,,,,,,,,,1,,,,,0.5],
 "move" : [1,,0.0458,,0.1652,0.442,,0.1422,,,,,,,,,,,1,,,,,0.0]
}

Create the player:

var player = new Audio();
player.src = jsfxr(sounds.start);

And then just update the src with the desired sound effect and play it:

player.src = jsfxr(sounds.start);
 player.play();

Menus

After the main part of the game was made, it was time to make it look more like a regular game with an ending/starting screen. Not much space left and making a complete image for them was not feasible. I had to use in some way what I already had. I will use the same tiles to make pixel-like text. Used google docs spreadsheet to create the grid and “draw” on it:

Screen Shot 2014-09-17 at 9.41.08 PM

Exported then to CVS and formatted into a 2-D js array:

var shield_maiden_title = [[0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8],
 [0.8,0,0,0,0.8,0,0.8,0,0.8,0,0.8,0,0,0.8,0,0.8,0.8,0,0,0.8,0.8,0.8],
 [0.8,0,0.8,0.8,0.8,0,0.8,0,0.8,0,0.8,0,0.8,0.8,0,0.8,0.8,0,0.8,0,0.8,0.8],
 [0.8,0,0,0,0.8,0,0,0,0.8,0,0.8,0,0,0.8,0,0.8,0.8,0,0.8,0,0.8,0.8],
 [0.8,0.8,0.8,0,0.8,0,0.8,0,0.8,0,0.8,0,0.8,0.8,0,0.8,0.8,0,0.8,0,0.8,0.8],
 [0.8,0,0,0,0.8,0,0.8,0,0.8,0,0.8,0,0,0.8,0,0,0.8,0,0,0.8,0.8,0.8],
 [0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8],
 [0,0,0,0,0,0.8,0,0,0,0.8,0,0.8,0,0,0.8,0.8,0,0,0.8,0,0,0],
 [0,0.8,0,0.8,0,0.8,0,0.8,0,0.8,0,0.8,0,0.8,0,0.8,0,0.8,0.8,0,0.8,0],
 [0,0.8,0.8,0.8,0,0.8,0,0,0,0.8,0,0.8,0,0.8,0,0.8,0,0,0.8,0,0.8,0],
 [0,0.8,0.8,0.8,0,0.8,0,0.8,0,0.8,0,0.8,0,0.8,0,0.8,0,0.8,0.8,0,0.8,0],
 [0,0.8,0.8,0.8,0,0.8,0,0.8,0,0.8,0,0.8,0,0,0.8,0.8,0,0,0.8,0,0.8,0],
 [0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8],
 [0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8],
 [0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8,0.8]
 ];

Then draw the tiles just the same as the background and use the 2-d array as the alpha values:

for (var r = 0; r < canvas.height/tileSize; r++) {
   for (var c = 0; c < canvas.width/tileSize; c++) {
       var tile = hardWallIndex;
       var tileRow = (tile / imageNumTiles) | 0;
       var tileCol = (tile % imageNumTiles) | 0;
       ctx.drawImage(tilesetImage, (tileCol * tileSize), (tileRow * tileSize), tileSize, tileSize, ((c) * tileSize), ((r) * tileSize), tileSize, tileSize);
       ctx.fillStyle = "rgba(0, 0, 0,"+shield_maiden_title[r][c]+")";
       ctx.fillRect(c*tileSize, r*tileSize, tileSize, tileSize);
   }
}

Procedural World Generation

During the development of Shield Maiden found the awesome site jsfiddle. In this site you can write code, import libraries and run in the same page. It is great.

I had the idea of using cellular automata for cave type levels and found this. It was almost what I had in mind, so I read the code to understand how it worked and adapted it to what I required.


 

Participating in js13kGames was a great experience and I learned a lot. Having a whole month was a nice time frame to program with not a ton of pressure like most of the game jams I have participated. I did had fun and enjoyed showing friends and coworkers what can be made with just 13kb. Looking forward for the next year competition!

I hope this explanation on the parts that made up the game is a nice complement to the source code in my github. Also you can take a look at the game in the js13kgames site.

Making a game in less than 13kb part 2

In the last post we covered the basic parts of the game, and now we’ll cover more datailed parts

Camera System

Bvhydb6IYAACal5

Adding a camera really improved the game. It allowed for bigger maps and more content. This was a particular item that gave me a headache, it seemed simple but it was difficult for me to implement. After some time, it turns out to be quite simple. It may not be optimised, but it did the job.

The object representation will not change, only the drawing is affected. Based on that a new object was created to hold the camera data:

var camera = {
 x:0,
 y:0
}

Then in the update method, just update the camera (x,y) to follow the hero and always center it on the screen:

 camera.x = hero.x - Math.floor((canvas.width/tileSize)/2);
 camera.y = hero.y - Math.floor((canvas.height/tileSize)/2);

Then to draw the hero, we have to take into account the hero position:

var drawHero = function(){
     ctx.drawImage(tilesetImage, (3 * tileSize), (2 * tileSize), tileSize, tileSize, (hero.x - camera.x)*tileSize, (hero.y-camera.y)*tileSize-10, tileSize, tileSize);
}

The same for enemies:

ctx.drawImage(tilesetImage, (col * tileSize), (row * tileSize), tileSize, tileSize, (enemy.x -camera.x)*tileSize, (enemy.y-camera.y)*tileSize-10, tileSize, tileSize);

And terrain:

ctx.drawImage(tilesetImage, (tileCol * tileSize), (tileRow * tileSize), tileSize, tileSize, ((c - camera.x) * tileSize), ((r - camera.y) * tileSize), tileSize, tileSize);

And that is it! We only had to offset the drawn elements according to the camera (x,y) position. Quite simple, but also quite confusing at first. You have access to the complete code at github and you can check out the differences of how it was implemented, in particular check this commit number.

Fog of War

BwZQOb3CEAEBQqN

The fog adds a nice effect, a more dungeon feel. It also focus attention on the player which is quite nice. All the content is specified in tiles and drawn from a matrix. For this FoW we just create a new matrix to hold the alpha values, and then draw it over the current canvas.

We create an empty array:

var generateFOW = function (){
 var FOW = [];
 for (var r = 0; r < rowTileCount; r++) {
     var column = [];
     for (var c = 0; c < colTileCount; c++) {
         column.push(0);
     }
     FOW.push(column);
 }
 return FOW;
}

Then we use something called the manhattan distance, which sounds a lot more complex in the wikipedia article. basically you calculate the distance in x-axis and then in y-axis and add them both. We use several manhattan measures to create a gradient with different alphas:

 for (var r = 0; r < rowTileCount; r++) {
     for (var c = 0; c < colTileCount; c++) {
         var xDistance = Math.abs(hero.x-camera.x-c);
         var yDistance = Math.abs(hero.y-camera.y-r);
         var manhattanDistance = xDistance + yDistance;
         if (manhattanDistance < 3){
             FOW[r][c] = 0
         }
         else if (manhattanDistance < 4){
             FOW[r][c] = 0.3
         }
         else if (manhattanDistance < 5){
             FOW[r][c] = 0.5
         } else{
             FOW[r][c] = 0.7
         }
     }
 }

Finally we draw on the canvas, using the previously calculated values:

for (var r = 0; r < rowTileCount; r++) {
    for (var c = 0; c < colTileCount; c++) {
    ctx.fillStyle = "rgba(0, 0, 0,"+FOW[r][c]+")";
    ctx.fillRect(c*tileSize, r*tileSize, tileSize, tileSize);
    }
}

 

Enemies

They are a huge part of videogames, always having someone to try stop the heroe’s dream. And for that we need them to be intelligent. At first were too intelligent and they always knew where you were. Later a restriction of distance was added to avoid having a big bunch of angry  centaurs following you.

//Just follow player if near
 if (manhattanDistance >= 15) return;

Enemies only move after the player moves and we move them in the order they were pushed into an array. Each enemy with try to get into contact with the player at a manhattan distance =1 in order to attack:

 if (manhattanDistance == 1){//Attack player directly
     hero.hp -= enemy.attack;
     return;
 }

Or just move closer by one square in x-axis or y-axis:

var row = enemy.y + 1;
var column = enemy.x;

They are not too smart, but they present a threat to the player is the enemies have high attack and high health.

 

Destructible terrain

I really like destructible terrain, when I started coding at first I just spent time making my own tunnels. Having destructible terrain add more choices to the player. Dig and lose this turn movement or just take a different longer path. When followed by an enemy the choice matters.

In the 2-d array we hold the data if there is dirt or is a clear path. When the player or enemy wanted to dig we just check if the wall is destructible and if yes, we replace the value of the destructible wall with a zero:

if (isDestructibleWall(row,column)){
   topLayer[row][column] = 0;
} else{
   //Do something if required
 }
var isDestructibleWall = function(row, column){
   var tile = topLayer[row][column];
   if (tile == destructibleWallIndex){
     return true;
   }
   return false;
}

Simple, but effective.


 

For the last part of the tutorial (part 3), I will cover the music and sound effects and how the menus were drawn.

Remember you can access the complete code in my github. And take a look at the game in the js13kgames site.

Making a game in less than 13kb part 1

Making a game is quite a challenge, making it in less than 13kb is insane. Here are all the details on my game Shield Maiden and how it was made. My first idea of game was a rogue-like and since I had never made one before I decided to go for it.

Game loop

Since April I have been developing in JavaScript, using Phaser as engine. This time, not even Phaser could be used for the engine.  A game loop was one of the minimal requirements, at least the update and render states.

Did a little research and found information about how to implement it in an article.

1. Use canvas as the drawing surface and attach the logic in the game.js script

<!DOCTYPE html>
<html>
<head lang="en">
   <title>A JS game in under 13kb</title>
</head>
<body>
   <canvas id=myCanvas width="704" height="480"></canvas>
   <script src="game.js"></script>
</body>
</html>

This is a minimal index.html with just the canvas and the game.js script

2. Request animation frame(In the game.js script):

var main = function () {
    var now = Date.now();
    var delta = now - then;
    update(delta / 1000);
    render();
    then = now;
    requestAnimationFrame(main);
};
var reset = function () {
    //Setup of the game world, initialize variables, etc...
};
var update = function (delta) {
    //Update of the game world
};
var render = function () {
    //Here is where we draw to canvas
};
var w = window;
requestAnimationFrame = w.requestAnimationFrame || w.webkitRequestAnimationFrame || w.msRequestAnimationFrame || w.mozRequestAnimationFrame;
var then = Date.now();
reset();
main();

Those are the most basic parts. The delta allows for ajustments, but since the game here is tile based and the actions are triggered by user input, I could just ignore it. In the requestAnimationFrame, there are several logic OR (||) for cross-browser support.

Capture Keyboard Input

After getting to work the game loop the next step was to capture the arrow keys.

1. Adding listeners for keyup/keydown and track of press status

var playerHasMoved = false;
var canPressKey = true;
var keysDown = {};
addEventListener("keydown", function (e) {
    if (canPressKey){
        playerHasMoved = false;
        keysDown[e.keyCode] = true;
        canPressKey = false;
    }
}, false);
addEventListener("keyup", function (e) {
    delete keysDown[e.keyCode];
    canPressKey = true;
}, false);

Shield Maiden requires 5 keys to be captured, the ARROWS plus the X key. Also a requirement was to only make an action after a full key down to key up. An action will only occur when pressing a key, and to trigger it again the key would have to be pressed again. This is accomplished with the canPressKey flag.

2. Update game according to keys

var KEY_UP = 38;
var KEY_DOWN = 40;
var KEY_LEFT = 37;
var KEY_RIGHT = 39;
var KEY_X = 88;
var update = function (delta){
    if (KEY_X in keysDown){
        //Do something when X is pressed
    }
 }

Inside the update function we can keep track of the key presses and do actions according to them, like move left when the user pressed the left arrow.

World Representation

To store the world map a 2-d arrays are used:

var topLayer = [[]];
var groundLayer = [];

The groundLayer was used to draw the background images and the topLayer is where all the game elements are placed: Destructible rocks, enemies, player and items.

A small example of a generated layer:

var layer = [
 [0,0,0,0,0],
 [1,0,0,0,1],
 [1,0,0,0,1]];

Then by assigning special meaning to each number we can draw each of the tiles. Then in the render method draw according to the index of the array.

Specifying the game size in variables helps to always use the same values and be able to change them fast if you require.

var tileSize = 32;
var rowTileCount = 48;
var colTileCount = 48;

Originally the game width/height was fixed and assigned the size by using canvas.height/tileSize and having a canvas size multiple of the tileSize.

Drawing in Canvas

To draw in the canvas we use the drawImage function, the regular function is:

context.drawImage(img,x,y);

This will draw the entire img in the x,y position, but for our purposes we use a tile map with several images(to save space):

js13k

We use a different drawImage function:

context.drawImage(img,sx,sy,swidth,sheight,x,y,width,height);

This allows us to specify the image and just a portion of the image we want to draw, then draw it in the x,y position. Read more in wc3school. It is important to clear the canvas if you are not completely redrawing it using:

context.clearRect(0,0,canvas.width, canvas.height);

It is quite an expensive task, but for the purpose of this game it seemed fine, so no optimization was done.

Using the drawImage function we can now draw the background without problem:

var imageNumTiles = 4;//Tiles per row
for (var r = 0; r < rowTileCount; r++) {
    for (var c = 0; c < colTileCount; c++) {
        var tile = groundLayer[ r ][ c ];
        var tileRow = (tile / imageNumTiles) | 0; 
        var tileCol = (tile % imageNumTiles) | 0;
        ctx.drawImage(tilesetImage, (tileCol * tileSize), (tileRow * tileSize), tileSize, tileSize, (c * tileSize), (r  * tileSize), tileSize, tileSize);
    }
}

Having our map in the 2-d array, we hace rows and columns so we iterate over all the values and according to the value stored in the array we pick the correct portion of the image to draw.

Each of the values represent the tile number.

js13

Using the example:

var layer = [
 [0,0,0,0,0],
 [1,0,0,0,1],
 [1,0,0,0,1]];

We would be drawing only the clear tile and the floor tile. By adding more layers, we can draw the enemies and items. We can also move them by changing the content of the layer. For example, the Blue Centaur is number 7 and is placed at the top left:

var layer = [
 [7,0,0,0,0],
 [0,0,0,0,0],
 [0,0,0,0,0]];

To change the position is just a matter of updating the 7 position in the array:

var layer = [
 [0,0,0,0,7],
 [0,0,0,0,0],
 [,0,0,0,0]];

Now the Blue Centaur is in the top right.


 

That was quite a lot of information, but it covers the basis for the game, the rest of the features are just improvements over the existing game logic that would be addressed in part 2 of this post.

Remember you can access the complete code in my github. And take a look at the game in the js13kgames site.