上一章把飛機添加到屏幕上,但是飛機要發射子彈對吧?那么這一章我們就來實現一下發射子彈,並實現一個簡單的子彈對象池
先來捋一捋思路
1.創建一個子彈對象
2.然后添加一個bitmap,顯示子彈貼圖
3.判斷子彈類型(我們這里是一個子彈是敵人和主角都在使用,根據不同的狀態類型,顯示不同的圖和往不同的方向飛行)
4.子彈回收(回收子彈的意思就是把子彈狀態還原,並且從界面中移除)
5.子彈對象池的設計(綜合上面四點,設計一個簡單的對象池)
6.主角發射子彈
首先,我們先新建一個IdentityType.ts的文件,這是一個標識身份的枚舉。用來區別是主角發射的子彈還是敵人發射的子彈
/**
* 子彈類型
*/
enum IdentityType {
/**
* 敵人
*/
ENEMY,
/**
* 主角
*/
HERO
}
然后創建一個BulletObject.ts,顧名思義就是子彈對象的意思。子彈的一切操作,都在這個類里面完成
class BulletObject extends egret.DisplayObjectContainer {
_bullet: egret.Bitmap;
public btype: IdentityType;
/**
* 是否使用
*/
public IsUse: boolean = false;
_main: Main;
_speed = 5;
public constructor(main: Main) {
super();
this.width = 9;
this.height = 21;
this._main = main;
this._bullet = new egret.Bitmap();
this.addChild(this._bullet)
}
frame() {
// console.log("Bullet Frame")
if (this.IsUse) {
if (this.btype == IdentityType.ENEMY) {
this.y += this._speed;
}
if (this.btype == IdentityType.HERO) {
this.y -= this._speed;
if (this.y <= 0) {
//從父節點中移除
if (this.parent) {
this.parent.removeChild(this);
this.Recycle();
}
}
}
}
}
/**
* 使用
*/
public Use(type: IdentityType, x: number, y: number) {
this.IsUse = true;
this.x = x;
this.y = y;
this.btype = type;
if (type == IdentityType.ENEMY) {
this._bullet.texture = RES.getRes("bullet1_png")
}
else {
this._bullet.texture = RES.getRes("bullet2_png")
}
this._main.addChildAt(this, 10)
this.addEventListener(egret.Event.ENTER_FRAME, this.frame, this)
}
/**
* 回收
*/
public Recycle() {
console.log("回收子彈:" + this.btype)
this.IsUse = false;
this.removeEventListener(egret.Event.ENTER_FRAME, this.frame, this)
}
}
在構造方法中,我們初始化了子彈對象的寬高和圖片顯示對象,但是這里不給bitmap的texture賦值,需要再使用的時候賦值
Use和Recycle方法都是在外部調用。
Use是使用這個對象,做的操作是根據外部傳遞進來的值來確定自己的主角的子彈還是敵人的子彈,並給texture賦值一個對應身份狀態的圖片對象,並且監聽Enter_frame事件,在每一幀,對應移動當前子彈的坐標
Recycle方法是回收當前子彈,把子彈狀態都還原,並且移除ENTER_FRAME事件的監聽
然后實現一個簡單的對象池
/**
* 初始化對象池
*/
InitPool() {
for (var i = 0; i < 50; i++) {
var bullet = new BulletObject(this);
this._bullet.push(bullet)
}
}
/**
* 從對象池獲取一個Bullet
*/
public GetBullet(): BulletObject {
for (var i = 0; i < this._bullet.length; i++) {
if (this._bullet[i].IsUse == false) {
return this._bullet[i];
}
}
console.log("對象池已經用光了,可能是沒有回收")
}
對象池這個東西按我個人的理解就是先初始化一堆東西放到一個籃子里,然后需要的時候,就去籃子里拿,用完再放回去,實現資源的重復利用,避免頻繁的對象new的操作。
子彈和對象池都搞定了,然后來實現主角發射子彈
玩過微信打飛機游戲的都知道,主角飛機是不停的發射子彈。飛機移動位置,子彈也對應從飛機的頭部發射子彈
我的思路就是,在飛機對象里面放一個Timer定時器,500毫秒發射一個子彈。然后自定義一個子彈發射的事件,飛機發射子彈的時候,只觸發發射事件,並不實際發射子彈,我們在飛機的外部容器,也就是我們的Main這個容器中,監聽對象,把子彈對象添加到Main中
先新建一個文件OpenFireEvent.ts
class OpenFireEvent extends egret.Event {
public static EventString = "開火";
/**
* 事件類型默認是Hero
*/
public Btype: IdentityType = IdentityType.HERO;
public constructor(type: string, bubbles: boolean = false, cancelable: boolean = false) {
super(type, bubbles, cancelable);
}
}
然后再HeroObject.ts的ADDED_TO_STAGE事件里面添加一個timer
this._heroOpenFireEvent = new OpenFireEvent(OpenFireEvent.EventString);
this._heroOpenFireEvent.Btype = IdentityType.HERO;
this.addEventListener(egret.Event.ENTER_FRAME, this.frame, this);
this._timer = new egret.Timer(GameConfig.HeroOpenFireTime);
this._timer.addEventListener(egret.TimerEvent.TIMER, this.timerFunc, this);
this._timer.addEventListener(egret.TimerEvent.COMPLETE, this.timerComplete, this);
this._timer.start();
.....
public timerFunc(e: egret.TimerEvent) {
// console.log("定時開火--------------------")
this.dispatchEvent(this._heroOpenFireEvent);
}
在Timer的每次調用中,我們觸發開火事件。
這里觸發了開火的事件,需要讓這個事件生效,我們就需要在其他的地方監聽這個事件
所以在Main的添加主角對象的地方
this._Hero.addEventListener(OpenFireEvent.EventString, (e: OpenFireEvent) => {
var b = this.GetBullet();
if (b == undefined) {
console.log("對象池中沒有對象")
return;
}
var x = this._Hero.x + this._Hero.width / 2 - 5;
var y = this._Hero.y - 18;
b.Use(IdentityType.HERO, x, y)
}, this)
監聽到事件之后,從對象池中取出一個子彈對象,然后計算這個子彈應該出現的坐標x=(英雄的X坐標+英雄的寬度/2),y=(英雄的Y坐標-子彈的高度) ,不要問我為啥我還有一個-5在后面,估計是我的圖片大小不標准,然后我對坐標進行微調了一下。。這樣看起來正常一點。。。
需要源碼的,可以加QQ群來問我要哈,等這一系列學習筆記寫完了,我再上傳到github去