gitee仓库地址:https://gitee.com/aflylong/canvas-the-plane-war.git
包括完整代码以及图片素材
<html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" href="img/favicon.ico">
<title>Document</title>
<style>
canvas {
display: block;//将画布转成块元素,设置页面居中才有效果
margin: 20px auto;
}
</style>
</head>
<body>
<canvas width="480" height="852" id="cas"></canvas>
</body>
</html>
图片文件夹的结构,hero:我方飞机,background:背景图,bullet1:子弹,enemy:敌机,start:飞机大战logo,game_pause:游戏暂停图片,game_loading:游戏加载图片
游戏大概五个阶段
// 1.准备阶段 使用定时器将背景图和logo进行绘制
// 2.游戏加载阶段 点击画布开始游戏 绘制三张加载的飞机
// 3. 游戏进行中 绘制我方飞机 绘制子弹 地方飞机 更新生命和分数
// 4. 暂停阶段 鼠标离开画布 飞机(包括敌方飞机)和子弹静止 画布运动
// 鼠标移入 游戏继续 恢复到第三阶段
// 5. 游戏结束 我方飞机发生爆炸 敌方飞机静止
以下都是在js中进行写入
var canvas = document.getElementById('cas');//获取画布元素
var ctx = canvas.getContext('2d');//创建一个CanvasRenderingContext2D对象作为2D渲染的上下文
//在这里可以设置一些初始状态,后面可能会用到的东西等
var start = 0;//游戏初始状态
var loading = 1;//游戏加载中
var running = 2;//游戏运行中
var pause = 3;//游戏暂停
var over = 4;//游戏结束
var score = 0;//游戏分数
var life = 3;//生命值
var state = start;//改变游戏状态
var Width = canvas.width;//获取画布的宽
var Height = canvas.height;//获取画布的高
function getImg(srcc) {//制造img对象
var img = new Image();
img.src = srcc;
return img;
}
var pause = getImg('img/game_pause_nor.png');//暂停
var zidan = getImg('img/bullet1.png')//子弹
//用数组存储起来,方便后面使用
var enboom1 = [//敌机形态1
'img/enemy1.png',
'img/enemy1_down1.png',
'img/enemy1_down2.png',
'img/enemy1_down3.png',
'img/enemy1_down4.png'];
var enboom2 = [//敌机形态2
'img/enemy2.png',
'img/enemy2_down1.png',
'img/enemy2_down2.png',
'img/enemy2_down3.png',
'img/enemy2_down4.png'];
var enboom3 = [//敌机形态3
'img/enemy3_n1.png',
'img/enemy3_n2.png',
'img/enemy3_hit.png',
'img/enemy3_down1.png',
'img/enemy3_down2.png',
'img/enemy3_down3.png',
'img/enemy3_down4.png',
'img/enemy3_down5.png',
'img/enemy3_down6.png'];
var heros = [//我方飞机
'img/hero1.png',
'img/hero2.png',
'img/hero_blowup_n1.png',
'img/hero_blowup_n2.png',
'img/hero_blowup_n3.png',
'img/hero_blowup_n4.png'];
//绘制一个运动的背景
//1准备阶段
//绘制一个运动的背景
var bgimg = getImg('img/background.png');
var BG = {
img: bgimg,
width: Width,
height: Height
}
var Log = {
img: getImg('img/start.png'),
width: Width,
height: Height,
x: 45
}
var loadf = [];
for (var i = 0; i < 4; i++) {
var str = `img/game_loading${i + 1}.png`
loadf[i] = getImg(str);
}
var loading = {
img: loadf,
width: 187,
height: 38,
length: loadf.length
}
var heroObj = [];
for (var i = 0; i < heros.length; i++) {
heroObj[i] = getImg(heros[i]);
}
var heroMy = {
img: heroObj,
width: 99,
height: 124,
length: heroObj.length
}
var bulletObj = {
img: zidan,
width: 9,
height: 21
}
var pauseObj = {
img: pause,
width: 60,
height: 45,
x: Width / 2 - 30,
y: Height / 2 - 22.5,
}
var enemy1arr = [];
for (var i = 0; i < enboom1.length; i++) {
enemy1arr[i] = getImg(enboom1[i]);
}
var enemy1Obj = {
img: enemy1arr,
width: 57,
height: 51,
length: enemy1arr.length,
life: 5,
type: 1,
}
var enemy2arr = [];
for (var i = 0; i < enboom2.length; i++) {
enemy2arr[i] = getImg(enboom2[i]);
}
var enemy2Obj = {
img: enemy2arr,
width: 69,
height: 95,
length: enemy2arr.length,
life: 10,
type: 2,
}
var enemy3arr = [];
for (var i = 0; i < enboom3.length; i++) {
enemy3arr[i] = getImg(enboom3[i]);
}
var enemy3Obj = {
img: enemy3arr,
width: 165,
height: 261,
length: enemy3arr.length,
life: 15,
type: 3,
}
var scoreObj = {
name: 'score',
num: score,
x: 0,
y: 40,
color: 'blue'
}
var lifeObj = {
name: 'life',
num: life,
x: Width - 150,
y: 40,
color: 'blue'
}
var overObj = {
name: 'GAME OVER',
num: 'PLAY GAME',
x: 0,
y: Height / 2 - 20,
color: 'red'
}
var sky = new Bg(BG);//背景
var log = new Bg(Log);//logo
var load = new Lod(loading);//加载
var hero = new Hero(heroMy);//实例化我方飞机
var bullets = new Bullet(bulletObj);//实例化子弹
var scores = new dayin(scoreObj)//实例化分数
var lifes = new dayin(lifeObj)//实例化生命值
var gameover = new dayin(overObj);//游戏结束
var enemyxs = new Enemy(enemy1Obj);//实例化敌机
var paused = new Bg(pauseObj);//暂停
var bulletBox = [];//存储子弹
var enemBox = [];
function bulletPaint(arr) {
for (var i = 0; i < bulletBox.length; i++) {
bulletBox[i].paint();//将存储的子弹遍历绘制
}
}
function bulletsPort() {
for (var i = 0; i < bulletBox.length; i++) {
bulletBox[i].sport();//将存储的子弹运动起来
}
}
function delet() {
for (var i = 0; i < bulletBox.length; i++) {
if (bulletBox[i].y <= 0 || bulletBox[i].bang) {
bulletBox.splice(i, 1);//控制子弹的数量
}
}
}
function enemPaint(arr) {
for (var i = 0; i < enemBox.length; i++) {
enemBox[i].paint();//将存储的敌机遍历绘制
}
}
function enemsPort() {
for (var i = 0; i < enemBox.length; i++) {
enemBox[i].sport();//将存储的敌机运动起来
}
}
function deletenem() {
for (var i = 0; i < enemBox.length; i++) {
if (enemBox[i].y >= Height || enemBox[i].boom) {
enemBox.splice(i, 1);//控制敌机的数量
}
}
}
function hit() {
for (var i = 0; i < enemBox.length; i++) {
if (enemBox[i].fillhit(hero)) {
hero.boom = true;
enemBox = [];
console.log(enemBox);
}
if (enemBox.length > 0) {
for (var j = 0; j < bulletBox.length; j++) {
if (enemBox[i].fillhit(bulletBox[j])) {
bulletBox[j].bang = true;
enemBox[i].down = true;
}
}
}
}
}
canvas.onclick = function () {//切换状态为加载
if (state === start) {
state = starting;
} else if (state === over) {
state = start;
scores.num = 0;
lifes.num = 3;
}
}
canvas.onmousemove = function (e) {//切换状态为加载
var x = e.offsetX;
var y = e.offsetY;
if (state === running) {
if (x < hero.width / 2) {
hero.x = 0;
} else if (x > Width - hero.width / 2) {
hero.x = Width - hero.width;
} else {
hero.x = x - hero.width / 2;
}
if (y > hero.Height - hero.height / 2) {
hero.y = hero.Height - hero.height;
} else if (y < hero.height / 2) {
hero.y = 0;
} else {
hero.y = y - hero.height / 2;
}
}
}
canvas.onmouseout = function () {//切换状态为暂停
if (state === running) {
state = pause;
}
}
canvas.onmouseover = function () {//切换状态为进行
if (state === pause) {
state = running;
}
}
setInterval(function () {
sky.paint();
sky.sport();
if (state === start) {
log.paint();
} else if (state === starting) {
load.paint();
load.sport();
} else if (state === running) {
hero.paint();
hero.sport();
hero.shoot();
bulletPaint();//绘制子弹
bulletsPort();//子弹运动轨迹
delet();//超过删除
enemPaint();//绘制敌机
enemsPort();//敌机运动
deletenem();//超出屏幕删除
scores.paint();//绘制分数
lifes.paint();//绘制生命值
hit();
} else if (state === pause) {
hero.paint();
bulletPaint();
paused.paint();
enemPaint();
scores.paint();//绘制分数
lifes.paint();//绘制生命值
} else if (state === over) {
scores.paint();//绘制分数
lifes.paint();//绘制生命值
gameover.paint();//绘制游戏结束
}
}, 30)
function getImg(srcc) {//制造img对象
var img = new Image();
img.src = srcc;
return img;
}
function getNum(min, max) {//获取随机数
return Math.floor(Math.random() * (max - min) + min);
}
function Bg(config) {//创造背景构造函数
this.img = config.img;
this.width = config.width;
this.height = config.height;
this.x = config.x || 0;
this.y = config.y || 0;
this.y1 = -this.height;
this.paint = function () {
ctx.drawImage(this.img, this.x, this.y);//绘制两幅图片避免出现空白
ctx.drawImage(this.img, this.x, this.y1);
}
this.sport = function () {
this.y += 1;//背景图片移动速度
this.y1 += 1;//背景图片移动速度
if (this.y >= this.height) {
this.y = -this.height;//判断位置是否需要重置
}
if (this.y1 == this.height) {
this.y1 = -this.height;
}
}
}
function Lod(config) {//创造加载构建函数
this.img = config.img;
this.width = config.width;
this.height = config.height;
this.x = 0;
this.y = Height - this.height;
this.stateIndex = 0;
this.tim = 0;
this.paint = function () {
ctx.drawImage(this.img[this.stateIndex], this.x, this.y)
}
this.sport = function () {
this.tim++;
if (this.tim % 5 == 0) {
this.stateIndex++
if (this.stateIndex == 4) {
state = running;
this.stateIndex = 0;
}
}
}
}
function Hero(config) {//创建我方飞机的构造函数
this.img = config.img;
this.width = config.width;
this.height = config.height;
this.length = config.length;
this.x = (Width - this.width) / 2;
this.y = Height - this.height - 100;
this.stateIndex = 0;
this.time = 0;
this.boom = false;
this.life = life;
this.paint = function () {
ctx.drawImage(this.img[this.stateIndex], this.x, this.y)
}
this.sport = function () {
if (this.boom) {
this.stateIndex++;
if (this.stateIndex == this.length) {
life--;
lifes.num--;
this.stateIndex = 0;
this.boom = false;
if (life <= 0) {
state = over;
}
}
} else {
this.stateIndex = this.stateIndex == 0 ? 1 : 0;
}
}
this.shoot = function () {
this.time++;
if (this.time % 3 == 0) {
bulletBox.push(new Bullet(bulletObj));
} else if (this.time % 10 == 0) {
Math.random() > 0.3 ? enemBox.push(new Enemy(enemy1Obj)) : enemBox.push(new Enemy(enemy2Obj))
} else if (this.time % 178 == 0) {
enemBox.push(new Enemy(enemy3Obj))
}
}
}
function Bullet(config) {//创建子弹的构造函数
this.img = config.img;
this.width = config.width;
this.height = config.height;
this.x = hero.x + hero.width / 2 - this.width / 2;
this.y = hero.y + this.height;
this.bang = false;
this.paint = function () {
ctx.drawImage(this.img, this.x, this.y)
}
this.sport = function () {
this.y -= this.height;
}
}
function Enemy(config) {
this.img = config.img;
this.width = config.width;
this.height = config.height;
this.length = config.length;
this.life = config.life;
this.type = config.type;
this.x = Math.random() * (Width - this.width);
this.y = -this.height;
this.startIndex = 0;//对应飞机下标
this.down = false;//是否碰撞
this.boom = false;
this.paint = function () {
ctx.drawImage(this.img[this.startIndex], this.x, this.y)
}
this.sport = function () {
if (this.down) {
this.life--;
if (this.life <= 0) {
this.startIndex++;
if (this.startIndex == this.length) {
this.startIndex = this.length - 1;
this.boom = true;
scores.num += this.type * 3
}
}
} else {
if (this.type == 3) {
this.startIndex = this.startIndex == 0 ? 1 : 0;
}
}
this.y += 2;
}
this.fillhit = function (my) {
return my.x > this.x && my.x < (this.x + this.width) && my.y <= (this.y + this.height) && my.y > 0;
}
}
function dayin(config) {
this.name = config.name;
this.num = config.num;
this.x = config.x;
this.y = config.y;
this.color = config.color;
this.paint = function () {
ctx.font = '40px 宋体 bold';
ctx.fillStyle = this.color;
ctx.fillText(this.name + ':' + this.num, this.x, this.y)
}
}