好家伙,本篇介紹敵機
好了,按照慣例我們來理一下思路:
我們有一個敵機類,第一步當然是實體一個敵機物件,
然后我們把這個敵機放入我們的敵機群(敵機陣列)
然后是熟悉的移動和繪制
那我們回顧一下子彈的生成邏輯
變數: 子彈 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 = https://www.cnblogs.com/FatTiger4399/archive/2022/09/01/"img/enemy1.jpg"
e1.death[0] = new Image();
e1.death[0].src = https://www.cnblogs.com/FatTiger4399/archive/2022/09/01/"img/enemy1_boom1.jpg"
e1.death[1] = new Image();
e1.death[1].src = https://www.cnblogs.com/FatTiger4399/archive/2022/09/01/"img/enemy1_boom2.jpg"
e1.death[2] = new Image();
e1.death[2].src = https://www.cnblogs.com/FatTiger4399/archive/2022/09/01/"img/enemy1_boom3.jpg"
//e2用于存放小敵機的圖片素材
const e2 = {
live: [],
death: [],
}
e2.live[0] = new Image();
e2.live[0].src = https://www.cnblogs.com/FatTiger4399/archive/2022/09/01/"img/enemy2.jpg"
e2.death[0] = new Image();
e2.death[0].src = https://www.cnblogs.com/FatTiger4399/archive/2022/09/01/"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啊
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/503418.html
標籤:其他
下一篇:JS中==和===的區別

