import { _decorator, CCBoolean, CCFloat, Component, instantiate, Node, NodePool, Prefab, UITransform, v3 } from 'cc'; import { GBundle } from '../../game/ConfigRes'; import { atgSkill } from './atgSkill'; import { gaoyaguo } from './gaoyaguo'; import { GEvent } from 'db://assets/mx/module/event/GEvent'; const { ccclass, property } = _decorator; @ccclass('zidanScript') export class zidanScript extends Component { //子弹内存池 @property(Prefab) zidanPrefab: Prefab = null; zidanPool: NodePool = null; bulletSpeed: number = 700; _dt: number = 0; deltaTime: number = 0.016; /** jiedianIndex==3 时:子弹世界坐标 Y 需接近主角才做命中判定,避免门廊区被超大 UITransform 误杀 */ @property(CCFloat) playerHitWorldYOffset: number = 180; @property(CCBoolean) debugBulletMove: boolean = false; /** 子弹世界坐标越界回收的边界(避免飞出屏幕后一直存在)。按设计分辨率 750x1600 做默认值,可在编辑器调。 */ @property(CCFloat) recycleWorldHalfWidth: number = 750; @property(CCFloat) recycleWorldHalfHeight: number = 1600; @property(CCFloat) recycleWorldPadding: number = 300; private _debugAcc = 0; protected onDestroy(): void { if (this.zidanPool) this.zidanPool.clear() } protected start(): void { this.zidanPool = new NodePool() //添加100个子弹 for (let i = 0; i < 100; i++) { let zidan = instantiate(this.zidanPrefab) this.zidanPool.put(zidan) } } //获取子弹 getZidan(): Node { if (this.zidanPool.size() > 0) { }else{ let zidan = instantiate(this.zidanPrefab) this.zidanPool.put(zidan) } return this.zidanPool.get() } //放入子弹 putZidan(zidan: Node) { zidan.removeFromParent(); this.zidanPool.put(zidan) } protected update(dt: number): void { if(gaoyaguo.Instance.isGameOver){ return } const children = this.node.children; const hitList: Node[] = []; /** 飞过主角仍未碰到主角盒,只回收不计分(避免与 qiang 超大 AABB 误杀整屏子弹) */ const missRecycleList: Node[] = []; const g = gaoyaguo.Instance; const guoTrans = g?.gaoyaguo_guo?.getComponent(UITransform) ?? null; const guoBox = guoTrans ? guoTrans.getBoundingBoxToWorld() : null; //const zhujueTrans = g?.zhujueNode?.getComponent(UITransform) ?? null; // const zhujueBox = zhujueTrans ? zhujueTrans.getBoundingBoxToWorld() : null; this._debugAcc += dt; const doDbg = this.debugBulletMove && this._debugAcc >= 1; if (doDbg) this._debugAcc = 0; let sampleChild: Node | null = null; children.forEach((child)=>{ if (doDbg && !sampleChild) sampleChild = child; if(!child.active){ return } const skill = child.getComponent(atgSkill); if (skill?.ignoreContactFrames > 0) { skill.ignoreContactFrames--; if (skill.ignoreContactFrames <= 0) { skill.ignoreDoorUuid = null; } } const dir = skill?.moveDir ?? v3(0, 1, 0); const p = child.position; child.setPosition( p.x + dir.x * this.bulletSpeed * dt, p.y + dir.y * this.bulletSpeed * dt, p.z ); // 世界坐标越界回收(含 padding) const wp = child.worldPosition; if ( Math.abs(wp.x) > this.recycleWorldHalfWidth + this.recycleWorldPadding || Math.abs(wp.y) > this.recycleWorldHalfHeight + this.recycleWorldPadding ) { missRecycleList.push(child); return; } // jiedianIndex == 2:子弹命中高压锅才触发回收/扣血(否则会导致“子弹一生成就被回收”) if (g?.jiedianIndex === 2 && guoBox) { const bulletTrans = child.getComponent(UITransform); if (bulletTrans) { const bulletBox = bulletTrans.getBoundingBoxToWorld(); if (bulletBox.intersects(guoBox)) { hitList.push(child); } } }else if(g?.jiedianIndex == 3){ // 只用主角盒判定「命中计分」;左右墙 UITransform 常过大,若参与 OR 会导致整路子弹同一帧进 hitList(length 上百) } }) if (doDbg) { const s = sampleChild?.getComponent(atgSkill); console.log('[zidanScript] tick', { jiedianIndex: g?.jiedianIndex, children: children.length, sample: sampleChild?.name ?? null, sampleLocal: sampleChild ? { x: sampleChild.position.x, y: sampleChild.position.y } : null, sampleWorld: sampleChild ? { x: sampleChild.worldPosition.x, y: sampleChild.worldPosition.y } : null, sampleDir: s ? { x: s.moveDir.x, y: s.moveDir.y } : null, bulletSpeed: this.bulletSpeed, }); } // 统一处理命中,避免遍历 children 时修改层级 if (hitList.length > 0) { for (const child of hitList) { this.putZidan(child); } if (g?.jiedianIndex == 2) { GEvent.Ins.emit(GEvent.hitGaoyaguo, hitList.length, hitList[0]); } } if (missRecycleList.length > 0) { console.log('missRecycleList', missRecycleList.length) for (const child of missRecycleList) { this.putZidan(child); } } } }