import { _decorator, Collider2D, Component, Contact2DType, IPhysics2DContact, Node, Vec3 } from 'cc'; import { SuDace } from './SuDace'; import { SuDaceEnemy } from './SuDaceEnemy'; const { ccclass, property } = _decorator; @ccclass('SuDaceBullet') export class SuDaceBullet extends Component { @property moveSpeed: number = 100; @property hitRadius = 24; /**攻击力 */ @property damage = 1; /** true 表示陷阱落石:仅向下运动,并回收到 stonePool */ @property isStone = false; private _col: Collider2D | null = null; onEnable() { this._col = this.node.getComponent(Collider2D); this._col?.on(Contact2DType.BEGIN_CONTACT, this.onBeginContact, this); } onDisable() { this._col?.off(Contact2DType.BEGIN_CONTACT, this.onBeginContact, this); this._col = null; } update(deltaTime: number) { let direction: Vec3; if (this.isStone) { // 落石固定竖直下落,避免被角度/复用状态影响成横向发射 direction = new Vec3(0, -1, 0); } else { // 普通子弹按角度飞行 const angle = this.node.angle * Math.PI / 180 + Math.PI / 2; // Node.angle 在 2D 中是“顺时针为正”,而 sin/cos 按“逆时针为正”来解释角度 // 这里对 Y 分量取反,修正“右下变右上/上下颠倒”的问题 direction = new Vec3(Math.cos(angle), -Math.sin(angle), 0).normalize(); } // 地图节点每帧在移动(相机跟随),子弹必须用世界坐标移动,否则会叠加父节点位移导致“角度对但轨迹偏” const wp = this.node.worldPosition; this.node.setWorldPosition( wp.x + direction.x * deltaTime * this.moveSpeed, wp.y + direction.y * deltaTime * this.moveSpeed, wp.z ); this.checkEnemyHitByDistance(); } private onBeginContact(self: Collider2D, other: Collider2D, contact: IPhysics2DContact | null) { if (!this.node.active || !other?.node?.active) return; const otherName = other.node.name.toLowerCase(); if (otherName === "enemy" || otherName.includes("enemy")) { this.hitEnemy(other.node); } } private checkEnemyHitByDistance() { const enemyRoot = SuDace.instance?.map?.enemyNode; if (!enemyRoot || !this.node.active) return; const bp = this.node.worldPosition; for (let i = 0; i < enemyRoot.children.length; i++) { const enemy = enemyRoot.children[i]; if (!enemy.active) continue; if (Vec3.distance(bp, enemy.worldPosition) <= this.hitRadius) { this.hitEnemy(enemy); break; } } } private hitEnemy(enemy: Node) { if (!this.node.active || !enemy?.active) return; enemy.getComponent(SuDaceEnemy)?.hit(this.damage); if (this.isStone) { SuDace.instance?.recycleStone(this.node); } else { SuDace.instance?.recycleBullet(this.node); } } }