JS & Canvas from scratch

Post all your tuts or request for tuts here.
Post Reply
User avatar
a_bertrand
Posts: 1537
Joined: Mon Feb 25, 2013 1:46 pm

JS & Canvas from scratch

Post by a_bertrand »

You will find here a work in progress of a tutorial, which should hopefully grow up to a full (maybe minimal) iso game written without any engine.

Latest result is visible here:
http://temp.nowhere-else.org/iso_game/

Why not use any engine? First of all, because you can start with just notepad.exe or vi. And also to show that you can do a lot with your own skills. Finally, understanding how things works do help to code better, and may help you in case you need to edit an existing engine or write your own game.

So let's start. Personally I code JS with the help of webstorm, however you can use whatever text editor you want, from the most basic one to eclipse or visual studio. Up to you.

Create a directory on your computer, and let's call it: iso_game, inside this directory we will create the files required to make our game. Nothing too fancy mind you, but still something cool.

Go inside the iso_game directory and create an empty file called index.html then edit it with your preferred editor:

Code: Select all

<!DOCTYPE html>
<html style='height: 100%; width: 100%;'>
<head>
<title>My ISO Game</title>
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
</head>
<body style='margin: 0px;height: 100%; width: 100%;'>
Now don't jump on me already complaining there is no end to the html or the body tag. It works without, so no need here to add them. If you are not happy add them.

This is nearly the minimum to make your html work fine on most browser. We will support IE 9+, FF and Chrome. It may work on others but I will not make any support for them.

We now need something to draw on, and in HTML5 this is called a canvas:

Code: Select all

<!DOCTYPE html>
<html style='height: 100%; width: 100%;'>
<head>
<title>My ISO Game</title>
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
</head>
<body style='margin: 0px;height: 100%; width: 100%;'>
<canvas id='gameCanvas' width='600' height='600'></canvas>
Ok the canvas is now called gameCanvas... however you will still see... nothing. To draw on the canvas we need to write some Javascript. At start we will mix the JS (Javascript) with the HTML, however it's good practice to avoid it as much as possible, and keep your JS code on separated files.

Code: Select all

<!DOCTYPE html>
<html style='height: 100%; width: 100%;'>
<head>
<title>My ISO Game</title>
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
</head>
<body style='margin: 0px;height: 100%; width: 100%;'>
<canvas id='gameCanvas' width='600' height='600'></canvas>
<script>
var canvas=document.getElementById('gameCanvas');
var ctx=canvas.getContext('2d');

ctx.fillStyle='red';
ctx.fillRect(0,0,600,600);
</script>
If you save this, and open it with your browser (double click on the file), you should see a "nice" red square on your page. Congratulations you have done your first canvas HTML5 page!
Last edited by a_bertrand on Thu May 23, 2013 3:09 pm, edited 1 time in total.
Creator of Dot World Maker
Mad programmer and annoying composer
User avatar
a_bertrand
Posts: 1537
Joined: Mon Feb 25, 2013 1:46 pm

Re: JS & Canvas from scratch

Post by a_bertrand »

So what did we do with the JS?

document.getElementById('...') let you find an item in the web page (the document) by an ID defined by the ID property of any tag. With it, we recovered the canvas we added inside the HTML to be usable by JS and store it in a variable called canvas.

canvas.getContext('2d') let us grab a graphic context of 2D type. Currently only 2d is available, but maybe at some point we will have 3d as well like that. With the graphic context in hand we can do all the drawing we want.

The ctx.fillStyle and fillRect let us paint on the context. There is quiet a few graphic functions, but I will not explain them here. We will basically work only with drawImage which let us "blit" or paint an image on the context.

For the old timers of us which already coded some game, you wonder if you will need to have a backbuffer to avoid flickering, and the answer is no. The browser should take care of that for you.
Creator of Dot World Maker
Mad programmer and annoying composer
User avatar
Jackolantern
Posts: 10893
Joined: Wed Jul 01, 2009 11:00 pm

Re: JS & Canvas from scratch

Post by Jackolantern »

Looking great! It would be awesome if this does grow to even a simple isometric "game" (just a character moving around on an isometric surface with some obstacles would work great), since we get a lot of questions and a lot of requests for isometric, a type of game and/or engine I have never worked with.
The indelible lord of tl;dr
User avatar
a_bertrand
Posts: 1537
Joined: Mon Feb 25, 2013 1:46 pm

Re: JS & Canvas from scratch

Post by a_bertrand »

You are too quick to ask ;) I had to take my lunch, so I had to cut this. Anyhow let's continue.

For the next step, download the following to images and save them in a new directory images under your iso_game directory:
http://temp.nowhere-else.org/walk.png
http://temp.nowhere-else.org/blocks.png

Those images are called spritesheets, basically it's just a bunch of smaller images glued together inside a bigger one. There is a couple of reasons why to do so and not work with individual images:
- Grouping multiple images tends to save space
- Loading a bigger one is MUCH faster than loading tons of small one
- It's easier to see on the disk 1 image instead of 196 different one ;)

Code wise, it's a bit more work, but you will see it's way not hard.

We need to make something with those images, so the first step will be to load them... and then paint them. This time, I skip a couple of steps and create some helper functions however all the code should be clearly documented. If you have issues just ask and I shall try to answer:

Code: Select all

<!DOCTYPE html>
<html style='height: 100%; width: 100%;'>
<head>
<title>My ISO Game</title>
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
<meta content="utf-8" http-equiv="encoding">
</head>
<body style='margin: 0px;height: 100%; width: 100%;'>
<canvas id='gameCanvas' width='600' height='600'></canvas>
<script>
var canvas=document.getElementById('gameCanvas');
var ctx=canvas.getContext('2d');

// Stores all the sprites
var sprites={};
sprites.walk={name:'walk',width:80,height:110,columns:8};
sprites.blocks={name:'blocks',width:64,height:78,columns:10};

// Draws a sprite on the graphic context
function drawSprite(spriteSheet,sprite,x,y)
{
	// The image has not been loaded yet.
	if(spriteSheet.image == null)
	{
		// Create a new image object
		var img=new Image();
		img.src='images/'+spriteSheet.name+'.png';
		spriteSheet.image=img;
		return;
	}

	// Retreives the column on which this sprite is
	var c=sprite%spriteSheet.columns;
	// Retreive the line on which the sprite is
	var r=Math.floor(sprite/spriteSheet.columns);
	// Blit the sprite
	ctx.drawImage(spriteSheet.image,
		spriteSheet.width*c,spriteSheet.height*r,
		spriteSheet.width,spriteSheet.height,
		x,y,
		spriteSheet.width,spriteSheet.height);
}

// Will be our drawing function
function drawScreen()
{
	// Clear the screen
	ctx.clearRect(0, 0, 600, 600);
	// Place back the avatar
	drawSprite(sprites.walk,0,128,128);
}

// Handles the animation and drawing
function animate()
{
    drawScreen();

	// Firefox specific
    if (window.mozRequestAnimationFrame != null && window.mozRequestAnimationFrame != undefined)
        window.mozRequestAnimationFrame(animate);
	// W3 Standard
    else if (window.requestAnimationFrame != null && window.requestAnimationFrame != undefined)
        window.requestAnimationFrame(animate);
	// Fallback for non supported
    else
        setTimeout('animate()', 16);
}

// Start the loop as soon as possible
animate();
</script>
Our game loop is in the function animate. This function will be called either on each frame refresh (60Hz normally) or in case the browser do not support the frame animation it uses a timeout.

Why did I started already with a game loop (called animate)? Because basically I start drawing before the image is loaded. Maybe not what you always want, but in this case it will do the trick.

The most interesting function of this code is the drawSprite function, which load the image the first time you try to blit it, as well as take care of taking just the part of the sprite we want to draw. The definition of the sprite sheets are at line 17-18, and defines the size of a single sprite inside the sheet as well as how many columns a sprite sheet has.

I added also 2 meta lines in the header to avoid any kind of error message from firefox.

Something to note and learn now is that all the major browser do offer some developers tools. From a simple error console which at least show you where the bug are, to a integrated (and not all that bad) profiler like for Chrome. If something doesn't work, make sure to have those tool open and check what the browser tell you!
Creator of Dot World Maker
Mad programmer and annoying composer
User avatar
a_bertrand
Posts: 1537
Joined: Mon Feb 25, 2013 1:46 pm

Re: JS & Canvas from scratch

Post by a_bertrand »

It's time to add the drawing of the background behind the avatar, so we could start trying to replace the drawScreen function:

Code: Select all

// Will be our drawing function
function drawScreen()
{
	// Clear the screen
	ctx.clearRect(0, 0, 600, 600);
	
	// draw the world
	for(var sy=-40, row=0;sy < 640;sy+=78,row++)
	{
		for(var sx=-32;sx < 632;sx+=64)
		{
			drawSprite(sprites.blocks,0,sx,sy);
		}
	}
	
	// Place back the avatar
	drawSprite(sprites.walk,0,128,128);
}
There is many ways to render the map on the screen. In this function I simply have a double loop for the X and the Y coordinate and blit a block on the screen. Yet... If you try it, you will see that it doesn't look ok, for a couple of reasons:
- Each odd row should have an offset to fill the gap
- And we see the blocks in 3D instead of having a nice flat surface

For the odd rows, we need to use the row number, while for the 3D look, we simply need to reduce the step in the Y coordinate:

Code: Select all

// Will be our drawing function
function drawScreen()
{
	// Clear the screen
	ctx.clearRect(0, 0, 600, 600);
	
	// draw the world
	for(var sy=-40, row=0;sy < 640;sy+=22,row++)
	{
		for(var sx=-32;sx < 632;sx+=64)
		{
			var rsx=sx+(row%2==0?0:32);
			drawSprite(sprites.blocks,0,rsx,sy);
		}
	}
	
	// Place back the avatar
	drawSprite(sprites.walk,0,128,128);
}
Creator of Dot World Maker
Mad programmer and annoying composer
User avatar
a_bertrand
Posts: 1537
Joined: Mon Feb 25, 2013 1:46 pm

Re: JS & Canvas from scratch

Post by a_bertrand »

Of course the map is totally useless, as it's just a plain blue background. Don't worry we will do something for it too. For the moment let's try to make the player walk over it as we have all the animation required (maybe not a very good one but that should be enough).

To make the player walk you need to store some information about the position of the player, the animation step, and handle the keyboard (we could do it via mouse too, but keyboard support is not bad for such a game).

The first things any JS developer would do for keyboard support would be the key press event, but actually that would be a big mistake. It's MUCH better to use the key down / key up events as they will allow to implement the keyboard handling in a better way. Let's add 2 new functions to our code:

Code: Select all

var keyPressed = new Array();
// Handles key down event
function keyDown(e)
{
	// Event handling for IE
    e = e ? e : event;

    keyPressed[e.keyCode] = true;
	// IE
	try
	{
		e.cancelBubble = true;
		e.returnValue = false;
	}
	catch (er)
	{
	}
	// Firefox
	try
	{
		e.preventDefault();
		e.stopPropagation();
	}
	catch (er)
	{
	}
	return false;
}

// Handles key up event
function keyUp(e)
{
	// Even handling for IE
    e = e ? e : event;
    keyPressed[e.keyCode] = false;

	// IE
	try
	{
		e.cancelBubble = true;
		e.returnValue = false;
	}
	catch (er)
	{
	}
	// Firefox
	try
	{
		e.preventDefault();
		e.stopPropagation();
	}
	catch (er)
	{
	}
	return false;
}
Each of those 2 function make sure to block what's called even bubbling or event propagation. You don't want that some keys are also interpreted by the browser (like backspace which go back in the history).

To let the browser use them, add 2 rows at the bottom of the script after or before the animate() call:

Code: Select all

document.onkeydown = keyDown;
document.onkeyup = keyUp;
Yet this doesn't do much. It just stores in an array if a key is pressed or not. That's it. But we should instead react to the keys right? But that's not all, we need to show somehow the difference on the screen. To show that the player is actually moving around. For that we need to edit again the drawing function, and adjust the the "offsets" based on the player position. Here you will get the whole code:

Code: Select all

<!DOCTYPE html>
<html style='height: 100%; width: 100%;'>
<head>
<title>My ISO Game</title>
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
<meta content="utf-8" http-equiv="encoding">
</head>
<body style='margin: 0px;height: 100%; width: 100%;'>
<canvas id='gameCanvas' width='600' height='600'></canvas>
<script>
var canvas=document.getElementById('gameCanvas');
var ctx=canvas.getContext('2d');

// Stores all the sprites
var sprites={};
sprites.walk={name:'walk',width:80,height:110,columns:8};
sprites.blocks={name:'blocks',width:64,height:78,columns:10};

// Draws a sprite on the graphic context
function drawSprite(spriteSheet,sprite,x,y)
{
	// The image has not been loaded yet.
	if(spriteSheet.image == null)
	{
		// Create a new image object
		var img=new Image();
		img.src='images/'+spriteSheet.name+'.png';
		spriteSheet.image=img;
		return;
	}

	// Retreives the column on which this sprite is
	var c=sprite%spriteSheet.columns;
	// Retreive the line on which the sprite is
	var r=Math.floor(sprite/spriteSheet.columns);
	// Blit the sprite
	ctx.drawImage(spriteSheet.image,
		spriteSheet.width*c,spriteSheet.height*r,
		spriteSheet.width,spriteSheet.height,
		x,y,
		spriteSheet.width,spriteSheet.height);
}

// Will be our drawing function
function drawScreen()
{
	// Clear the screen
	ctx.clearRect(0, 0, 600, 600);
	
	// Sub tile position
    var subX = playerX % 32;
    var subY = playerY % 32;

    // Sub offset to shift X or Y depending on the player position
    var offX = Math.floor((-subX) - subY);
    var offY = Math.round(subX * 22 / 32 - subY * 22 / 32);
	
	// draw the world
	for(var sy=-80, row=0;sy < 680;sy+=22,row++)
	{
		for(var sx=-80;sx < 680;sx+=64)
		{
			// Calculate the offset for each odd row
			var rsx=sx+(row%2==0?0:32);
			drawSprite(sprites.blocks,0,rsx+offX,sy+offY);
		}
	}
	
	// Place back the avatar
	drawSprite(sprites.walk,playerDirection*8+Math.floor(playerStep/2),300-40,300-55);
}

// Handles the animation and drawing
function animate()
{
	playerMovement();
    drawScreen();

	// Firefox specific
    if (window.mozRequestAnimationFrame != null && window.mozRequestAnimationFrame != undefined)
        window.mozRequestAnimationFrame(animate);
	// W3 Standard
    else if (window.requestAnimationFrame != null && window.requestAnimationFrame != undefined)
        window.requestAnimationFrame(animate);
	// Fallback for non supported
    else
        setTimeout('animate()', 16);
}

var keyPressed = new Array();
// Handles key down event
function keyDown(e)
{
	// Event handling for IE
    e = e ? e : event;

    keyPressed[e.keyCode] = true;
	// IE
	try
	{
		e.cancelBubble = true;
		e.returnValue = false;
	}
	catch (er)
	{
	}
	// Firefox
	try
	{
		e.preventDefault();
		e.stopPropagation();
	}
	catch (er)
	{
	}
	return false;
}

// Handles key up event
function keyUp(e)
{
	// Even handling for IE
    e = e ? e : event;
    keyPressed[e.keyCode] = false;

	// IE
	try
	{
		e.cancelBubble = true;
		e.returnValue = false;
	}
	catch (er)
	{
	}
	// Firefox
	try
	{
		e.preventDefault();
		e.stopPropagation();
	}
	catch (er)
	{
	}
	return false;
}

// Checks if the left key or A is pressed
function isKeyLeft()
{
    return (keyPressed[52] || keyPressed[37] || keyPressed[65]);
}

// Checks if the right key or D is pressed
function isKeyRight()
{
    return (keyPressed[54] || keyPressed[39] || keyPressed[68]);
}

// Checks if the down key or S is pressed
function isKeyDown()
{
    return (keyPressed[40] || keyPressed[50] || keyPressed[83]);
}

// Checks if the up key or W is pressed
function isKeyUp()
{
    return (keyPressed[56] || keyPressed[38] || keyPressed[87]);
}

// Stores the X coordinate
var playerX=0;
// Stores the Y coordinate
var playerY=0;
// Stores the animation step
var playerStep=0;
// Stores the direction
var playerDirection=0;
// Stores the walking speed
var playerSpeed=2;

function playerMovement()
{
	// Going left
	if(isKeyLeft())
	{
		playerX-=playerSpeed;
		playerDirection = 6;
		playerStep=(playerStep+1)%16;
	}
	// Going right
	else if(isKeyRight())
	{
		playerX+=playerSpeed;
		playerDirection = 2;	
		playerStep=(playerStep+1)%16;
	}
	// Going up
	else if(isKeyUp())
	{
		playerY-=playerSpeed;
		playerDirection = 4;	
		playerStep=(playerStep+1)%16;
	}
	// Going down
	else if(isKeyDown())
	{
		playerY+=playerSpeed;
		playerDirection = 0;	
		playerStep=(playerStep+1)%16;
	}
}

// Start the loop as soon as possible
animate();
document.onkeydown = keyDown;
document.onkeyup = keyUp;
</script>
Now we have the avatar walking around, on an infinite blue map. You may wonder why the key handling is so complex, but the advantage of such system is that you can intercept combination keys, as well as run at the speed of the game loop and not at the key press event.
Creator of Dot World Maker
Mad programmer and annoying composer
User avatar
a_bertrand
Posts: 1537
Joined: Mon Feb 25, 2013 1:46 pm

Re: JS & Canvas from scratch

Post by a_bertrand »

Would be time to add some features to our map, right? Such that we actually see some changes while walking and also show that the map could be a simple grid in memory. Due to the choice of the rendering, we will need to convert our screen coordinate to the map coordinate to find which kind of cell we need to paint.

As I don't want to make a world editor, I will instead rely like in my own game on a function to generate the map. However I will make a simple sinusoidal generation this time to make things a bit more simple maybe without going into the details of a perlin noise (which is the function I used and which is used by most games). The function is called.... generateWorld and generate a 2D map. Certainly not something you may want to use in your game, but that should already be enough for this scope. Again as I modified some other areas, I provide you the whole code here:

Code: Select all

<!DOCTYPE html>
<html style='height: 100%; width: 100%;'>
<head>
<title>My ISO Game</title>
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
<meta content="utf-8" http-equiv="encoding">
</head>
<body style='margin: 0px;height: 100%; width: 100%;'>
<canvas id='gameCanvas' width='600' height='600'></canvas>
<script>
var canvas=document.getElementById('gameCanvas');
var ctx=canvas.getContext('2d');

// Stores all the sprites
var sprites={};
sprites.walk={name:'walk',width:80,height:110,columns:8};
sprites.blocks={name:'blocks',width:64,height:78,columns:10};

// Draws a sprite on the graphic context
function drawSprite(spriteSheet,sprite,x,y)
{
	// The image has not been loaded yet.
	if(spriteSheet.image == null)
	{
		// Create a new image object
		var img=new Image();
		img.src='images/'+spriteSheet.name+'.png';
		spriteSheet.image=img;
		return;
	}

	// Retreives the column on which this sprite is
	var c=sprite%spriteSheet.columns;
	// Retreive the line on which the sprite is
	var r=Math.floor(sprite/spriteSheet.columns);
	// Blit the sprite
	ctx.drawImage(spriteSheet.image,
		spriteSheet.width*c,spriteSheet.height*r,
		spriteSheet.width,spriteSheet.height,
		x,y,
		spriteSheet.width,spriteSheet.height);
}

// Will be our drawing function
function drawScreen()
{
	// Clear the screen
	ctx.clearRect(0, 0, 600, 600);
	
	// Player position on the map
	var xpos=Math.floor(playerX/32);
	var ypos=Math.floor(playerY/32);
	
	// Sub tile position
    var subX = playerX % 32;
    var subY = playerY % 32;

    // Sub offset to shift X or Y depending on the player position
    var offX = Math.floor((-subX) - subY);
    var offY = Math.round(subX * 22 / 32 - subY * 22 / 32);
	
	// draw the world
	for(var sy=-80, row=0;sy < 680;sy+=22,row++)
	{
		for(var sx=-80;sx < 680;sx+=64)
		{
			// Find the cell position based on the coordinate
			var a = Math.floor(xpos + sx / 64 - sy / 44) - 4;
			var b = Math.floor(ypos + sy / 44 + sx / 64) - 13;	

			if(a < 0 || b < 0 || a >= 300 || b >= 300)
				continue;
			
			// Calculate the offset for each odd row
			var rsx=sx+(row%2==0?0:32);
			// Draw the sprite based on the map value
			drawSprite(sprites.blocks,map[a][b],rsx+offX,sy+offY);
		}
	}
	
	// Place back the avatar
	drawSprite(sprites.walk,playerDirection*8+Math.floor(playerStep/2),300-40,300-55);
}

// Handles the animation and drawing
function animate()
{
	playerMovement();
    drawScreen();

	// Firefox specific
    if (window.mozRequestAnimationFrame != null && window.mozRequestAnimationFrame != undefined)
        window.mozRequestAnimationFrame(animate);
	// W3 Standard
    else if (window.requestAnimationFrame != null && window.requestAnimationFrame != undefined)
        window.requestAnimationFrame(animate);
	// Fallback for non supported
    else
        setTimeout('animate()', 16);
}

var keyPressed = new Array();
// Handles key down event
function keyDown(e)
{
	// Event handling for IE
    e = e ? e : event;

    keyPressed[e.keyCode] = true;
	// IE
	try
	{
		e.cancelBubble = true;
		e.returnValue = false;
	}
	catch (er)
	{
	}
	// Firefox
	try
	{
		e.preventDefault();
		e.stopPropagation();
	}
	catch (er)
	{
	}
	return false;
}

// Handles key up event
function keyUp(e)
{
	// Even handling for IE
    e = e ? e : event;
    keyPressed[e.keyCode] = false;

	// IE
	try
	{
		e.cancelBubble = true;
		e.returnValue = false;
	}
	catch (er)
	{
	}
	// Firefox
	try
	{
		e.preventDefault();
		e.stopPropagation();
	}
	catch (er)
	{
	}
	return false;
}

// Checks if the left key or A is pressed
function isKeyLeft()
{
    return (keyPressed[52] || keyPressed[37] || keyPressed[65]);
}

// Checks if the right key or D is pressed
function isKeyRight()
{
    return (keyPressed[54] || keyPressed[39] || keyPressed[68]);
}

// Checks if the down key or S is pressed
function isKeyDown()
{
    return (keyPressed[40] || keyPressed[50] || keyPressed[83]);
}

// Checks if the up key or W is pressed
function isKeyUp()
{
    return (keyPressed[56] || keyPressed[38] || keyPressed[87]);
}

// Stores the X coordinate
var playerX=150*32;
// Stores the Y coordinate
var playerY=150*32;
// Stores the animation step
var playerStep=0;
// Stores the direction
var playerDirection=0;
// Stores the walking speed
var playerSpeed=3;

function playerMovement()
{
	// Going left
	if(isKeyLeft())
	{
		playerX-=playerSpeed;
		playerDirection = 6;
		playerStep=(playerStep+1)%16;
	}
	// Going right
	else if(isKeyRight())
	{
		playerX+=playerSpeed;
		playerDirection = 2;	
		playerStep=(playerStep+1)%16;
	}
	// Going up
	else if(isKeyUp())
	{
		playerY-=playerSpeed;
		playerDirection = 4;	
		playerStep=(playerStep+1)%16;
	}
	// Going down
	else if(isKeyDown())
	{
		playerY+=playerSpeed;
		playerDirection = 0;	
		playerStep=(playerStep+1)%16;
	}
}

var map=null;
// World generator
function generateWorld()
{
	map=new Array();
	for(var i=0;i < 300;i++)
	{
		map[i]=new Array();
		for(var j=0;j < 300;j++)
		{
			// Will generate a positive number depending on the X and Y coordinate
			var v=(Math.sin(i/5.0)+1)*2+(Math.cos(j/5.0)+1)*2;
			if(v > 7)
				map[i][j]=3;
			else if(v > 5)
				map[i][j]=2;
			else if(v > 3)
				map[i][j]=1;
			else
				map[i][j]=0;
		}
	}
}

// Set the events
document.onkeydown = keyDown;
document.onkeyup = keyUp;
// Starts the map generation
generateWorld();
// Start the loop as soon as possible
animate();
</script>
Creator of Dot World Maker
Mad programmer and annoying composer
User avatar
a_bertrand
Posts: 1537
Joined: Mon Feb 25, 2013 1:46 pm

Re: JS & Canvas from scratch

Post by a_bertrand »

The whole script is now 258 lines between the JS and the little HTML. Not too bad for a first isometric test. The speed of the rendering should be also pretty fast (checked with https://github.com/mrdoob/stats.js/ and got around 60FPS on my machine which is actually the max).

So how does the isometric work and what's the difference between isometric and a top down view for example? Isometric use tiles which are not square but more diamond shaped. Which means you can't simply place them once after the other like in a top down view. However isometric views allows to draw objects of a given height, and as you draw from far away to the camera (y=0 to y=100 for example), you may have objects hiding others. You can also "fake" 3D world like I did by simply drawing the Z layers one after the other with just an offset on the Y position. In this example we will not do it but all those are possibilities opened by the isometric view. No other 2D view offer all those options.

Now what's missing in our game? Well a lot, we don't have enemies, we don't have any collision detection, we don't even handle the map edges or the map itself beside drawing it on the screen. All those are relatively easy to implement if we want to continue, but I let you now decide a bit more the direction of this tutorial. Try to make this work on your side and try to understand EVERY SINGLE LINE of it. If something is not clear, ask and I shall try to explain it more.
Creator of Dot World Maker
Mad programmer and annoying composer
User avatar
OldRod
Posts: 1321
Joined: Sun Sep 20, 2009 4:26 pm

Re: JS & Canvas from scratch

Post by OldRod »

Very nice!!

Thank you for doing this, can't wait to see more! :)
User avatar
hallsofvallhalla
Site Admin
Posts: 12031
Joined: Wed Apr 22, 2009 11:29 pm

Re: JS & Canvas from scratch

Post by hallsofvallhalla »

I have stickied this.
Post Reply

Return to “Tutorials”