三、代码实现逻辑
3.1 普通子弹
此处的普通子弹的作用有两个,提供坐标给Line组件绘制激光,与敌机产生碰撞。
普通子弹的运行较为简单,因为教程很多,不赘述了,与普通子弹不同是,需要添加一个boolean值,用于判断子弹是否已经销毁,因为如果销毁了,就不再用其坐标绘制激光。
3.2 子弹控制器
因为子弹的密度越高,激光会越平滑,所以需要设定一下子弹的数量上限,达到上限后的子弹自动销毁,优化一部分性能。
初始化的子弹节点存入数组bulletNodes,子弹数量达到上限后,销毁最早发射的子弹,并把最近生成的子弹存入数组。
if (self.bulletNodes.length < self.maxPointNum) {
newBulletNode.getComponent(BulletByUpdate)?.init(
BulletDirection.TO_UP, //子弹朝向
self.oriPos, //子弹发射坐标
self.distance, //子弹移动距离
self.speed, //子弹速度
90, //子弹角度
self.bulletSprite, //子弹的精灵
BulletExplodeAnimation.NONE //子弹爆炸 动画
)
self.bulletNodes.push(newBulletNode);
find("Canvas/BulletLayer")!.addChild(newBulletNode);
}
else
{
self.bulletNodes.push(newBulletNode);
self.bulletNodes[0].destroy();
self.bulletNodes.shift();
self.bulletNodes[self.bulletNodes.length - 1].getComponent(BulletByUpdate)?.init(
BulletDirection.TO_UP,
self.oriPos,
self.distance,
self.speed,
90,
self.bulletSprite,
BulletExplodeAnimation.NONE
)
find("Canvas/BulletLayer")!.addChild(self.bulletNodes[self.bulletNodes.length - 1]);
}
3.3 Line组件绘制激光
有了bulletNodes后就可以每帧遍历数组内的子弹坐标绘制激光了,此时需要处理第一个问题,如下图:
当敌机从侧面碰撞子弹后(碰撞了橙色点的子弹),此时绿色点的子弹并未消失,Line的绘制仍会将绿色点和蓝色点连接起来,造成激光穿过敌机的现象,此时应该在update里,每帧获取子弹的碰撞状态,并通过计算仅在bulletNodes数组内保留蓝色的子弹节点。
let i = 0;
let destroyNodes;
for (i = 0; i < this.bulletNodes.length; i++) {
if (this.bulletNodes[i].position != null && this.bulletNodes[i].position != undefined) {
if (this.bulletNodes[i].getComponent(BulletLaserByUpdate)?.hasCrash()) {
// console.log("i:" + i);
destroyNodes = this.bulletNodes.splice(0, i); //获取撞击的拐点,并将其之前的拐点全部删除
let j = 0;
for (j = 0; j < destroyNodes.length; j++) {
destroyNodes[j].destroy(); //将删除的拐点全部销毁
}
}
}
}
然后再遍历bulletNodes,获取坐标,存入数组,用于最后给LINE组件绘制用:
this.pointVec = new Array<Vec3>();
let a = 0;
for(a = 0;a<this.bulletNodes.length;a++)
{
if(this.bulletNodes[a].active != false)
{
this.pointVec.push(this.bulletNodes[a].getWorldPosition());
}
}
之后还需要处理第二个问题,因为每个子弹发射有一定的时间间隔,在下一发子弹未出现前,激光的两端以第一颗和最后一颗子弹为拐点,那在最新一发子弹发出前后,LINE组件分别如下图绘制:
这样会导致激光发射位置持续反复横跳,看着很不舒服,所以此时需要设置一个固定的起始点作为Line组件的发射点。
let planeHead: Vec3 = null!;
//如果未定义子弹的初始发射位置,则把发射初始位置设置在预制体前部中间位置
if (this.shotPos != null && this.shotPos != undefined) {
planeHead = new Vec3(this.fatherNode.worldPosition.x + this.shotPos.x,
this.fatherNode.worldPosition.x + this.shotPos.y,
0)
}
else if (this.shotPos == null) {
if (this.direction == BulletDirection.TO_UP) {
planeHead = new Vec3(this.fatherNode.getWorldPosition().x,
this.fatherNode.getWorldPosition().y
+ this.fatherNode.getComponent(UITransform)?.height! / 2);
}
else if (this.direction == BulletDirection.TO_DOWN) {
planeHead = new Vec3(this.fatherNode.getWorldPosition().x,
this.fatherNode.getWorldPosition().y
- this.fatherNode.getComponent(UITransform)?.height! / 2);
}
}
this.pointVec.push(planeHead);
最后为Line组件设置拐点:
this.laserLine.positions = this.pointVec as never;
就完成了。
四、效果显示优化。
因为Line只是单纯的一条直线,可以通过粒子效果分别在激光头尾部挂载,实现看起来比较酷炫的效果。
效果如下:
配置如下:
PositionType需要设置成RELATIVE效果。