Html飞机大战(九): 使徒来袭 (设计敌机)

好家伙,本篇介绍敌机

 

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

 

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

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

然后是熟悉的移动和绘制

 

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

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

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

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

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

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

。。。。。。

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

 

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

(像啊,太像了)

Html飞机大战(九): 使徒来袭 (设计敌机)

 

 

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

 

那么,开始干活:

 

1.配置项

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

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

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

//e1用于存放小敌机的图片素材         const e1 = {             live: [],             death: [],         }         e1.live[0] = new Image();         e1.live[0].src = "img/enemy1.jpg"         e1.death[0] = new Image();         e1.death[0].src = "img/enemy1_boom1.jpg"         e1.death[1] = new Image();         e1.death[1].src = "img/enemy1_boom2.jpg"         e1.death[2] = new Image();         e1.death[2].src = "img/enemy1_boom3.jpg"          //e2用于存放小敌机的图片素材         const e2 = {             live: [],             death: [],         }         e2.live[0] = new Image();         e2.live[0].src = "img/enemy2.jpg"         e2.death[0] = new Image();         e2.death[0].src = "img/enemy2_boom1.jpg"

Html飞机大战(九): 使徒来袭 (设计敌机)Html飞机大战(九): 使徒来袭 (设计敌机) Html飞机大战(九): 使徒来袭 (设计敌机)Html飞机大战(九): 使徒来袭 (设计敌机)

 

 

 Html飞机大战(九): 使徒来袭 (设计敌机) Html飞机大战(九): 使徒来袭 (设计敌机)

 

 

 

 (图片素材来自网络)

 

 

 

 2.敌机配置项

//小敌机         const E1 = {             type: 1,             width: 57,             height: 51,             life: 1, //少点血,一下打死             score: 1,             frame: e1,             minSpeed: 20,             maxSpeed: 10,         }         //大敌机         const E2 = {             type: 2,             width: 69,             height: 95,             life: 2,             frame: e2,             minSpeed: 50,             maxSpeed: 20,         }

minSpeed: 50, maxSpeed: 20,
值得说明一下,这两个玩意是为了弄敌机的随机速度(更刺激一点,但实际上好像没什么感觉)
关于如何弄到一个”随机速度“,接着往下看


3.敌机类

class Enemy {              constructor(config) {                 //敌机类型                 this.type = config.type;                 //敌机宽,高                 this.width = config.width;                 this.height = config.height;                 //敌机的初始化位置                 this.x = Math.floor(Math.random() * (480 - config.width));                 //这里我们让飞机从头部开始渲染,所以Y轴坐标自然是飞机高度的负值                 this.y = -config.height;                 //敌机生命                 this.life = config.life;                 //敌机分数                 this.score = config.score;                 //敌机图片库                 this.frame = config.frame;                 //此刻展示的图片                 this.img = null;                 //活着的证明                 this.live = true;                 // this.minSpeed = config.minSoeed;                 // this.maxSpeed = config.speed;                 //随机去生成一个速度                 this.speed = Math.floor(Math.random() * (config.minSpeed - config.maxSpeed + 1)) + config.maxSpeed;                 //最后渲染的时间                 this.lastTime = new Date().getTime();              }             //移动敌机             move() {                 const currentTime = new Date().getTime();                 //                                  if (currentTime - this.lastTime >= this.speed) {                     // console.log("此处为this.frame"+this.frame.live[0]);                     this.img = this.frame.live[0];                     this.y++;                     //时间修正                     this.lastTime = currentTime;                 }             }              //渲染敌机方法             paint(context) {                 // console.log("此处为this.img"+this.img);                 if(this.img !=null){                     context.drawImage(this.img, this.x, this.y);                   }                              }         }

 

 

3.1.随机速度

先浅浅的说明一下

随机数方法 Math.random

 

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

然后问题来了,这是一个半开半闭区间,也就是说它会取到0但是不会取到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.敌机的移动方法

move() {                 const currentTime = new Date().getTime();                 //                                  if (currentTime - this.lastTime >= this.speed) {                     // console.log("此处为this.frame"+this.frame.live[0]);                     this.img = this.frame.live[0];                     this.y++;                     //时间修正                     this.lastTime = currentTime;                 }             }

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

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


3.3.渲染方法

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

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


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

//以下三项均为全局变量         const enemies = [];         //敌机产生的速率         const ENEMY_CREATE_INTERVAL = 2000;         let ENEMY_LASTTIME = new Date().getTime();          //全局函数 用于生产敌机         function createComponent() {             const currentTime = new Date().getTime();             const forenemyTime = new Date().getTime();              //一手经典判断             if (currentTime - ENEMY_LASTTIME >= ENEMY_CREATE_INTERVAL) {                 //当时间满足 实例化一架敌机 放入敌机数组中                 // 小飞机 70% 中飞机30%                 //用随机数去弄概率                 //[0,99]                 //Math.random()=>[0,1)*100                 //EnemyTypeRandom产生的随机数用于判断产生不同的飞机                 let EnemyTypeRandom = Math.floor(Math.random() * 100);                 if (EnemyTypeRandom > 70) {                     enemies.push(new Enemy(E1));                 } else if (EnemyTypeRandom < 30) {                     enemies.push(new Enemy(E2));                 }                 console.log(enemies);                 //更新时间                 ENEMY_LASTTIME = currentTime;             }         }


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

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


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


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

 

5.全局函数渲染


到这里就非常简单了

这里也揭开了前面的谜底

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

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

//全局函数 来移动所有的子弹/敌人组件         function judgeComponent() {             console.log("judge被触发");             for (let i = 0; i < hero.bulletList.length; i++) {                 hero.bulletList[i].move();             }             for(let i=1;i<enemies.length;i++){                 enemies[i].move();             }         }         //全局函数 来绘制所有的子弹/敌人组件         function paintComponent() {             for (let i = 0; i < hero.bulletList.length; i++) {                 hero.bulletList[i].paint(context);             }             for(let i=1;i<enemies.length;i++){                 enemies[i].paint(context);             }         }


 

6.方法调用


case RUNNING:                         sky.judge();                         sky.paint(context);                         //加载主角                          hero.paint(context);                         hero.shoot();                         createComponent();                         //子弹发射                         judgeComponent();                         paintComponent();                         deleteComponent();                         // context.drawImage(hero_frame.live[0], 0, 0);                         break;


 


 
ok,来看看效果吧:
Html飞机大战(九): 使徒来袭 (设计敌机)

 Html飞机大战(九): 使徒来袭 (设计敌机)

 

 

确实是非常地nice啊

 

发表评论

评论已关闭。

相关文章