您现在的位置是:网站首页> 编程资料编程资料

canvas实现飞机打怪兽射击小游戏的示例代码H5 canvas实现贪吃蛇小游戏html5 canvas-2.用canvas制作一个猜字母的小游戏

2023-10-16 395人已围观

简介 这篇文章主要介绍了canvas实现飞机打怪兽射击小游戏的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

接触 canvas 也只有一个多月,第一次完整实现一个游戏流程,收获还是挺大的。

射击游戏截图

先上 demo:https://littleyljy.github.io/demo/shootgame/

游戏规则

要求玩家控制飞机发射子弹,消灭会移动的怪兽,如果全部消灭了则游戏成功,如果怪兽移动到底部则游戏失败。

  • 使用 ← 和 → 操作飞机
  • 使用空格(space)进行射击
  • 需有暂停功能
  • 多关卡

场景切换

游戏分为几个场景:

  • 开始游戏(.game-intro)
  • 游戏中(#canvas)
  • 游戏失败(.game-failed)
  • 游戏成功(.game-success)
  • 游戏通关(.game-all-success)
  • 暂停(.game-stop)

实现场景切换,其实是先把所有场景 display: none , 然后通过 js 控制 data-status 分别为 start 、playing 、failed 、success 、all-success 、stop 来实现对应场景 display: block 。

HTML 和 CSS 如下:

射击游戏

这是一个令人欲罢不能的射击游戏,使用 ← 和 → 操作你的飞机,使用空格(space)进行射击,使用回车(enter)暂停游戏。一起来消灭宇宙怪兽吧!

当前Level: 1

游戏结束

最终得分:

游戏成功

通关成功

你已经成功地防御了怪兽的所有攻击。

游戏暂停

分数:
 #game{ width: 700px; height: 600px; position: relative; left: 50%; top: 40px; margin: 0 0 0 -350px; background: linear-gradient(-180deg, #040024 0%, #07165C 97%); } .game-ui{ display: none; padding: 55px; box-sizing: border-box; height: 100%; } [data-status="start"] .game-intro { display: block; padding-top: 180px; background: url(./img/bg.png) no-repeat 430px 180px; background-size: 200px; } [data-status="playing"] .game-info { display: block; position: absolute; top:0; left:0; padding:20px; } [data-status="failed"] .game-failed, [data-status="success"] .game-success, [data-status="all-success"] .game-all-success, [data-status="stop"] .game-stop{ display: block; padding-top: 180px; background: url(./img/bg-end.png) no-repeat 380px 190px; background-size: 250px; } 

面向对象

整个游戏可以把怪兽(Enemy)、飞机(Plane)、子弹(Bullet)都当作对象,另外还有配置对象(CONFIG)和控制游戏逻辑的游戏对象(GAME)。

游戏相关配置

 /** * 游戏相关配置 * @type {Object} */ var CONFIG = { status: 'start', // 游戏开始默认为开始中 level: 1, // 游戏默认等级 totalLevel: 6, // 总共6关 numPerLine: 7, // 游戏默认每行多少个怪兽 canvasPadding: 30, // 默认画布的间隔 bulletSize: 10, // 默认子弹长度 bulletSpeed: 10, // 默认子弹的移动速度 enemySpeed: 2, // 默认敌人移动距离 enemySize: 50, // 默认敌人的尺寸 enemyGap: 10, // 默认敌人之间的间距 enemyIcon: './img/enemy.png', // 怪兽的图像 enemyBoomIcon: './img/boom.png', // 怪兽死亡的图像 enemyDirection: 'right', // 默认敌人一开始往右移动 planeSpeed: 5, // 默认飞机每一步移动的距离 planeSize: { width: 60, height: 100 }, // 默认飞机的尺寸, planeIcon: './img/plane.png' }; 

定义父类

因为怪兽(Enemy)、飞机(Plane)、子弹(Bullet)都有相同的 x, y, size, speed 属性和 move() 方法,所以可以定义一个父类 Element,通过子类继承父类的方式实现。

 /*父类:包含x y speed move() draw()*/ var Element = function (opts) { this.opts = opts || {}; //设置坐标、尺寸、速度 this.x = opts.x; this.y = opts.y; this.size = opts.size; this.speed = opts.speed; }; Element.prototype.move = function (x, y) { var addX = x || 0; var addY = y || 0; this.x += addX; this.y += addY; }; //继承原型的函数 function inheritPrototype(subType, superType) { var proto = Object.create(superType.prototype); proto.constructor = subType; subType.prototype = proto; } 

move(x, y) 方法根据传入的 (x, y) 值自叠加。

定义怪兽

怪兽包含特有属性:怪兽状态、图像、控制爆炸状态持续的 boomCount ,和 draw()、down()、direction()、booming() 方法。

 /*敌人*/ var Enemy = function (opts) { this.opts = opts || {}; //调用父类属性 Element.call(this, opts); //特有属性状态和图像 this.status = 'normal';//normal、booming、noomed this.enemyIcon = opts.enemyIcon; this.enemyBoomIcon = opts.enemyBoomIcon; this.boomCount = 0; }; //继承Element方法 inheritPrototype(Enemy, Element); //方法:绘制敌人 Enemy.prototype.draw = function () { if (this.enemyIcon && this.enemyBoomIcon) { switch (this.status) { case 'normal': var enemyIcon = new Image(); enemyIcon.src = this.enemyIcon; ctx.drawImage(enemyIcon, this.x, this.y, this.size, this.size); break; case 'booming': var enemyBoomIcon = new Image(); enemyBoomIcon.src = this.enemyBoomIcon; ctx.drawImage(enemyBoomIcon, this.x, this.y, this.size, this.size); break; case 'boomed': ctx.clearRect(this.x, this.y, this.size, this.size); break; default: break; } } return this; }; //方法:down 向下移动 Enemy.prototype.down = function () { this.move(0, this.size); return this; }; //方法:左右移动 Enemy.prototype.direction = function (direction) { if (direction === 'right') { this.move(this.speed, 0); } else { this.move(-this.speed, 0); } return this; }; //方法:敌人爆炸 Enemy.prototype.booming = function () { this.status = 'booming'; this.boomCount += 1; if (this.boomCount > 4) { this.status = 'boomed'; } return this; } 
  • draw() 主要是根据怪兽的状态绘制不同的图像。
  • down() 调用父类 move() 方法,传入 y 值控制怪兽向下移动。
  • direction() 根据传入的方向值控制左/右移动。
  • booming() 让爆炸状态持续4帧,4帧后再消失。

定义子弹

子弹有 fly() 、draw() 方法。

 /*子弹*/ var Bullet = function (opts) { this.opts = opts || {}; Element.call(this, opts); }; inheritPrototype(Bullet, Element); //方法:让子弹飞 Bullet.prototype.fly = function () { this.move(0, -this.speed); return this; }; //方法:绘制子弹 Bullet.prototype.draw = function () { ctx.beginPath(); ctx.strokeStyle = '#fff'; ctx.moveTo(this.x, this.y); ctx.lineTo(this.x, this.y - CONFIG.bulletSize); ctx.closePath(); ctx.stroke(); return this; }; 
  • fly() 调用父类 move() 方法,传入 y 值控制子弹向上移动。
  • draw() 因为子弹其实就是一条长度为 10 的直线,通过绘制路径的方式画出子弹。

定义飞机

飞机对象包含特有属性:状态、宽高、图像、横坐标最大最小值,有 hasHit()、draw()、direction()、shoot()、drawBullets() 方法。

 /*飞机*/ var Plane = function (opts) { this.opts = opts || {}; Element.call(this, opts); //特有属性状态和图像 this.status = 'normal'; this.width = opts.width; this.height = opts.height; this.planeIcon = opts.planeIcon; this.minX = opts.minX; this.maxX = opts.maxX; //子弹相关 this.bullets = []; this.bulletSpeed = opts.bulletSpeed || CONFIG.bulletSpeed; this.bulletSize = opts.bulletSize || CONFIG.bulletSize; }; //继承Element方法 inheritPrototype(Plane, Element); //方法:子弹击中目标 Plane.prototype.hasHit = function (enemy) { var bullets = this.bullets; for (var i = bullets.length - 1; i >= 0; i--) { var bullet = bullets[i]; var isHitPosX = (enemy.x < bullet.x) && (bullet.x < (enemy.x + enemy.size)); var isHitPosY = (enemy.y < bullet.y) && (bullet.y < (enemy.y + enemy.size)); if (isHitPosX && isHitPosY) { this.bullets.splice(i, 1); return true; } } return false; }; //方法:绘制飞机 Plane.prototype.draw = function () { this.drawBullets(); var planeIcon = new Image(); planeIcon.src = this.planeIcon; ctx.drawImage(planeIcon, this.x, this.y, this.width, this.height); return this; }; //方法:飞机方向 Plane.prototype.direction = function (direction) { var speed = this.speed; var planeSpeed; if (direction === 'left') { planeSpeed = this.x < this.minX ? 0 : -speed; } else { planeSpeed = this.x > this.maxX ? 0 : speed; } console.log('planeSpeed:', planeSpeed); console.log('this.x:', this.x); console.log('this.minX:', this.minX); console.log('this.maxX:', this.maxX); this.move(planeSpeed, 0); return this;//方便链式调用 }; //方法:发射子弹 Plane.prototype.shoot = function () { var bulletPosX = this.x + this.width / 2; this.bullets.push(new Bullet({ x: bulletPosX, y: this.y, size: this.bulletSize, speed: this.bulletSpeed })); return this; }; //方法:绘制子弹 Plane.prototype.drawBullets = function () { var bullets = this.bullets; var i = bullets.length; while (i--) { var bullet = bullets[i]; bullet.fly(); if (bullet.y <= 0) { bullets.splice(i, 1); } bullet.draw(); } }; 
  • hasHit() 判断飞机发射的子弹是否击中怪兽,主要是判断子弹的横坐标是否在[怪兽横坐标,怪兽横坐标+怪兽高度]范围内,同时子弹的纵坐标在[怪兽纵坐标,怪兽纵坐标+怪兽宽度]范围内,击中返回 true,并移除该子弹。
  • draw() 绘制子弹和飞机。
  • direction() 因为飞机移动范围有左右边界,需要判断飞机横坐标是否到达边界,如果到达边界 planeSpeed 为 0,不再移动。
  • shoot() 创建子弹对象,保存到 bullets 数组,子弹横坐标为飞机横坐标加上飞机宽度的一半。
  • drawBullets() 绘制子弹,从数组最后往回遍历子弹对象数组,调用子弹 fly() 方法,如果子弹向上飞出屏幕,则移除这颗子弹。

定义键盘事件

键盘事件有以下几种状态:

  • keydown:用户在键盘上按下某按键时发生。一直按着某按键则会不断触发(opera 浏览器除外)。
  • keypress:用户按下一个按键,并产生一个字符时发生(也就是不管类似 shift、alt、ctrl 之类的键,就是说用户按了一个能在屏幕上输出字符的按键 keypress 事件才会触发)。一直按着某按键则会不断触发。
  • keyup:用户释放某一个按键是触发。

因为飞机需要按下左键(keyCode=37)右键(keyCode=39)时(keydown)一直移动,释放时 keyup 不移动。按下空格(keyCode=32)或上方向键(keyCode=38)时(keydown)发射子弹,释放时 keyup 停止发射。另外按下回车键(keyCode=13)暂停游戏。所以,需要定义一个 KeyBoard 对象监听 onkeydown 和 onkeyup 是否按下或释放某个键。

因为左右键是矛盾的,为保险起见,按下左键时需要把右键 设为 false。右键同理。

相关内容

-六神源码网