Egret实战开发笔记,飞行射击游戏(五)

今天是开发飞行射击游戏第五天,爆炸特效体系与NPC子弹弹幕。

简介

实现爆炸特效体系与NPC子弹弹幕。

飞机爆炸也是一个类,爆炸也是个数可变的,也需要特效管理者类。

实现效果

本来想路视频转GIF的,但是gif文件过大,超过5M又上传不了,而且压缩后失帧严重,仅截取了一部分转为gif, 请大家原谅。
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
代码及过程

一、特效体系:爆炸

爆炸是一个动画帧,播放完就可以,因为比较简单, 用一个类+id 种类索引通过状态机的形式区分多种不同的爆炸。
创建特效类TX和特效管理者类TXManager类
1)TX类

	public id:number;		//代表种类
	public vis:boolean;		//被工厂管理的都需要vis
	public fi:number;		//代表动画帧播放第几帧
	public t:number;		//延时计时器 记录延时如:3次主循环出现,
	public m:number;		//状态
	public l:number;		//特效播放的总时长
	public tm:TXManager;	//上级指针

构造:

update(){}

动画延时播放:当create时,它可能不存在,如果有延时,比如延时5,visible可见性是false,但是vis是true。
如果t是0,就不用延时,一种是倒计时状态,没有出现。另一种是播放状态
m 0是倒计时 1是播放。

在构造最后

if(this.t > 0){
			//虽然在仓库,但是看不到
			//0 是倒计时, 1是播放
			this.visible = false;
			this.m = 0;
		}else{
			this.visible = true;
			this.m = 1 ;
		}

TX类


```c
class TX extends egret.Sprite{

	public im:egret.Bitmap;
	public id:number;		//代表种类
	public vis:boolean;		//被工厂管理的都需要vis
	public fi:number;		//代表动画帧播放第几帧
	public t:number;		//延时计时器 记录延时如:3次主循环出现,
	public m:number;		//状态
	public l:number;		//特效播放的总时长

	public tm:TXManager;	//上级指针

	public constructor(id:number,x:number, y:number,
		t:number,l:number,tm:TXManager) {
	
		super();
		this.id = id;
		this.x = x ;
		this.y = y;
		this.t = t;
		this.l = l;
		this.m = 0;
		this. fi = 0 ;
		this.vis = true;	//若它是false 工厂内对象消失
		this.tm = tm;
		
		this.im = Main.createBitmapByName("tx1_1_png");
		this.anchorOffsetX = this.im.width/2;
		this.anchorOffsetY = this.im.height/2;
		this.addChild(this.im);

		if(this.t > 0){
			//虽然在仓库,但是看不到
			//0 是倒计时, 1是播放
			this.visible = false;
			this.m = 0;
		}else{
			this.visible = true;
			this.m = 1 ;
		}


	}

	public update(){
		 switch(this.m ){
			 //
			 case 0:
			 	//倒计时
			 	this.t-- ;
				 if(this.t <=0 ){
					 //出现爆炸
					 this.visible = true;
					 this.m = 1;		//播放
				 }
			 break;
			 case 1 :
			 	//动画播放
			 	this.fi++;
				 //
				 if(this.fi >= this.l){
					 this.vis =false;		//工厂里对象销毁
				 }else{						//没有播放完,动画帧切换
					 //动画切换公式	fi从0开始,到9,
					 //10是动画的张数有几张动画就*几。 fi是动画帧,l是动画长度,
					 this.im.texture = RES.getRes("tx1_"+Math.floor(this.fi*10/this.l + 1)+"_png");
				 }
		 }
	}
}

``

TXManager类

class TXManager extends egret.Sprite{
	
	public tm:Array<TX>;		//Array动态数组(容器),通过添加,添加对象,移除对象。
	public game:MainGame;

	public constructor(game:MainGame) {
		super();
		this.game = game;
		this.tm = new Array();
	}
	//每create一次就new一个
	public create(id:number,x:number,y:number,t:number,l:number,game:MainGame){
		//生成子弹
		let one = new TX(id,x,y,t,l,this);
		//添加到世界
		this.addChild(one);
		//放到仓库数组最后
		this.tm.push(one);
		
	}
	//更新所有子弹,找到每一颗子弹,每颗更新
	public update(){
		//整个仓库长度 ,利用循环可 以循环出所有子弹
		for(let i = 0 ; i < this.tm.length ; i++){
			//找到每颗子弹
			let one = this.tm[i];
			one.update();
			//若子弹太多,仓库会满,所以子弹需要移除
			//子弹出屏,vis == false。移除
			if(one.vis == false){
				//先从场景移除
				this.removeChild(one);
				//仓库移除 
				this.tm.splice(i ,1);
				//移除一个对象,长度-1
				i--;
			}
		}
	}
}

在Maingame中申请public tm:TXManager
构造:this.tm = tm; this.addChild(this.tm);
更新 this.tm.update();
在ZDManager类中

if(npc.hp <= 0 ){
	for(let k = 0 ; k < 10 ; k ++){				//-50 到 +50 
		this.game.tm.create( 0, npc.x + Math.random()*100 -50 ,
						npc.y + Math.random()*100 -50 ,
						Math.floor(Math.random() * 5), 10,this.game);
					}
					npc.vis = false;
				}

实现爆炸效果 在这里插入图片描述

在上面的设置中,所有的NPC爆炸都是一个样子,如果出现不同的NPC爆炸怎么办?
有限状态机实现多样化都是在子类中,case。
如果对象和对象当中,想出现不同的现象,需要不同的代码。
需要给每一个子类,每一个特殊的NPC写相应的爆炸,

二.爆炸效果多样性
子类实现不同的dead()方法,
在NPC父类中 添加:public abstract dead();
实现每个NPC不同的死亡效果,在每个子类中都添加。
在NPC0,1中添加

public dead(){
		for(let k = 0 ; k < 10 ; k ++){			
			this.nm.game.tm.create( 0, this.x + Math.random()*100 - 50 ,
			this.y + Math.random()*100 - 50 ,
			Math.floor(Math.random() * 5), 10,this.nm.game);
		}
	}

之后再ZDManager中调用dead方法
在这里插入图片描述

父类当中有抽象方法,但是实际执行每个子类的代码。

三.实现NPC的特殊弹幕
boss也是NPC,也得接受NPCManager的管理
创建BOSS0类

class BOSS0 extends NPC{

	public im:egret.Bitmap;
	public m:number;
	public t:number;

	public constructor(x:number,y:number,nm:NPCManager) {

		super(nm);
		this.x = x ;this.y = y;
		this.im = Main.createBitmapByName("boss50_png");
		this.im.anchorOffsetX = this.im.width/2;
		this.im.anchorOffsetY = this.im.height/2;
		this.addChild(this.im);

		this.m = this.t = 0;
		this.hp = 1000;
	}


	public update(){

	}
	public isHit(x:number,y:number):boolean{
		return false;
	}
	public dead(){

	}
}

添加BOSS更新方法

switch(this.m){
			//boss停的位置
			case 0:
				this.y+=10;
				if(this.y >= 150){
					this.t = 20 ;
					this.m = 1;
				}
			break;
			//等待状态
			case 1:
				this.t--;
				if(this.t <=0){
					this.m = 10;
					this.t = 0 ;
				}
			break;
			//发射子弹
			case 10:
				this.t++;
				//每隔3次主循环发射一颗
				if(this.t % 3 == 0 ){
					for(let i = 0 ; i < 5 ; i++){
						//5串子弹
						//160+i*10 间隔10度发一颗
					this.nm.game.nzm.create(1, this.x, this.y, 10 ,160+i*10,this.nm.game );
					}
				}
				if(this.t >= 20){
					this.t = 10;
					this.m = 1;
				}
			break;
		}

①一串子弹
在这里插入图片描述
在这里插入图片描述

②实现环形的子弹
//发射子弹

case 10:
	this.t++;
	//每隔3次主循环发射一颗
	if(this.t % 3 == 0 ){
		for(let i = 0 ; i < 36 ; i++){
			//5穿子弹
			//160+i*10 间隔10度发一颗
		this.nm.game.nzm.create(1, this.x, this.y, 10 ,160+i*10,this.nm.game );
		}
	}
	if(this.t >= 20){
		this.t = Math.random()* 20 + 10;
		this.m = 1;
	}
break;

③漩涡形子弹
在这里插入图片描述

case 11:
	this.t++;
	//随着t的++,发射子弹的角度不断变化
	this.nm.game.nzm.create(1, this.x, this.y, 10 ,180+this.t*10,this.nm.game );	//逆时针转
	this.nm.game.nzm.create(1, this.x, this.y, 10 ,180 - this.t*10,this.nm.game );	//顺时针转
	//子弹发射跟t有关联	间隔10度发一颗 
	if(this.t >= 36){
		this.t = Math.random()* 20 + 10;
		this.m = 1;		
				}
break;

④矩阵型子弹
在这里插入图片描述
在这里插入图片描述

case 12:
	this.t++;
	//每10次主循环中15,16,17,18,19发射子弹 5颗										
	if(this.t % 20 > 14){
	for(let i = 0 ; i < 5 ; i++){
	//间隔20度打一排,从130度开始打5颗,
	//每十次主循环是一波,this.t/10,前十次结果为10,11,12,13结果为1,取整*20就是每波的距离
			this.nm.game.nzm.create(1,this.x,this.y,10,Math.floor(this.t/20)*20 +130+i*5,this.nm.game );
			//135度左右打5颗,
		}
	}
	if(this.t >= 100){
		this.t = Math.random() * 20 + 10;
		this.m =1 ;
	}
			break;

⑤鞭形子弹
后发先至效果
在这里插入图片描述

//鞭形子弹

case 13:
	this.t++;
	//速度随着时间增加,产生后发先至效果
	this.nm.game.nzm.create( 1, this.x - 50, this.y, 6 + this.t *2 ,190 - this.t,this.nm.game );
	this.nm.game.nzm.create( 1, this.x+50, this.y, 6 + this.t *2 ,170 + this.t,this.nm.game );
	if(this.t >= 10){
			this.t = Math.random() * 20 + 10;
			this.m =1 ;
	}
break;

⑥朝向性子弹
在这里插入图片描述

NPC朝向玩家发射子弹并不是跟踪形子弹

朝向性子弹由子弹发射位置和玩家位置决定

过程:
NZDManager将角度参数设为缺省值 n?:numbe。 若不赋值,值为空。

原理:
在这里插入图片描述

0角度向上顺时针转过n角度,求角度n = arctan( px-x /y -py)
代码为:
在NZDManager中
在这里插入图片描述

//如果n角度是空的,就是没有赋值
	if(!n){
		n = Math.atan2(this.game.player.x - x,y - this.game.player.y);
		//注意:三角函数算出的都是弧度制,还需要弧度制转换角度制
		n = n * 180/Math.PI;

	}

BOSS0中

//朝向型子弹
			case 14 :
				this.t++;
				//打5团,每10次主循环打一组,
				if(this.t % 10 > 4){
					this.nm.game.nzm.create( 1, this.x - 50, this.y, 15);
				}
				if(this.t >= 50){
					this.t = Math.random() * 20 + 10;
					this.m =1 ;
				}
				
			break;

实现随机类型子弹
在等待状态case1中 this.m = Math.floor(Math.random() * 5 ) + 10; //随机类型子弹

四.不规则图形的碰撞

白鹭引擎中有像素级的碰撞检测,但是在游戏中,子弹与BOSS的碰撞,假如一颗子弹100*100像素,一颗子弹就检测了10000次,50颗子弹就是50万次,游戏会非常卡。那么不开启像素级碰撞,不规则图形的碰撞怎么解决呢?在这里插入图片描述
图片宽 146 高 166 各中心点 73,83像素
在这里插入图片描述

if(Math.abs(x - this.x ) < 45 &&
		Math.abs(y - this.y )< 82){
			return true;
		}	

不规则图形拆成规则图形。
在这里插入图片描述

//宽73,高36

if(Math.abs(x - this.x) < 73 &&
		Math.abs(y -(this.y + 44)) < 36){
			return true;
		}

要用矩形中心检测,横坐标在中心,纵坐标在选框中,实际比y多44,
在这里插入图片描述
至此,第五天的开发笔记已经完成,学习需要坚持,坚持到最后一定会有结果,每天写下笔记来记录自己的学习内容, 以后有需要也可以查看,大家可以一起学习。

想要我一起学习的可以关注我的公众号 知言不尽 找到我,交流学习,获取图片素材和源代码。

发布了6 篇原创文章 · 获赞 95 · 访问量 4369

猜你喜欢

转载自blog.csdn.net/qq_39207481/article/details/105735244