开发工具: vscode
一、人物拼图游戏
游戏介绍: 拼图游戏将一幅图片分割成若干拼块并将它们随机打乱顺序。当将所有拼块都放回原位置时,就完成了拼图(游戏结束)。
在“游戏”中,单击滑块选择游戏难易,“容易”为3行3列拼图游戏,中间为一个4行4列拼图游戏,“难”为5行5列拼图游戏。拼块以随机顺序排列,玩家用鼠标单击空白块的四周来交换它们的位置,直到所有拼块都回到原位置。
程序设计思路: HTML5可以把图片整合到网页中。使用canvas元素可以在这个空白的画布上填充线条,载入图片文件,甚至动画效果。这里制作拼图游戏用来展示HTML5 canvas的图片处理能力。
游戏程序首先显示以正确顺序排列的图片缩略图,根据玩家设置的分割数,将图片订割成相应tileCount行列数的拼块,并按顺序编号。动态生成一个大小tileCount_tileCount,的数组boardParts,存放用0、1、2到tileCount_ tileCount-1的数,每个数字代表一个拼块(例如4*4的游戏拼块编号如图所示)。
游戏开始时,随机打乱这个数组boardParts,假如boardParts[0]是5,则在左上角显示编号是5的拼块。根据玩家用鼠标单击的拼块和空白块所在位置,来交换该boardParts数组对应的元素,最后依据元素排列顺序来判断是否已经完成游戏。
游戏参考代码:
sliding.js
var context=document.getElementById('puzzle').getContext('2d');
var img=new Image();
img.src='defa.jpg';
img.addEventListener('load',drawTiles,false);
var boardSize=document.getElementById('puzzle').width;
var tileCount=document.getElementById('scale').value;
var tileSize=boardSize/tileCount;
var clickLoc=new Object;
clickLoc.x=0;
clickLoc.y=0;
var emptyLoc=new Object;
emptyLoc.x=0;
emptyLoc.y=0;
var solved=false;
var boardParts=new Object;
setBoard();
document.getElementById('scale').onchange=function(){
tileCount=this.value;
tileSize=boardSize/tileCount;
setBoard();
drawTiles();
};
document.getElementById('puzzle').onmousemove=function(e){
clickLoc.x=Math.floor((e.pageX-this.offsetLeft)/tileSize);
clickLoc.y=Math.floor((e.pageY-this.offsetTop)/tileSize);
};
document.getElementById('puzzle').onclick=function(){
if (distance(clickLoc.x,clickLoc.y,emptyLoc.x,emptyLoc.y)==1){
slideTile(emptyLoc,clickLoc);
drawTiles();
}
if(solved){
setTimeout(function(){alert("You solved it!");},500);
}
};
function setBoard(){
boardParts=new Array(tileCount);
for(var i=0;i<tileCount;++i) {
boardParts[i]=new Array(tileCount);
for (var j=0;j<tileCount;++j){
boardParts[i][j]=new Object;
boardParts[i][j].x=(tileCount-1)-i;
boardParts[i][j].y=(tileCount-1)-j;
}
}
emptyLoc.x=boardParts[tileCount-1][tileCount-1].x;
emptyLoc.y=boardParts[tileCount-1][tileCount-1].y;
solved=false;
}
function drawTiles(){
context.clearRect(0,0,boardSize,boardSize);
for(var i=0;i<tileCount;++i){
for(var j=0;j<tileCount;++j){
var x=boardParts[i][j].x;
var y=boardParts[i][j].y;
if(i!=emptyLoc.x || j!=emptyLoc.y || solved==true){
context.drawImage(img,x*tileSize,y*tileSize,tileSize,tileSize,
i*tileSize,j*tileSize,tileSize,tileSize);
}
}
}
}
function distance(x1,y1,x2,y2) {
return Math.abs(x1-x2)+Math.abs(y1-y2);
}
function slideTile(toLoc,fromLoc){
if(!solved){
boardParts[toLoc.x][toLoc.y].x=boardParts[fromLoc.x][fromLoc.y].x;
boardParts[toLoc.x][toLoc.y].y=boardParts[fromLoc.x][fromLoc.y].y;
boardParts[fromLoc.x][fromLoc.y].x=tileCount-1;
boardParts[fromLoc.x][fromLoc.y].y=tileCount-1;
toLoc.x=fromLoc.x;
toLoc.y=fromLoc.y;
checkSolved();
}
}
function checkSolved(){
var flag=true;
for(var i=0;i<tileCount;++i){
for(var j=0;j<tileCount;++j){
if(boardParts[i][j].x!=i || boardParts[i][j].y!=j){
flag=false;
}
}
}
solved=flag;
}
index.html
<!doctype html>
<html>
<head>
<title>拼图游戏</title>
<style>
.picture{
border:1px solid black;
}
</style>
</head>
<body>
<div id="title">
<h2>拼图游戏</h2>
</div>
<div id="slider">
<form>
<label>低</label>
<input type="range" id="scale" value="4" min="3" max="5" step="1">
<label>高</label>
</form>
<br>
</div>
<div id="main" class="main">
<canvas id="puzzle" width="480px" height="480px"></canvas>
</div>
<script src="sliding.js"></script>
</body>
</html>
运行结果:
二、雷电飞机射击游戏
游戏介绍: 通过上下左右控制飞机移动,空格键完成射击
程序设计步骤: 将游戏种所用到的玩家、敌人、子弹等封装成类,planobj()来检测飞机的碰撞
游戏参考代码:
<!DOCTYPE html>
<html>
<head>
<title>飞机大战</title>
<meta charset="utf-8">
</head>
<body>
<canvas id='myCanvas' width="320" height="480" style="border: solid">
你的浏览器不支持canves画布元素,请更新浏览器获得演示效果。
</canvas>
<div id="message_txt" style="display: block;">飞机大战</div>
<div id="score_txt" style="display: block;">分数:0分</div>
<script type="text/javascript">
var canvas=document.getElementById('myCanvas');
var context=canvas.getContext('2d');
document.addEventListener('keydown',onKeydown);
//飞机类和其属性
var Plan=function(image,x,y,n){
this.image=image;
this.x=x;
this.y=y;
this.orignx=x;
this.origny=y;
this.width=image.width/n;
this.height=image.height;
this.isCaught=false;
this.frm=0;
this.dis=0;
this.n=n;
};
Plan.prototype.getCaught=function(bool){
this.isCaught=bool;
if (bool==false){
this.orignx=0;
this.origny=this.y;
}
};
Plan.prototype.testPoint=function(x,y){
var betweenX=(x>=this.x)&&(x<=this.x+this.width);
var betweenY=(y>=this.y)&&(y<=this.y+this.height);
return betweenX&&betweenY;
};
Plan.prototype.move=function(dx,dy){
this.x+=dx;
this.y+=dy;
};
Plan.prototype.Y=function(){
return this.y;
};
//不断下移飞机
Plan.prototype.draw=function(ctx){
ctx.save();
ctx.translate(this.x,this.y);
ctx.drawImage(this.image,this.frm*this.width,0,this.width,this.height,0,0,this.width,this.height);
ctx.restore();
this.y++;
this.x=this.orignx+20*Math.sin(Math.PI/100*this.y);
this.dis++;
if(this.dis>=3){
this.dis=0;
this.frm++;
if(this.frm>=this.n) this.frm=0;
}
};
//原地不动画飞机
Plan.prototype.draw2=function(ctx){
ctx.save();
ctx.translate(this.x,this.y);
ctx.drawImage(this.image,this.frm*this.width,0,this.width,this.height,0,0,this.width,this.height);
ctx.restore();
this.dis++;
//3帧换一次图片
if(this.dis>=3){
this.dis=0;
this.frm++;
if(this.frm>=this.n) this.frm=0;
}
};
//检测飞机碰撞
Plan.prototype.hitTestObject=function(planobj){
if(iscolliding(this.x,this.y,this.width,this.height,planobj.x,planobj.y,planobj.width,planobj.height))
return true;
else
return false;
}
function iscolliding(ax,ay,aw,ah,bx,by,bw,bh){
if(ay>by+bh||by>ay+ah||ax>bx+bw||bx>ax+aw)
return false;
else
return true;
}
//子弹类和其属性
var Bullet=function(image,x,y){
this.image=image;
this.x=x;
this.y=y;
this.orignx=x;
this.orignx=y;
this.width=image.width/4;
this.height=image.height;
this.isCaught=false;
this.frm=0;
this.dis=0;
}
Bullet.prototype.testPoint=function(x,y){
var betweenX=(x>=this.x)&&(x<this.x+this.width);
var betweenY=(y>=this.y)&&(y<this.y+this.height);
return betweenX&&betweenY;
};
Bullet.prototype.move=function(dx,dy){
this.x+=dx;
this.y+=dy;
};
Bullet.prototype.Y=function(){
return this.y;
};
Bullet.prototype.draw=function(ctx){
ctx.save();
ctx.translate(this.x,this.y);
ctx.drawImage(this.image,this.frm*this.width,0,this.width,this.height,0,0,this.width,this.height);
ctx.restore();
this.y--;
this.dis++;
if(this.dis>=10){
this.dis=0;
this.frm++;
if(this.frm>=4) this.frm=0;
}
};
//检测子弹与敌人的碰撞
Bullet.prototype.hitTestObject=function(planobj){
if(iscolliding(this.x,this.y,this.width,this.height,planobj.x,planobj.y,planobj.width,planobj.height))
return true;
else
return false;
}
//爆炸动画类和属性
var Bomb=function(image,x,y){
this.image=image;
this.x=x;
this.y=y;
this.width=image.width/6;
this.height=image.height;
this.frm=0;
this.dis=0;
};
Bomb.prototype.draw2=function(ctx){
ctx.save();
ctx.translate(this.x,this.y);
if(this.frm>=6) return ;
ctx.drawImage(this.image,this.frm*this.width,0,this.width,this.height,0,0,this.width,this.height);
ctx.restore();
this.dis++;
if(this.dis>=10){
this.dis=0;
this.frm++;
}
};
var plan1,plan2,plan3,plan4,caughtplan=null;
var isClick=false;
var mouseX,mouseY,preX,preY;
var plans=[];
var bullets=[];
var bombs=[];
var score=0;
var overflag=false;
var myplane;
//导入外部材料图
var image=new Image();
var image2=new Image();
var image3=new Image();
var image4=new Image();
var image5=new Image();
var bakground=new Image();
bakground.src='map_0.png';
image.src='plan.png';
image.onload=function(){
}
image2.src='bomb.png';
image2.onload=function(){
}
image3.src='enemy.png';
image3.onload=function(){
myplane=new Plan(image,300*Math.random(),400,6);
plan_interval=setInterval(function(){
plans.push(new Plan(image,300*Math.random(),20*Math.random(),2));
},3000);//3秒产生一架敌机
setInterval(function(){
context.clearRect(0,0,320,480);
context.drawImage(bakground,0,0);
//画己方飞机
if(!overflag)
myplane.draw2(context);
//画敌机
for(var i=plans.length-1;i>=0;i--){
if (plans[i].Y()>400){
plans.splice(i,1);//删除敌机
}
else{
plans[i].draw(context);
}
}
//画子弹
for (var i=bullets.length-1;i>=0;i--){
if (bullets[i].Y()<100){
bullets.splice(i,1);//删除子弹
}
else{
bullets[i].draw(context);
}
}
//检测玩家是否撞到敌机
for (vari=plans.length-1;i>=0;i--){
e1=plans[i];
if(e1!=null && myplane!=null && myplane.hitTestObject(e1)){
clearInterval(plan_interval);
plans.splice(i,1);//删除敌机
bombs.push(new Bomb(image2,myplane.x,myplane.y));
message_txt.innerHTML='敌机碰到玩家自己飞机,游戏结束';
overflag=true;
}
}
//判断子弹击中没有
for(var j=bullets.length-1;j>=0;j--){
var b1=bullets[j];
for(var i=plans.length-1;i>=0;i--){
e1=plans[i];
if (e1!=null && b1!=null && b1.hitTestObject(e1)){
plans.splice(i,1);
bullets.splice(i,1);
bombs.push(new Bomb(image2,b1.x,b1.y-36));
message_txt.innerHTML='敌机被击中,加20分';
score+=20;
score_txt.innerHTML='分数:'+score+'分';
}
}
}
//画爆炸
for (var i=bombs.length-1;i>=0;i--){
if (bombs[i].frm>=6){
bombs.splice(i,1);
}
else{
bombs[i].draw2(context);
}
}
},1000/60);
};
image4.src='bullet.png';
image4.onload=function(){
};
//飞机移动控制
function onKeydown(e){
if(e.keyCode==32){
bullets.push(new Bullet(image4,myplane.x,myplane.y-36));
}else if(e.keyCode==37){
myplane.move(-10,0);
}else if(e.keyCode==39){
myplane.move(10,0);
}else if(e.keyCode==38){
myplane.move(0,-10);
}else if(e.keyCode==40){
myplane.move(0,10);
}
}
</script>
</body>
</html>
运行结果:
三、FlappyBird游戏
游戏介绍: 通过鼠标点击来控制小鸟,跨越由各种不同长度水管所组成的障碍。
游戏参考代码:
bird.js
var canvas=document.getElementById("canvas");
var c=canvas.getContext("2d");
function Bird(x,y,image) {
this.x=x,
this.y=y,
this.width=image.width/2,
this.height=image.height,
this.image=image;
this.draw=function (context,state) {
if(state==="up")
context.drawImage(image,0,0,this.width,this.height,this.x,this.y,this.width,this.height);
else {
context.drawImage(image,this.width,0,this,width,this.height,this.x,this.y,this.width,this.height);
}
}
};
function Obstacle(x,y,h,image) {
this.x=x,
this.y=y,
this.width=image.width/2,
this.height=h,
this.flypast=false;
this.draw=function (context,state) {
if(state==="up")
context.drawImage(image,0,0,this.width,this.height,this.x,this.y,this.width,this.height);
else {
context.drawImage(image,this.width,image.height-this.height,this.height,this.width,this.height,this.x,this.y,this.width,this.height)
}
}
};
function FlappyBird() {}
FlappyBird.prototype= {
bird: null,
bg: null,
obs: null,
obsList: [],
mapWidth: 340,
mapHeight: 453,
startX: 90,
startY: 225,
obsDistance: 150,
obsSpeed: 2,
obsInterval: 2000,
upSpeed: 8,
downSpeed: 3,
line: 56,
score: 0,
touch: false,
gameOver: false,
CreateMap: function () {
//背景
this.bg = new Image();
this.bg.src = "img/bg.png";
var startBg = new Image();
startBg.src = "img/start.jpg";
startBg.onload = function () {
c.drawImage(startBg, 0, 0);
};
//小鸟
var image = new Image();
image.src = "img/bird.png";
image.onload = function () {
this.bird = new Bird(this.startX, this.startY, image);
}.bind(this);
//障碍物
this.obs = new Image();
this.obs.src = "img/obs.png";
this.obs.onload = function () {
var h = 100;
var h2 = this.mapHeight - h - this.obsDistance;
var obs1 = new Obstacle(this.mapWidth, 0, h, this.obs);
var obs2 = new Obstacle(this.mapWidth, this.mapHeight - h2, h2 - this.line, this.obs);
this.obsList.push(obs1);
this.obsList.push(obs2);
}.bind(this);
},
CreatObs: function () {
var h = Math.floor(Math.random() * (this.mapHeight - this.obsDistance - this.line));
var h2 = this.mapHeight - h - this.obsDistance;
var obs1 = new Obstacle(this.mapWidth, 0, h, this.obs);
var obs2 = new Obstacle(this.mapWidth, this.mapHeight - h2, h2 - this.line, this.obs);
this.obsList.push(obs1);
this.obsList.push(obs2);
if (this.obsList[0].x < -this.obsList[0].width)
this.obsList.splice(0, 2);
},
DrawObs:function(){
c.fillStyle="#00ff00";
for(var i=0;i<this.obsList.length;i++){
this.obsList[i].x-=this.obsSpeed;
if(i%2)
this.obsList[i].draw(c,"up");
else
this.obsList[i].draw(c,"down");
}
},
CountScore:function () {
if(this.obsList[0].x + this.obsList[0].width < this.startX &&this.obsList[0].flypast==false){
this.score+=1;
this.obsList[0].flypast=true;
}
},
ShowScore:function () {
c.strokeStyle="#000";
c.lineWidth=1;
c.fillStyle="#fff";
c.fillText(this.score,10,50);
c.strokeText(this.score,10,50);
},
CanMove:function () {
if(this.bird.y<0 || this.bird.y > this.mapHeight-this.bird.height-this.line){
this.gameOver=true;
}else{
var boundary=[{
x:this.bird.x,
y:this.bird.y
},{
x:this.bird.x+this.bird.width,
y:this.bird.y
},{
x:this.bird.x,
y:this.bird.y+this.bird.height
},{
x:this.bird.x+this.bird.width,
y:this.bird.x+this.bird.height
}];
for (var i=0;i<this.obsList.length;i++){
for(var j=0;j<4;j++)
if(boundary[j].x>=this.obsList[i].x && boundary[j].x <= this.obsList[i].x+this.obsList[i].width &&
boundary[j].y>=this.obsList[i].y&& boundary[j].y<=this.obsList[i].y+this.obsList[i].height){
this.gameOver=false;
break;
}
if(this.gameOver)
break;
}
}
},
CheckTouch:function () {
if(this.touch){
this.bird.y-=this.upSpeed;
this.bird.draw(c,"up");
}else {
this.bird.y+=this.downSpeed;
this.bird.draw(c,"down");
}
},
ClearScreen:function () {
c.drawImage(this.bg,0,0);
},
ShowOver:function () {
var overImg=new Image();
overImg.src="img/over.png";
overImg.onload=function () {
c.drawImage(overImg,(this.mapWidth-overImg.width)/2,(this.mapHeight-overImg.height)/2-50);
}.bind(this);
return;
}
};
var game=new FlappyBird();
var Speed=20;
var IsPlay=false;
var GameTime=null;
var btn_start;
window.onload=InitGame;
function InitGame() {
c.font="3em 微软雅黑";
game.CreateMap();
canvas.onmousedown=function () {
game.touch=true;
}
canvas.onmouseup=function () {
game.touch=false;
};
canvas.onclick=function () {
if (!IsPlay) {
IsPlay = true;
GameTime = RunGame(Speed);
}
}
}
function RunGame(speed) {
var updateTimer=setInterval(function () {
game.CanMove();
if(game.gameOver){
game.ShowOver();
clearInterval(updateTimer);
return;
}
game.ClearScreen();
game.DrawObs();
game.CheckTouch();
game.CountScore();
game.ShowScore();
},speed);
var obsTimer=setInterval(function () {
if (game.gameOver){
clearInterval(obsTimer);
return;
}
game.CreatObs();
},game.obsInterval);
}
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Flappy Bird</title>
</head>
<body>
<canvas id="canvas" width="340" height="453" style="border: 2px solid #000;background: #fff;"></canvas>
<script src="bird.js" type="text/javascript"></script>
</body>
</html>
运行结果: