好家伙,本篇介绍敌机

好了,按照惯例我们来理一下思路:

 

我们有一个敌机类,第一步当然是实例一个敌机对象,

然后我们把这个敌机放入我们的敌机群(敌机数组)

然后是熟悉的移动和绘制

 

那我们回顾一下子弹的生成逻辑

变量: 子弹  bullet  弹夹(用来装子弹的东西)bulletList[] 

方法:装填子弹  绘制子弹 移动子弹

子弹发射的物理逻辑是很简单的:

生产第一个子弹,推入弹夹中,绘制弹夹(即绘制弹夹中的所有子弹),

生产第二个子弹,同样推入弹夹,移动第一颗子弹(应该说是改变第一颗子弹的y坐标),绘制弹夹中的所有子弹 

。。。。。。

生产第n个子弹,推入弹夹中,改变第n-1颗子弹的Y坐标,绘制弹夹中的所有子弹

 

有没有感觉到两者逻辑的相似之处

(像啊,太像了)

 

 

子弹和敌机的处理,本质上是用的是同一套逻辑

 

那么,开始干活:

1.配置项

这里我们会用到两种类型的配置项E1和E2

(因为我们有两种类型的敌人,大敌机和小敌机,其中e1为小敌机(血少),e2为大敌机(血厚))

先设置一个数组存放图片资源

  1. //e1用于存放小敌机的图片素材
  2. const e1 = {
  3. live: [],
  4. death: [],
  5. }
  6. e1.live[0] = new Image();
  7. e1.live[0].src = "img/enemy1.jpg"
  8. e1.death[0] = new Image();
  9. e1.death[0].src = "img/enemy1_boom1.jpg"
  10. e1.death[1] = new Image();
  11. e1.death[1].src = "img/enemy1_boom2.jpg"
  12. e1.death[2] = new Image();
  13. e1.death[2].src = "img/enemy1_boom3.jpg"
  14. //e2用于存放小敌机的图片素材
  15. const e2 = {
  16. live: [],
  17. death: [],
  18. }
  19. e2.live[0] = new Image();
  20. e2.live[0].src = "img/enemy2.jpg"
  21. e2.death[0] = new Image();
  22. e2.death[0].src = "img/enemy2_boom1.jpg"

 

  

 (图片素材来自网络)

2.敌机配置项

  1. //小敌机
  2. const E1 = {
  3. type: 1,
  4. width: 57,
  5. height: 51,
  6. life: 1, //少点血,一下打死
  7. score: 1,
  8. frame: e1,
  9. minSpeed: 20,
  10. maxSpeed: 10,
  11. }
  12. //大敌机
  13. const E2 = {
  14. type: 2,
  15. width: 69,
  16. height: 95,
  17. life: 2,
  18. frame: e2,
  19. minSpeed: 50,
  20. maxSpeed: 20,
  21. }
  1. minSpeed: 50,
  2. maxSpeed: 20,
    值得说明一下,这两个玩意是为了弄敌机的随机速度(更刺激一点,但实际上好像没什么感觉)
    关于如何弄到一个”随机速度“,接着往下看

3.敌机类

  1. class Enemy {
  2. constructor(config) {
  3. //敌机类型
  4. this.type = config.type;
  5. //敌机宽,高
  6. this.width = config.width;
  7. this.height = config.height;
  8. //敌机的初始化位置
  9. this.x = Math.floor(Math.random() * (480 - config.width));
  10. //这里我们让飞机从头部开始渲染,所以Y轴坐标自然是飞机高度的负值
  11. this.y = -config.height;
  12. //敌机生命
  13. this.life = config.life;
  14. //敌机分数
  15. this.score = config.score;
  16. //敌机图片库
  17. this.frame = config.frame;
  18. //此刻展示的图片
  19. this.img = null;
  20. //活着的证明
  21. this.live = true;
  22. // this.minSpeed = config.minSoeed;
  23. // this.maxSpeed = config.speed;
  24. //随机去生成一个速度
  25. this.speed = Math.floor(Math.random() * (config.minSpeed - config.maxSpeed + 1)) + config.maxSpeed;
  26. //最后渲染的时间
  27. this.lastTime = new Date().getTime();
  28. }
  29. //移动敌机
  30. move() {
  31. const currentTime = new Date().getTime();
  32. //
  33. if (currentTime - this.lastTime >= this.speed) {
  34. // console.log("此处为this.frame"+this.frame.live[0]);
  35. this.img = this.frame.live[0];
  36. this.y++;
  37. //时间修正
  38. this.lastTime = currentTime;
  39. }
  40. }
  41. //渲染敌机方法
  42. paint(context) {
  43. // console.log("此处为this.img"+this.img);
  44. if(this.img !=null){
  45. context.drawImage(this.img, this.x, this.y);
  46. }
  47. }
  48. }

3.1.随机速度

  1. 先浅浅的说明一下

随机数方法 Math.random

这玩意会在[0,1)也就是在0到1之间取一个值

然后问题来了,这是一个半开半闭区间,也就是说它会取到0但是不会取到1

  1. this.speed = Math.floor(Math.random() * (config.minSpeed - config.maxSpeed + 1)) + config.maxSpeed;

在这里我们要取的是一个10到20之间的速度由于我们向下取整

Math.floor(Math.random() * (config.minSpeed - config.maxSpeed )) + config.maxSpeed;

必然只能取得10-19之间的数


于是我们在(config.minSpeed - config.maxSpeed )中加一

变成(Math.random() * (config.minSpeed - config.maxSpeed +1))


(聪明的你一定能很快想明白,而愚蠢的我想了很久才想明白)


3.2.敌机的移动方法

  1. move() {
  2. const currentTime = new Date().getTime();
  3. //
  4. if (currentTime - this.lastTime >= this.speed) {
  5. // console.log("此处为this.frame"+this.frame.live[0]);
  6. this.img = this.frame.live[0];
  7. this.y++;
  8. //时间修正
  9. this.lastTime = currentTime;
  10. }
  11. }

移动同样的用时间判定的方式去控制速率

现在和过去的时间差大于速度,更新地址


3.3.渲染方法

  1. paint(context) {
  2. // console.log("此处为this.img"+this.img);
  3. if(this.img !=null){
  4. context.drawImage(this.img, this.x, this.y);
  5. }
  6. }

嗯,非常好理解了,多加的一个if是为了防止出现空img导致报错


4.全局函数(生产敌机)

  1. //以下三项均为全局变量
  2. const enemies = [];
  3. //敌机产生的速率
  4. const ENEMY_CREATE_INTERVAL = 2000;
  5. let ENEMY_LASTTIME = new Date().getTime();
  6. //全局函数 用于生产敌机
  7. function createComponent() {
  8. const currentTime = new Date().getTime();
  9. const forenemyTime = new Date().getTime();
  10. //一手经典判断
  11. if (currentTime - ENEMY_LASTTIME >= ENEMY_CREATE_INTERVAL) {
  12. //当时间满足 实例化一架敌机 放入敌机数组中
  13. // 小飞机 70% 中飞机30%
  14. //用随机数去弄概率
  15. //[0,99]
  16. //Math.random()=>[0,1)*100
  17. //EnemyTypeRandom产生的随机数用于判断产生不同的飞机
  18. let EnemyTypeRandom = Math.floor(Math.random() * 100);
  19. if (EnemyTypeRandom > 70) {
  20. enemies.push(new Enemy(E1));
  21. } else if (EnemyTypeRandom < 30) {
  22. enemies.push(new Enemy(E2));
  23. }
  24. console.log(enemies);
  25. //更新时间
  26. ENEMY_LASTTIME = currentTime;
  27. }
  28. }
  1.  

这里同样的,我们用随机数去控制出现大/小敌机的概率

(E1,E2分别是大小敌机的配置项)

  1.  
  1. let EnemyTypeRandom = Math.floor(Math.random() * 100);
  2. if (EnemyTypeRandom > 70) {
                //产小敌机
  3. enemies.push(new Enemy(E1));
  4. } else if (EnemyTypeRandom < 30) {
                //产大敌机
  5. enemies.push(new Enemy(E2));
  6. }
  1.  

你细品,这个控制得还是非常巧妙的

5.全局函数渲染


到这里就非常简单了

这里也揭开了前面的谜底

因为敌机生成和子弹生成的逻辑太过相似

所以我们把他们放到同一个全局函数是一个非常明智的选择

  1. //全局函数 来移动所有的子弹/敌人组件
  2. function judgeComponent() {
  3. console.log("judge被触发");
  4. for (let i = 0; i < hero.bulletList.length; i++) {
  5. hero.bulletList[i].move();
  6. }
  7. for(let i=1;i<enemies.length;i++){
  8. enemies[i].move();
  9. }
  10. }
  11. //全局函数 来绘制所有的子弹/敌人组件
  12. function paintComponent() {
  13. for (let i = 0; i < hero.bulletList.length; i++) {
  14. hero.bulletList[i].paint(context);
  15. }
  16. for(let i=1;i<enemies.length;i++){
  17. enemies[i].paint(context);
  18. }
  19. }
  1.  

6.方法调用

  1.  
  1. case RUNNING:
  2. sky.judge();
  3. sky.paint(context);
  4. //加载主角
  5. hero.paint(context);
  6. hero.shoot();
  7. createComponent();
  8. //子弹发射
  9. judgeComponent();
  10. paintComponent();
  11. deleteComponent();
  12. // context.drawImage(hero_frame.live[0], 0, 0);
  13. break;
  1.  
  1.  
  1.  
    ok,来看看效果吧:

 

 

确实是非常地nice啊

Html飞机大战(九): 使徒来袭 (设计敌机)的更多相关文章

  1. 一、利用Python编写飞机大战游戏-面向对象设计思想

    相信大家看到过网上很多关于飞机大战的项目,但是对其中的模块方法,以及使用和游戏工作原理都不了解,看的也是一脸懵逼,根本看不下去.下面我做个详细讲解,在做此游戏需要用到pygame模块,所以这一章先进行 ...

  2. 飞机大战编写以及Java的面向对象总结

    面向对象课程完结即可编写一个简单的飞机大战程序.我觉得我需要总结一下 飞机大战中类的设计: 父类:FlyingObject(抽象类) 接口:Award .Enemy 子类:Hero.Bullet.Ai ...

  3. cocos2dx 3.0 飞机大战

    因为课程须要.然后又水平有限.所以写了个飞机大战.加上不会画画.所以图片资源也是从微信apk解压出来的,设计思路參考的偶尔e网事. 闲话不说.先讲一下设计.大体上一共分为3个场景.场景以下是Layer ...

  4. 微信5.0 Android版飞机大战破解无敌模式手记

    微信5.0 Android版飞机大战破解无敌模式手记 转载: http://www.blogjava.net/zh-weir/archive/2013/08/14/402821.html 微信5.0 ...

  5. web版canvas做飞机大战游戏 总结

    唠唠:两天的时间跟着做了个飞机大战的游戏,感觉做游戏挺好的.说是用html5做,发现全都是js.说js里一切皆为对象,写的最多的还是函数,都是函数调用.对这两天的代码做个总结,希望路过的大神指点一下, ...

  6. MFC实现简单飞机大战(含游戏声音)

    1 实验内容 本实验主要是实现简单的飞机大战游戏,包含游戏声音.碰撞后爆炸效果,有大小敌机等.所用到的知识点如下: 1.贴图技术 2.飞机类.子弹类实现 3.位图移动 4.碰撞判断,实现爆炸效果 5. ...

  7. 微信小游戏 demo 飞机大战 代码分析 (一)(game.js, main.js)

    微信小游戏 demo 飞机大战 代码分析(一)(main.js) 微信小游戏 demo 飞机大战 代码分析(二)(databus.js) 微信小游戏 demo 飞机大战 代码分析(三)(spirit. ...

  8. Cocos2d-x 3.0final 终结者系列教程16-《微信飞机大战》实现

    看到cocos2d-x推出了3.1版本号,真是每月一次新版本号,速度. 另一个好消息就是http://cn.cocos2d-x.org/上线了,祝贺!啥时候把我的视频和教程放上去呢?!! . 视频下载 ...

  9. 【Web前端Talk】无聊吗?写个【飞机大战】来玩吧(上篇)

    01前言介绍 微信小游戏是基于微信客户端的游戏,它即点即玩,无需下载安装,体验轻便,可以和微信内的好友一起玩,比如PK.围观等,享受小游戏带来的乐趣.那如何开发一款属于自己的小游戏呢? 源码地址: h ...

随机推荐

  1. SAP 隐式增强 Enhancement point

    1.进入编辑器:SE38/SE37/SE24 Edit-->Enhancement Operations-->Create Option 2.填写相关信息,点击对号. 3.点击Enhanc ...

  2. NC14683 储物点的距离

    NC14683 储物点的距离 题目 题目描述 一个数轴,每一个储物点会有一些东西,同时它们之间存在距离. 每次给个区间 \([l,r]\) ,查询把这个区间内所有储物点的东西运到另外一个储物点的代价是 ...

  3. .NET服务治理之限流中间件-FireflySoft.RateLimit

    概述 FireflySoft.RateLimit自2021年1月发布第一个版本以来,经历了多次升级迭代,目前已经十分稳定,被很多开发者应用到了生产系统中,最新发布的版本是3.0.0. Github:h ...

  4. SuperSocket 1.6 创建一个简易的报文长度在头部的Socket服务器

    我们来做一个头为6位报文总长度,并且长度不包含长度域自身的例子.比如这样的Socket报文000006123456. 添加SuperSocket.Engine,直接使用Nuget搜索SuperSock ...

  5. CSS 盒子模型(一)

    CSS 盒子模型(一) 本人在校学生,主学后端,后来发现前端的基础都忘得差不多了才想着写文章回来复习!欢迎留言交流. 什么是盒子呢? 拿下举例,我们可以把每个红框都比作一个盒子,他们可以是任意的 HT ...

  6. 感知器网络(MP模型)和自适应线性元件

  7. Tomcat深入浅出——Filter与Listener(五)

    一.Filter过滤器 1.1 Filter过滤器的使用 这是过滤器接口的方法 public interface Filter { default void init(FilterConfig fil ...

  8. 再见Docker!Containerd安装与使用

    Containerd 的技术方向和目标 简洁的基于 gRPC 的 API 和 client library 完整的 OCI 支持(runtime 和 image spec) 同时具备稳定性和高性能的定 ...

  9. ROS机械臂 Movelt 学习笔记1 | 基础准备

    环境:Ubuntu18.04 + ROS Melodic 1. 安装ROS 官网下载安装步骤:http://wiki.ros.org/melodic/Installation/Ubuntu 一键安装的 ...

  10. 1.JS中变量的重新声明和提升

    重新声明 1.允许在程序的任何位置使用 var 重新声明 JavaScript 变量: 实例 var x = 10; // 现在,x 为 10 var x = 6; // 现在,x 为 6 2.在相同 ...