好家伙,本篇介绍敌机

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

 

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

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

然后是熟悉的移动和绘制

 

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

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

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

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

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

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

。。。。。。

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

 

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

(像啊,太像了)

 

 

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

 

那么,开始干活:

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"

 

  

 (图片素材来自网络)

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,来看看效果吧:

 

 

确实是非常地nice啊

最新文章

  1. Reveal分析IOS界面,plist文件读取
  2. LAMP-五分钟搭建个人论坛
  3. javascript 字符串加密的几种方法
  4. sql2008 查询字段所属表
  5. 关于刷Sylvain/burst_ind分支的一些问题解答
  6. MvcAdmin功能介绍
  7. hdu 1358 KMP的next数据运用
  8. linux_无密登录
  9. 通过 DynamicLinq 简单实现 N-Tier 部署下的服务端数据库通用分页
  10. java_JDBC(2)
  11. vue-cli 脚手架目录结构说明
  12. [离散时间信号处理学习笔记] 8. z逆变换
  13. Socket 网络通信
  14. salt 配置管理
  15. Java 使用 happen-before 规则实现共享变量的同步操作
  16. 老男孩Python全栈视频
  17. chromium 使用 flash
  18. 136. Single Number(位运算)
  19. forEach、map、filter、find、sort、some等易错点整理
  20. 拒绝采样 Rejection Sampling

热门文章

  1. C# 使用SpecFlow创建BDD测试用例
  2. Python if-else的简单表示
  3. 29.MySQL高级SQL语句
  4. 5G的发布加快了智慧城市/三维物联网等行业的发展
  5. Bitbucket 使用 SSH 拉取仓库失败的问题
  6. 多校联训 DP 专题
  7. CF1042E Vasya and Magic Matrix 题解
  8. 《ASP.NET Core 6框架揭秘》样章发布[200页/5章]
  9. 本地使用 Docker Compose 与 Nestjs 快速构建基于 Dapr 的 Redis 发布/订阅分布式应用
  10. Windows JDK 的下载与安装