HTML5 Game Development by some HTML5 Noobs

Command & Conquer current progress

So – The last few weeks haven’t seen much in the way of new features. Just better unit movement.

I’m also putting my build copy live – it will live here: http://www.html5gamedev.info/cnc/

  • It has no preload yet – the map may take a few seconds to download. Hit Begin + End until you see it load.
  • Add item will place a GDI MCV onto the map (same rules as the map)
  • The only thing you can really do is single unit click + move.
  • I’ve left in the debug mode (grid + path finding)

Getting an HTML5 canvas going

In the beginning

This is a recap of the first steps taken when I decided to make an RTS game. While I made numerous mistakes getting up and running, I can now explain the (best?) way to begin an HTML5 project.

 

A live demo of this is available here.

 

Step 1

Get a canvas going and get some JS objects to represent the game and the visual representation of the world. I add jQuery in but it’s hardly used even after one week’s coding. It’s like a safety blanket :)

<html>
<head>
    <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.5/jquery.min.js"></script>
    <script type="text/javascript">
        var map = {
        };

        var game = {
        };
    </script>
</head>
<body>
    <canvas id="display"></canvas>
</body>
</html>

Step 2

Time for some game basics… A game is an interactive animation of sorts. While you could (as Ronco has done) use <DIV> tags to represent the map, the trees, the tanks – it’s going to be quite resource intensive having a DOM contain 100s of <DIV>s – Instead, I went with using <CANVAS> to render the scene… This means I need to worry about the internal game tick. Each tick / frame, the current frame needs clearing and any changes need to be rendered.

var game = {
    ticker: {
        timer: 0,        // JS timer reference
        interval: 40    // time in ms
    },

    init: function() {
        this.ticker.timer = setInterval("game.run()", this.ticker.interval);
    },

    run: function() {
        // main run method
    },

    end: function() {
        clearInterval(this.ticker.timer);
    },
};

If I call game.init() nothing will run every 40ms – perfect! game.end() stops the game running.

Step 3

Time to get something appearing on screen. I’m starting off with a blank map, this map – it’s just a large image using CnC’s own grass texture. It’s bigger than the canvas is going to be so that leads onto the next part – scrolling the map.

init: function() {
        this.ticker.timer = setInterval("game.run()", this.ticker.interval);

        // bind arrow keys
        $(document).keydown(function(event) {

            if (event.which == 37) {
                event.preventDefault();
                map.scroll(map.SCROLL_LEFT);
                return;
            }
            if (event.which == 38) {
                event.preventDefault();
                map.scroll(map.SCROLL_UP);
                return;
            }
            if (event.which == 39) {
                event.preventDefault();
                map.scroll(map.SCROLL_RIGHT);
                return;
            }
            if (event.which == 40) {
                event.preventDefault();
                map.scroll(map.SCROLL_DOWN);
                return;
            }
        });
    }

This binds the arrow keys. Using some constants to define the direction of scroll and jQuery – when an arrow key is pushed, I call the scroll() function:

Before writing the scroll function, the viewport needs defining. This will be a small object inside the map object that holds information about which part of the map we’re looking at. Essentially the canvas is the viewport – but it never moves. So the viewport abstracts the scroll values. This is the viewport:

viewport : {
    x : 0,
    y : 0,
    scroll_amount: 12
}

Another object I created was the dimensions container. This deals with the dimensions of the entire map:

dimensions: {
    w: 983,
    h: 700
}

And now the scroll() function:

scroll: function(dir) {
	if (dir == this.SCROLL_LEFT) {
		if (this.viewport.x == 0) {
			return;
		}
		this.viewport.x -= this.viewport.scroll_amount;
	}
	else if (dir == this.SCROLL_UP) {
		if (this.viewport.y == 0) {
			return;
		}
		this.viewport.y -= this.viewport.scroll_amount;
	}
	else if (dir == this.SCROLL_RIGHT) {
		if (Math.abs(this.viewport.x) + this.viewport.w >= this.dimensions.w) {
			return;
		}
		this.viewport.x += this.viewport.scroll_amount;
	}
	else {
		if (Math.abs(this.viewport.y) + this.viewport.h >= this.dimensions.h) {
			return;
		}
		this.viewport.y += this.viewport.scroll_amount;
	}
	this.requires_redraw = true;
    }
}

The last bit is to actually draw stuff. This means a draw() function. But since our scene could potentially be rendered every 40ms – this could result in a lot of extra work. To combat this – there is a boolen toggle requires_redraw – when this is true, the draw() function will draw – when it’s false it won’t. Simples!

draw: function() {
    if (!this.requires_redraw) {
        return;
    }

    // clear canvas
    this.canvas.width = this.canvas.width;

    this.context.drawImage(this.tile, this.viewport.x, this.viewport.y, this.viewport.w, this.viewport.h, 0, 0, this.viewport.w, this.viewport.h);

    this.requires_redraw = false;
}

The entire thing is available here

I can haz some drifting

Here’s some pseudocode which I will be implementing tonight which will drift cars around corners:


//persistent variables
maxSpeed = 1
accel = 0.1
decel = 0.01
friction = 0.7
vx = 0
vy = 0
// every frame
// get current car rotation
carAng = car.rotation
// get normalised x&y –1 - 1
dx = math.cos(carAng)
dy = math.sin(carAng)
// deal with power
if (upKey){
power += accel
if (power > maxPower)
power = maxPower
}else{
power –= decel
if (power < 0)
power = 0
}
// add power to velocity
vx += dx*power
vy += dx*power
// apply friction to velocity
vx *= friction
vy *= friction
// add velocity to car’s position
car.x += vx
car.y += vy

Polar co-ordinates

A very useful guide to the polar co-ordinate system – http://www.helixsoft.nl/articles/circle/sincos.htm

Micro Machines 2 notes

Just given Micro Machines 2 a play, here are some notes:

  • The sound effects are fucking horrible. Sound will be the last thing I do, if at all.
  • You can drive anywhere on the table top, but you bounce off of obvious objects
  • If you drive off the table top, a spinning animation kicks in, the car disappears and is then reset to a point on the map.
  • The AI follows a pre-set path.
  • The turning angle segments are approximately 10 degrees.
  • There are small relics on the actual track which cause the car to jump if the car is going fast enough
  • What look like hills on the map have no effect on the sprite of the car but cause it to slow down slightly.
  • As suspected, left/right increment/decrement the angle of the car, up/down accelerate/decelerate the car.
More soon when I can find the mute button.

Hello from Project Micro Machines

I’ve just put up a new page for Project Micro Machines – please read, digest, enjoy.

Hello world!

Right!

Time to get started writing some code and documentation…

In the mean time – read up on Project: RTS