topical media & game development
mobile-game-ch17-lander-explosion.htm / htm
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Lander</title>
<script src='mobile-game-ch17-js-jquery.min.js'></script>
<script src='mobile-game-ch17-js-underscore.js'></script>
<script src='mobile-game-ch17-js-quintus.js'></script>
<script src='mobile-game-ch17-js-quintus-input.js'></script>
<script src='mobile-game-ch17-js-quintus-sprites.js'></script>
<script src='mobile-game-ch17-js-quintus-scenes.js'></script>
<meta name="viewport" content="width=480, user-scalable=no">
<style>
body { padding:0px; margin:0px; }
#quintus { background-color:#CCC; }
</style>
</head>
<body>
<canvas id='quintus' width='480' height='320'></canvas>
<script>
var Q = Quintus()
.include("Input,Sprites,Scenes")
.setup()
.controls();
Q.Ship = Q.MovingSprite.extend({
step: function(dt) {
if(this.dead) return;
var p = this.p;
// Set our horizontal force
p.fx = 0;
if(Q.inputs['left']) { p.fx -= p.thrustX; }
if(Q.inputs['right']) { p.fx += p.thrustX; }
// Set our vertical force
if(Q.inputs['fire']) {
p.fy = -p.thrustY;
p.asset = "lander-thrust.png";
} else {
p.fy = 0;
p.asset = "lander.png";
}
// Calculate our y and x acceleration
p.ay = p.gravity + p.fy / p.m;
p.ax = p.fx / p.m;
// Let our super-class update our x and y
this._super(dt);
var col;
if(col = this.checkCollision()) {
if(col == 1 && Math.abs(p.vy) < 30) {
if(p.vy > 0) {
p.vy = 0;
}
} else {
this.parent.insert(new Q.Explosion(p.x,p.y,p.vx,p.vy,this.imageData));
this.parent.remove(this);
this.dead=true;
}
}
// Force our lander to stay in our box
// and zero out our velocity when we hit a wall
if(p.y < 0) { p.y = 0; p.vy = 0; }
if(p.y > Q.height- p.h) { p.y = Q.height - p.h; p.vy = 0; }
if(p.x < 0) { p.x = 0; p.vx = 0; }
if(p.x > Q.width - p.w) { p.x = Q.width - p.w; p.vx = 0; }
},
checkCollision: function() {
var bgData = Q.backgroundPixels;
// Get a integer based position from our
// x and y values
var bgx = Math.floor(this.p.x);
var bgy = Math.floor(this.p.y);
// Calculate the initial offset into our background
var bgOffset = bgx * 4 + bgy * bgData.width * 4 + 3;
// Pull out our data easy access
var pixels = this.imageData.data;
var bgPixels = bgData.data;
for(var sy=0;sy < this.imageData.height;sy++) {
for(var sx=0;sx < this.imageData.width;sx++) {
// Check for an existing pixel on our ship
if(pixels[sx*4 + sy * this.imageData.width * 4 + 3]) {
// Then check for a matching existing pixel on the background
// starting from our bgOffest and then indexing in from there
if(bgPixels[bgOffset + sx*4 + sy * bgData.width * 4]) {
// Next check if we are at the bottom of the lander
// if so return 1, to indicate that we might be landing
// instead of crashing
if(sy > this.imageData.height - 2) {
return 1;
} else {
// Otherwise return 2 and...Boom!
return 2;
}
}
}
}
}
return 0;
}
});
Q.Explosion = Q.GameObject.extend({
init: function(x,y,vx,vy,imgData) {
// Set up a container for our pixels
this.particles = []
// Grab the lander's image data
var landerData = imgData.data;
// Create a 3x3 pixel-data
// image data container to use for blitting down the road
this.pixelData = Q.ctx.createImageData(3,3);
this.drawPixel = this.pixelData.data;
// Pixels are going to be exploding out from
// the center of the lander
var centerX = imgData.width / 2;
var centerY = imgData.height / 2;
// Loop over each fourth pixel of the lander image
for(var sy=0;sy < imgData.height;sy+=4) {
for(var sx=0;sx < imgData.width;sx+=4) {
// Offset into the 1 dimension pixel data array
var loc = sx*4 + sy * imgData.width * 4;
// If theres a lander pixel here
if(landerData[loc + 3]) {
// Get the direction of the pixel from center
var distX = sx - centerX;
var distY = sy - centerY;
// Add a new particle
this.particles.push({
x: x + sx, // starting position x
y: y + sy, // starting position y
lifetime: 5, // remaining lifetime
r: landerData[loc] + 20, // make it a little redder
g: landerData[loc+1],
b: landerData[loc+2],
a: landerData[loc+3],
// For particle velocity, use the ships velocity,
// plus a random direction emanating from the center of the ship
vx: vx/6 + distX * 5 *(Math.random()+0.5),
vy: vy/6 + distY * 5 * (Math.random()+0.5)
});
}
}
}
},
step: function(dt) {
var bgData = Q.backgroundPixels;
var pixels = bgData.data;
for(var i =0,len=this.particles.length;i<len;i++) {
var v = this.particles[i];
if(v.lifetime > 0) {
var oldx = v.x, oldy = v.y;
v.vy += 20 * dt;
v.x += dt * v.vx;
v.y += dt * v.vy;
var loc = Math.floor(v.x)*4 + Math.floor(v.y) * bgData.width * 4;
if(pixels[loc + 3]) {
v.x = oldx;
v.y = oldy;
v.vy *= -0.2;
v.vx *= -0.2;
}
v.lifetime -= dt;
}
if(v.lifetime <= 0) { Q.stageScene('level'); return; }
}
},
draw: function(ctx) {
for(var i=0,len=this.particles.length;i<len;i++) {
var v = this.particles[i];
if(v.lifetime > 0) {
for(var l=0;l<36;l+=4) {
this.drawPixel[l+0] = v.r;
this.drawPixel[l+1] = v.g;
this.drawPixel[l+2] = v.b;
this.drawPixel[l+3] = v.a;
}
ctx.putImageData(this.pixelData,v.x,v.y);
}
}
}
});
Q.load(['lander.png','background.png',
'lander-thrust.png','map.png'], function() {
Q.scene("level",new Q.Scene(function(stage) {
stage.insert(new Q.Sprite({ asset: "background.png" }));
stage.insert(new Q.Sprite({ asset: "map.png" }));
var ship = stage.insert(new Q.Ship({
asset: 'lander.png',
x: 10, // X Position
y: 170, // Y Position
gravity: 20, // Gravity
m: 1, // Ship’s Mass
thrustX: 40, // Horizontal Thrust
thrustY: 80, // Vertical Thrust
}));
Q.backgroundPixels = Q.imageData(Q.asset('map.png'));
ship.imageData = Q.imageData(Q.asset('lander.png'));
}));
Q.stageScene("level");
});
</script>
</body>
</html>
(C) Æliens
04/09/2009
You may not copy or print any of this material without explicit permission of the author or the publisher.
In case of other copyright issues, contact the author.