1、首先,写出canvas前提,定义所需要的变量,如宽高、分数、生命值等。
<div style="text-align: center;"> <canvas id="canvas" width="480px" height="640px"></canvas> </div> <script> var canvas = document.getElementById("canvas"); var gj = canvas.getContext("2d"); // 定义游戏的五种状态,记录分数、生命值等相关数据。 const START = 0; var LOADING = 1; var RUNNING = 2; var PAUSE = 3; var GAMEOVER = 4; var state = START; var wid = canvas.width; var hei = canvas.height; var score = 0; var life = 3;
2、定义背景图片及开始状态:
由于我们设置的背景是动态向下滚动的,所以这里先获取我们所需要的图片,然后将图片所具有的一些属性封装为一个对象方面后面调用。
然后定义一个背景图片的构造函数,里面传入之前定义好的对象,然后用this将参数转换成函数内属性,接着画出图片并规定图片向下滚动的方式。
然后将这个构造函数存储为一个对象,设置定时器调用此对象里的函数方法实现背景图片的持续滚动状态。
然后设置一个canvas的点击事件,使其点击之后进入下一个阶段,也就是游戏加载中的loading状态。
// 定义背景图片的构造函数 function backgroung(images){ this.img = images.img; this.width = images.width; this.height = images.height; this.x1 = 0; this.y1 = 0; this.x2 = 0; this.y2 = -this.height; this.paint = function(){ gj.drawImage(this.img,this.x1,this.y1); gj.drawImage(this.img,this.x2,this.y2); } this.step = function(){ this.y1++; this.y2++; if (this.y1 == this.height) { this.y1 = -this.height; } if (this.y2 == this.height) { this.y2 = -this.height; } } } var load = new backgroung(BG); // 绘出logo var logo = new Image(); logo.src="img/start.png"; // 给canvas一个点击事件,当处于开始状态的时候点击进入loading状态 canvas.onclick = function () { if (state == START) { state = LOADING; } }
setInterval( function (){ load.paint(); load.step(); if (state == START) { gj.drawImage(logo,40,0) } else if (state == LOADING) { wait.paint(); wait.step(); } else if (state == RUNNING) { heroes.paint(); heroes.step(); heroes.shoot(); bulletsPaint(); bulletsStep(); bulletsDel(); // console.log(bullets); } else if (state == PAUSE) { } else if (state == GAMEOVER) { } },50 )
3、绘制loading画面:
loading画面的实现是利用四张图片来形成的动态效果,所以将这四张图片装入一个数组中以便调用。
同样的用对象来储存其包含的属性。
// 定义loading图片,用数组包含方便调用切换 var loadings = []; loadings[0] = new Image(); loadings[0].src = "img/game_loading1.png"; loadings[1] = new Image(); loadings[1].src = "img/game_loading2.png"; loadings[2] = new Image(); loadings[2].src = "img/game_loading3.png"; loadings[3] = new Image(); loadings[3].src = "img/game_loading4.png"; var Loading = { img:loadings, 186, height:38, length:loadings.length }
接下来,新建构造函数来生成图片,并在loading动画结束后转入游戏运行也就是这里的RUNNING状态。
由于定时器时间间隔过快导致loading画面快速闪过又没必要重新创建定时器,故这里用到一个变量来储存定时器运行次数,可以任意选择当定时器运行几次之后切换图片,这样更加流畅也更加合理。
// 定义loading图片的构造函数 function loading(images){ this.img = images.img; this.width = images.width; this.height = images.height; this.length = images.length; this.num = 0; this.times = 0 this.paint = function () { gj.drawImage(this.img[this.num],0,hei-this.height); } this.step = function () { this.times ++ ; //新建一个变量用来储存定时器作用次数,并用取摸的方式改变定时器作用在此上面的时间间隔。 if (this.times % 6 == 0) { this.num ++ ; } if (this.num == this.length) { state = RUNNING; } } } var wait = new loading(Loading);
4、绘制我方飞机:
首先,我方飞机有不同的状态,有两张图片来实现飞机正常情况下的动态效果,有四张图片来实现飞机撞毁时的动态效果。所以将这所有的6张图片保存在一个数组中方便调用。同样的,也新建一个对象来封装它的属性。
// 绘制我方飞机 var hero = []; hero[0] = new Image(); hero[0].src = "img/hero1.png"; hero[1] = new Image(); hero[1].src = "img/hero2.png"; hero[2] = new Image(); hero[2].src = "img/hero_blowup_n1.png"; hero[3] = new Image(); hero[3].src = "img/hero_blowup_n2.png"; hero[4] = new Image(); hero[4].src = "img/hero_blowup_n3.png"; hero[5] = new Image(); hero[5].src = "img/hero_blowup_n4.png"; var Hero = { img : hero, width : 99, height : 124, length : hero.length }
接下来,同样的,为其创建一个构造函数,获取之前对象的值,另外定义一些后面会用到的变量,像判断飞机是否撞毁、定义飞机移动的坐标等等,当然,到后面遇到相应功能再添加也是可以的。并且以同样的方式来调用。
而且,我们是用鼠标来控制飞机的移动,这里就得有一个onmousemove事件来获取鼠标坐标并相应改变飞机坐标。
function heros (images){ this.img = images.img; this.width = images.width; this.height = images.height; this.length = images.length; this.x = wid/2-this.width/2; this.y = hei-this.height; this.num = 0; this.boom = false; //判断我方飞机是否发生碰撞, this.paint = function () { gj.drawImage(this.img[this.num],this.x,this.y); } this.step = function () { if (!this.boom) { this.num ++ ; this.num = this.num % 2; //当我方飞机并未发生碰撞,动画在0 1之间互相转化形成动画效果。 }else{ this.num ++ ; if (this.num == this.length) { life -- ; if (life == 0) { this.num = this.length - 1; //当其发生碰撞,画面停留在碰撞动画的最后一张,然后进入GAMROVER状态。 state = GAMEOVER ; } else { heroes = new heros(Hero); } } } } this.booms = function () { this.boom = true; } var times = 0; this.shoot = function (){ times ++ ; if (times % 3 == 0) { bullets.push(new Bullets(Bullet)) //用来向生成的数组中添加对象元素。 } } } var heroes = new heros(Hero); // 添加鼠标移动事件,使我方飞机跟随鼠标在canvas界面中移动. canvas.onmousemove = function (event) { var event = event || window.event if (state == RUNNING) { var x = event.offsetX; var y = event.offsetY; heroes.x = x - heroes.width/2; heroes.y = y - heroes.width/2; } }
5、绘制子弹并理清子弹的运行逻辑:
首先如同之前一样,绘出子弹的图片,注意子弹是从飞机头射出,注意调整坐标定位。
// 绘制我方飞机的子弹, var bullet = new Image (); bullet.src = "img/bullet1.png"; var Bullet = { img : bullet, width : 9, height : 21 } function Bullets(images){ this.img = images.img; this.width = images.width; this.height = images.height; this.x = heroes.x + heroes.width/2 - this.width/2; this.y = heroes.y - this.height - 10; this.boom = false; this.paint = function () { gj.drawImage(this.img,this.x,this.y); } this.step = function () { this.y -= 10; } this.booms = function () { this.boom = true; } }
这里我们需要了解到的是,每一颗生成的子弹都是一个独立的对象,所以不能像之前那样调用函数。
这里我们新建一个数组用来储存每一个生成的子弹对象,循环遍历所有生成的子弹使其全都拥有同样且独立的产生方法和运动形式。
并且每一颗子弹撞到敌方飞机或者飞出canvas界面之外,我们将其从数组中清除,使其运行更为流畅。
// 每一颗子弹都是一个独立的对象,这里新建一个数组用来储存所有的子弹. var bullets = []; function bulletsPaint(){ for (var i = 0;i < bullets.length;i++) { bullets[i].paint(); } } function bulletsStep(){ for (var i = 0;i < bullets.length;i++) { bullets[i].step(); } } function bulletsDel(){ for (var i = 0;i < bullets.length;i++) { if (bullets[i].boom || bullets[i].y < -bullets[i].height) { bullets.splice(i,1); } } }
这次我们先做到这里,下次做完一个完整的飞机大战游戏。