import { _decorator, Animation, Collider, Enum, Node, RigidBody, Vec3 } from 'cc'; import { BaseExp } from '../core/base/BaseExp'; import { PoolManager } from '../core/manager/PoolManager'; import { userIns } from '../data/UserData'; import { Logger } from '../extend/Logger'; import { ResUtil } from '../utils/ResUtil'; import { Game } from './Game'; import { Enemy, EPartType } from './Enemy'; import { audioMgr } from '../core/manager/AudioManager'; import { Constants } from '../data/Constants'; const { ccclass, property } = _decorator; //沙袋堆动画 const sandbge_take = "take" /** * 战场障碍物类型 */ export enum ObstacleType { /** 沙袋堆 */ SANDBAG_PILE = "沙袋堆", /** 油桶 */ OIL_BARREL = "油桶", /** 军车 */ MILITARY_TRUCK = "军车", /** 小沙袋 */ SMALL_SANDBAG = "小沙袋" } /** * 杂物建筑物类 */ @ccclass('Sundries') export class Sundries extends BaseExp { @property({ type: Enum(ObstacleType), tooltip: "沙袋堆:SANDBAG_PILE 油桶:OIL_BARREL 军车: MILITARY_TRUCK 小沙袋:SMALL_SANDBAG", displayName: "障碍物类型"}) public obstacleType: ObstacleType = ObstacleType.SANDBAG_PILE; //添加爆炸队列和标记 private static explosionQueue: { target: Enemy | Sundries, damage: number, source: any }[] = []; //标记是否正在处理爆炸队列 private static isProcessingQueue = false; //杂物信息数据 public data: any = null; //杂物是否被打报废 private isDead: boolean = false; //杂物当前的总血量 private totalHp: number = 0; start() { //给碰撞体绑定数据 this.setupColliders(this.node); //根据障碍物类型设置总血量 let sundrie_id: number = -1; switch (this.obstacleType) { case ObstacleType.SANDBAG_PILE: sundrie_id = 11001; break; case ObstacleType.OIL_BARREL: sundrie_id = 11002; break; case ObstacleType.MILITARY_TRUCK: sundrie_id = 11003; break; case ObstacleType.SMALL_SANDBAG: sundrie_id = 11004; break; } if(sundrie_id !== -1){ const data: any = userIns.sundriesTable.find(e=>e.id == sundrie_id); this.data = data; this.totalHp = data.hp; }else{ Logger.log('未找到对应的杂物信息'); } } /** * 杂物被打掉血 * @param hp 血 * @param pData 玩家数据 */ public subHP(hp: number, pData:any){ if(!this.node ||Game.I.isPause ||this.isDead ||hp == null ||this.totalHp <= 0){ return; } this.totalHp -= hp; //杂物打打死报废 if(this.totalHp <= 0 && !this.isDead){ this.isDead = true; this.scrap(); } } /** * 杂物被打报废 */ public scrap(){ switch (this.obstacleType) { case ObstacleType.SANDBAG_PILE:{//沙袋堆 //沙袋掀开的动画 let anim:Animation = this.node.getComponent(Animation); if(!anim)return; anim.getState(sandbge_take).repeatCount = 1; anim.play(sandbge_take); } break; case ObstacleType.OIL_BARREL:{//油桶 //油漆桶音效 audioMgr.playOneShot(Constants.audios.Oildrum_explosion); //爆炸后生成爆炸特效 粒子路径、自动回收时间、外部设置回调 ResUtil.playParticle( `effects/Prefabs/OilBoom`, 4, (particle) => { particle.parent = this.node.parent; const targetPos: Vec3 = this.node.worldPosition.clone(); particle.worldPosition = targetPos; particle.active = true; this.recycle(); //爆炸后生成爆炸伤害 this.explosionDamage(targetPos); } ); } break; case ObstacleType.MILITARY_TRUCK:{//军车 //加载报废的军车 ResUtil.loadRes(`enemy/sundries/scrap_car`, this.node) .then((military: Node | null) => { if(!military)return; military.active = true; military.parent = this.node.parent; const targetPos: Vec3 = this.node.worldPosition.clone(); military.worldPosition = targetPos; military.eulerAngles = this.node.eulerAngles.clone(); military.scale = this.node.scale.clone(); //回收原有的军车 this.recycle(); //播放爆炸过后的浓烟 ResUtil.playParticle( `effects/Prefabs/HeavySmoke`, 3, (particle) => { particle.parent = Game.I.map.node; particle.worldPosition = targetPos; particle.active = true; //爆炸后生成爆炸伤害 this.explosionDamage(targetPos); } ); }); } break; case ObstacleType.SMALL_SANDBAG:{//小沙袋 //获取刚体组件 const rigidBody = this.node.getComponent(RigidBody); if (!rigidBody) return; //计算抛射方向 const startPos = this.node.worldPosition.clone(); const playerPos = Game.I.player.node.worldPosition.clone(); const direction = startPos.subtract(playerPos).normalize(); //随机抛射参数 水平力1500-2500N const horizontalForce = 1500 + Math.random() * 1000; //垂直力800-1500N const verticalForce = 800 + Math.random() * 700; //旋转扭矩50-150 const torqueForce = 50 + Math.random() * 100; //施加作用力 rigidBody.applyForce(new Vec3( direction.x * horizontalForce, verticalForce, direction.z * horizontalForce )); //施加旋转扭矩 rigidBody.applyTorque(new Vec3(0, torqueForce, 0)); } break; } } /** * 爆炸物产生爆炸伤害 * @param targetPos 爆炸物的位置 */ public explosionDamage(targetPos: Vec3){ //爆炸半径 const eRadius: number = this.data.explosion_radius / 10; //查找不是自身的所有杂物 然后去看看是否有连续爆炸 const allSundries: Sundries[] = Game.I.map.sundries.children .filter(node => node.active && node.parent && node != this.node) .map(node => node.getComponent(Sundries)) .filter(comp => comp && !comp.isDead ) as Sundries[]; const allEnemys = [...Game.I.buildEnemys.allEnemys.filter(e=>!e.isDead),...allSundries]; if(allEnemys.length <= 0)return; //爆炸伤害 const damage: number = this.data.explosion_injury; allEnemys.forEach((e: any) => { if(e.node){ const ePos: Vec3 = e.node.worldPosition; const distance = ePos.clone().subtract(targetPos).length(); if(distance <= eRadius) { if(e instanceof Enemy){//敌人直接扣血 e.subHP(damage, this.data); }else{//爆炸物 如果队列未在处理则开始处理 将爆炸目标加入队列而不是立即触发 Sundries.explosionQueue.push({ target: e, damage: damage, source: this.data }); if(!Sundries.isProcessingQueue) { this.processExplosionQueue(); } } } } }) } /** * 处理爆炸队列(新增方法) */ private processExplosionQueue() { if (Sundries.explosionQueue.length == 0) { Sundries.isProcessingQueue = false; return; } Sundries.isProcessingQueue = true; const explosion = Sundries.explosionQueue.shift()!; //触发当前爆炸 explosion.target.subHP(explosion.damage, explosion.source); //添加0.2秒延迟后处理下一个爆炸 this.scheduleOnce(()=>{ this.processExplosionQueue(); },0.2) } /** * 递归设置所有子节点的Collider参数 * @param node 起始节点 */ private setupColliders(node: Node) { const collider = node.getComponent(Collider); if(collider){ collider["args"] = [EPartType.sundries, this]; } //递归处理所有子节点 node.children.forEach(child => { this.setupColliders(child); }); } /** * 杂物回收 */ public recycle(){ if(this.node.parent){ this.isDead = true; PoolManager.putNode(this.node); } } }