我们继续这一系列文章,使用HTML5的canvas组件进行游戏开发。我们将要更新完善我们的第4课html5游戏制作入门系列教程(四)的游戏实例,并增加了火球,敌人和碰撞检测等功能模块。所以,现在我们的飞龙可以施放火球来杀死敌人(还有成绩统计)。现在,这个游戏的互动性更强了。你可以点击这里阅读这一系列教程的前一篇文章:html5游戏制作入门系列教程(六)。我们将基于之前的程序和代码进行开发。
这里有我们的演示和下载包:
好吧,下载所需文件,让我们开始编码!
步骤1:HTML
下面是基本的HTML代码:
这里是我演示的HTML,非常简单,对不对?
<!DOCTYPE html>
<html lang="en" >
<head>
<meta charset="utf-8" />
<title>html5游戏制作入门系列教程(七)</title>
<link href="css/main.css" rel="stylesheet" type="text/css" />
<!--[if lt IE 9]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<script src="js/jquery.js"></script>
<script src="js/script.js"></script>
</head>
<body>
<header tabindex="0">
<h2>html5游戏制作入门系列教程(七)</h2>
<a href="http://html5gamedev.org/?p=343" class="stuts">返回原文<span>HTML5GAME</span></a>
</header>
<div class="container">
<canvas id="scene" width="1000" height="600" tabindex="1"></canvas>
</div>
</body>
</html>
步骤2:CSS
下面是所使用的CSS样式表文件。
css/main.css
今天就不把css样式贴出来了,和以前的一样,没有什么特别之处。你可以在下载包里找到它。
步骤3:JS
js/script.js
// inner variables
var canvas, ctx;
var backgroundImage;
var iBgShiftX = 100;
var dragon, enemy = null; // game objects
var balls = [];
var enemies = [];
var dragonW = 75; // dragon width
var dragonH = 70; // dragon height
var iSprPos = 0; // initial sprite frame
var iSprDir = 0; // initial dragon direction
var iEnemyW = 128; // enemy width
var iEnemyH = 128; // enemy height
var iBallSpeed = 10; // initial ball speed
var iEnemySpeed = 2; // initial enemy speed
var dragonSound; // dragon sound
var wingsSound; // wings sound
var explodeSound, explodeSound2; // explode sounds
var laughtSound; // wings sound
var bMouseDown = false; // mouse down state
var iLastMouseX = 0;
var iLastMouseY = 0;
var iScore = 0;
// -------------------------------------------------------------
// objects :
function Dragon(x, y, w, h, image) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.image = image;
this.bDrag = false;
}
function Ball(x, y, w, h, speed, image) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.speed = speed;
this.image = image;
}
function Enemy(x, y, w, h, speed, image) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.speed = speed;
this.image = image;
}
// -------------------------------------------------------------
// get random number between X and Y
function getRand(x, y) {
return Math.floor(Math.random()*y)+x;
}
// draw functions :
function drawScene() { // main drawScene function
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); // clear canvas
// draw background
iBgShiftX += 4;
if (iBgShiftX >= 1045) {
iBgShiftX = 0;
}
ctx.drawImage(backgroundImage, 0 + iBgShiftX, 0, 1000, 940, 0, 0, 1000, 600);
// update sprite positions
iSprPos++;
if (iSprPos >= 9) {
iSprPos = 0;
}
// in case of mouse down - move dragon more close to our mouse
if (bMouseDown) {
if (iLastMouseX > dragon.x) {
dragon.x += 5;
}
if (iLastMouseY > dragon.y) {
dragon.y += 5;
}
if (iLastMouseX < dragon.x) {
dragon.x -= 5;
}
if (iLastMouseY < dragon.y) {
dragon.y -= 5;
}
}
// draw dragon
ctx.drawImage(dragon.image, iSprPos*dragon.w, iSprDir*dragon.h, dragon.w, dragon.h, dragon.x - dragon.w/2, dragon.y - dragon.h/2, dragon.w, dragon.h);
// draw fireballs
if (balls.length > 0) {
for (var key in balls) {
if (balls[key] != undefined) {
ctx.drawImage(balls[key].image, balls[key].x, balls[key].y);
balls[key].x += balls[key].speed;
if (balls[key].x > canvas.width) {
delete balls[key];
}
}
}
}
// draw enemies
if (enemies.length > 0) {
for (var ekey in enemies) {
if (enemies[ekey] != undefined) {
ctx.drawImage(enemies[ekey].image, enemies[ekey].x, enemies[ekey].y);
enemies[ekey].x += enemies[ekey].speed;
if (enemies[ekey].x < - iEnemyW) {
delete enemies[ekey];
// play laught sound
laughtSound.currentTime = 0;
laughtSound.play();
}
}
}
}
// collision detection
if (balls.length > 0) {
for (var key in balls) {
if (balls[key] != undefined) {
if (enemies.length > 0) {
for (var ekey in enemies) {
if (enemies[ekey] != undefined && balls[key] != undefined) {
if (balls[key].x + balls[key].w > enemies[ekey].x && balls[key].y + balls[key].h > enemies[ekey].y && balls[key].y < enemies[ekey].y + enemies[ekey].h) {
delete enemies[ekey];
delete balls[key];
iScore++;
// play explode sound #2
explodeSound2.currentTime = 0;
explodeSound2.play();
}
}
}
}
}
}
}
// draw score
ctx.font = '16px Verdana';
ctx.fillStyle = '#fff';
ctx.fillText('Score: ' + iScore * 10, 900, 580);
ctx.fillText('Plese click "1" to cast fireball', 100, 580);
}
// -------------------------------------------------------------
// initialization
$(function(){
canvas = document.getElementById('scene');
ctx = canvas.getContext('2d');
var width = canvas.width;
var height = canvas.height;
// load background image
backgroundImage = new Image();
backgroundImage.src = 'images/hell.jpg';
backgroundImage.onload = function() {
}
backgroundImage.onerror = function() {
console.log('Error loading the background image.');
}
// 'Dragon' music init
dragonSound = new Audio('media/dragon.wav');
dragonSound.volume = 0.9;
// 'Laught' music init
laughtSound = new Audio('media/laught.wav');
laughtSound.volume = 0.9;
// 'Explode' music inits
explodeSound = new Audio('media/explode1.wav');
explodeSound.volume = 0.9;
explodeSound2 = new Audio('media/explosion.wav');
explodeSound2.volume = 0.9;
// 'Wings' music init
wingsSound = new Audio('media/wings.wav');
wingsSound.volume = 0.9;
wingsSound.addEventListener('ended', function() { // loop wings sound
this.currentTime = 0;
this.play();
}, false);
wingsSound.play();
// initialization of empty ball
var oBallImage = new Image();
oBallImage.src = 'images/fireball.png';
oBallImage.onload = function() { }
// initialization of empty enemy
var oEnemyImage = new Image();
oEnemyImage.src = 'images/enemy.png';
oEnemyImage.onload = function() { }
// initialization of dragon
var oDragonImage = new Image();
oDragonImage.src = 'images/dragon.gif';
oDragonImage.onload = function() {
dragon = new Dragon(400, 300, dragonW, dragonH, oDragonImage);
}
$('#scene').mousedown(function(e) { // binding mousedown event (for dragging)
var mouseX = e.layerX || 0;
var mouseY = e.layerY || 0;
if(e.originalEvent.layerX) { // changes for jquery 1.7
mouseX = e.originalEvent.layerX;
mouseY = e.originalEvent.layerY;
}
bMouseDown = true;
if (mouseX > dragon.x- dragon.w/2 && mouseX < dragon.x- dragon.w/2 +dragon.w &&
mouseY > dragon.y- dragon.h/2 && mouseY < dragon.y-dragon.h/2 +dragon.h) {
dragon.bDrag = true;
dragon.x = mouseX;
dragon.y = mouseY;
}
});
$('#scene').mousemove(function(e) { // binding mousemove event
var mouseX = e.layerX || 0;
var mouseY = e.layerY || 0;
if(e.originalEvent.layerX) {
mouseX = e.originalEvent.layerX;
mouseY = e.originalEvent.layerY;
}
// saving last coordinates
iLastMouseX = mouseX;
iLastMouseY = mouseY;
// perform dragon dragging
if (dragon.bDrag) {
dragon.x = mouseX;
dragon.y = mouseY;
}
// change direction of dragon (depends on mouse position)
if (mouseX > dragon.x && Math.abs(mouseY-dragon.y) < dragon.w/2) {
iSprDir = 0;
} else if (mouseX < dragon.x && Math.abs(mouseY-dragon.y) < dragon.w/2) {
iSprDir = 4;
} else if (mouseY > dragon.y && Math.abs(mouseX-dragon.x) < dragon.h/2) {
iSprDir = 2;
} else if (mouseY < dragon.y && Math.abs(mouseX-dragon.x) < dragon.h/2) {
iSprDir = 6;
} else if (mouseY < dragon.y && mouseX < dragon.x) {
iSprDir = 5;
} else if (mouseY < dragon.y && mouseX > dragon.x) {
iSprDir = 7;
} else if (mouseY > dragon.y && mouseX < dragon.x) {
iSprDir = 3;
} else if (mouseY > dragon.y && mouseX > dragon.x) {
iSprDir = 1;
}
});
$('#scene').mouseup(function(e) { //绑定鼠标键松开事件
dragon.bDrag = false;
bMouseDown = false;
// play dragon sound
dragonSound.currentTime = 0;
dragonSound.play();
});
$(window).keydown(function(event){ // 键盘事件处理
switch (event.keyCode) {
case 49: // '1' key
balls.push(new Ball(dragon.x, dragon.y, 32, 32, iBallSpeed, oBallImage));
// 播放爆炸声音片段1 #1
explodeSound.currentTime = 0;
explodeSound.play();
break;
}
});
setInterval(drawScene, 30); // 循环渲染场景
// generate enemies randomly
var enTimer = null;
function addEnemy() {
clearInterval(enTimer);
var randY = getRand(0, canvas.height - iEnemyH);
enemies.push(new Enemy(canvas.width, randY, iEnemyW, iEnemyH, - iEnemySpeed, oEnemyImage));
var interval = getRand(5000, 10000);
enTimer = setInterval(addEnemy, interval); // 循环渲染场景(添加敌人)
}
addEnemy();
});
在一开始,我增加了两个新的对象:火球和敌人。每个对象都有自己的属性集(如位置,大小,形象,速度)。之后,我加入我们的drawScene()函数,负责绘制火球和敌人。另外,在这个函数的底部,你可以看到碰撞检测方法:
// collision detection
if (balls.length > 0) {
for (var key in balls) {
if (balls[key] != undefined) {
if (enemies.length > 0) {
for (var ekey in enemies) {
if (enemies[ekey] != undefined && balls[key] != undefined) {
if (balls[key].x + balls[key].w > enemies[ekey].x && balls[key].y + balls[key].h > enemies[ekey].y && balls[key].y < enemies[ekey].y + enemies[ekey].h) {
delete enemies[ekey];
delete balls[key];
iScore++;
// 播放爆炸声音片段#2
explodeSound2.currentTime = 0;
explodeSound2.play();
}
}
}
}
}
}
}
最后,我们必须定期添加我们的敌人(随机):
// 产生随机敌人
var enTimer = null;
function addEnemy() {
clearInterval(enTimer);
var randY = getRand(0, canvas.height - iEnemyH);
enemies.push(new Enemy(canvas.width, randY, iEnemyW, iEnemyH, - iEnemySpeed, oEnemyImage));
var interval = getRand(5000, 10000);
enTimer = setInterval(addEnemy, interval); // 循环绘制场景(添加敌人)
}
addEnemy();
步骤4:资源文件
游戏制作需要使用如下的游戏资源文件,包括图片和声音文件,这些你都可以在下载包中找到。
images/dragon.gif, images/enemy.png, images/fireball.png, images/hell.jpg
media/dragon.wav, media/explode1.wav, media/explosion.wav, media/laught.wav, media/wings.wav
结论
超级酷,不是吗?我会很高兴看到您的评论和意见。祝你好运!