You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
90 lines
3.1 KiB
90 lines
3.1 KiB
|
1 week ago
|
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);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|