|
|
@@ -7,11 +7,15 @@ const { ccclass, property } = _decorator;
|
|
|
*/
|
|
|
|
|
|
export interface BulletTrajectoryConfig {
|
|
|
- type: 'straight' | 'parabolic' | 'homing'; // 弹道类型
|
|
|
- speed: number; // 初始速度
|
|
|
- gravity: number; // 重力影响
|
|
|
- homingStrength: number; // 追踪强度 (0-1)
|
|
|
- homingDelay: number; // 追踪延迟(秒)
|
|
|
+ type: 'straight' | 'parabolic' | 'homing' | 'arc'; // 弹道类型
|
|
|
+ speed: number; // 初始速度
|
|
|
+ gravity: number; // 重力影响 (arc/straight 可为0)
|
|
|
+ homingStrength: number; // 追踪强度 (0-1)
|
|
|
+ homingDelay: number; // 追踪延迟(秒)
|
|
|
+ /**
|
|
|
+ * 旋转速率 (0~1)。仅在 arc 弹道中使用,值越大转向越快。
|
|
|
+ */
|
|
|
+ rotateSpeed?: number;
|
|
|
}
|
|
|
|
|
|
export interface TrajectoryState {
|
|
|
@@ -31,6 +35,10 @@ export class BulletTrajectory extends Component {
|
|
|
private targetNode: Node = null;
|
|
|
private homingTimer: number = 0;
|
|
|
|
|
|
+ // === Arc Trajectory ===
|
|
|
+ private arcDir: Vec3 = null; // 当前方向
|
|
|
+ private arcTargetDir: Vec3 = null; // 目标方向(最终方向)
|
|
|
+
|
|
|
/**
|
|
|
* 初始化弹道
|
|
|
*/
|
|
|
@@ -38,8 +46,8 @@ export class BulletTrajectory extends Component {
|
|
|
this.config = { ...config };
|
|
|
this.rigidBody = this.getComponent(RigidBody2D);
|
|
|
|
|
|
- if (!this.rigidBody) {
|
|
|
- console.error('BulletTrajectory: 需要RigidBody2D组件');
|
|
|
+ if (!this.rigidBody && config.type !== 'arc') {
|
|
|
+ console.error('BulletTrajectory: 需要 RigidBody2D 组件 (除 arc 弹道外)');
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
@@ -90,6 +98,30 @@ export class BulletTrajectory extends Component {
|
|
|
this.state.initialVelocity.y
|
|
|
);
|
|
|
break;
|
|
|
+
|
|
|
+ case 'arc':
|
|
|
+ // 计算带 45° 随机偏移的初始方向
|
|
|
+ const baseDir = this.state.initialVelocity.clone().normalize();
|
|
|
+ const sign = Math.random() < 0.5 ? 1 : -1;
|
|
|
+ const rad = 45 * Math.PI / 180 * sign;
|
|
|
+ const cos = Math.cos(rad);
|
|
|
+ const sin = Math.sin(rad);
|
|
|
+ const offsetDir = new Vec3(
|
|
|
+ baseDir.x * cos - baseDir.y * sin,
|
|
|
+ baseDir.x * sin + baseDir.y * cos,
|
|
|
+ 0
|
|
|
+ ).normalize();
|
|
|
+
|
|
|
+ this.arcDir = offsetDir;
|
|
|
+ this.arcTargetDir = baseDir;
|
|
|
+
|
|
|
+ // 如果有物理组件,则设置线速度;否则后续 updateArcTrajectory 将直接操作位移
|
|
|
+ if (this.rigidBody) {
|
|
|
+ this.rigidBody.linearVelocity = new Vec2(offsetDir.x * this.config.speed, offsetDir.y * this.config.speed);
|
|
|
+ }
|
|
|
+ // 保存当前速度
|
|
|
+ this.state.currentVelocity.set(offsetDir.x * this.config.speed, offsetDir.y * this.config.speed, 0);
|
|
|
+ break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -176,7 +208,7 @@ export class BulletTrajectory extends Component {
|
|
|
}
|
|
|
|
|
|
update(dt: number) {
|
|
|
- if (!this.config || !this.state || !this.rigidBody) return;
|
|
|
+ if (!this.config || !this.state) return;
|
|
|
|
|
|
this.state.elapsedTime += dt;
|
|
|
|
|
|
@@ -190,6 +222,9 @@ export class BulletTrajectory extends Component {
|
|
|
case 'homing':
|
|
|
this.updateHomingTrajectory(dt);
|
|
|
break;
|
|
|
+ case 'arc':
|
|
|
+ this.updateArcTrajectory(dt);
|
|
|
+ break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -274,11 +309,58 @@ export class BulletTrajectory extends Component {
|
|
|
this.state.currentVelocity.set(newVelocity);
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 更新弧线弹道
|
|
|
+ */
|
|
|
+ private updateArcTrajectory(dt: number) {
|
|
|
+ if (!this.arcDir || !this.arcTargetDir) return;
|
|
|
+
|
|
|
+ // === 动态追踪目标 ===
|
|
|
+ // 若当前没有有效目标,则尝试重新寻找
|
|
|
+ if (!this.targetNode || !this.targetNode.isValid) {
|
|
|
+ this.findTarget();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 每帧计算目标方向,使得弹道持续朝向敌人
|
|
|
+ if (this.targetNode && this.targetNode.isValid) {
|
|
|
+ const pos = this.node.worldPosition;
|
|
|
+ const dirToTarget = this.targetNode.worldPosition.clone().subtract(pos).normalize();
|
|
|
+ this.arcTargetDir.set(dirToTarget);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 根据与目标距离动态调整转向速率:越近转得越快,避免打圈
|
|
|
+ let rotateFactor = (this.config.rotateSpeed ?? 0.5) * dt;
|
|
|
+ if (this.targetNode && this.targetNode.isValid) {
|
|
|
+ const distance = Vec3.distance(this.node.worldPosition, this.targetNode.worldPosition);
|
|
|
+ rotateFactor *= (2000 / Math.max(distance, 50));
|
|
|
+ }
|
|
|
+
|
|
|
+ const newDir = new Vec3();
|
|
|
+ Vec3.slerp(newDir, this.arcDir, this.arcTargetDir, Math.min(1, rotateFactor));
|
|
|
+ this.arcDir.set(newDir);
|
|
|
+
|
|
|
+ // 更新位移 / 速度
|
|
|
+ if (this.rigidBody) {
|
|
|
+ this.rigidBody.linearVelocity = new Vec2(this.arcDir.x * this.config.speed, this.arcDir.y * this.config.speed);
|
|
|
+ } else {
|
|
|
+ const displacement = this.arcDir.clone().multiplyScalar(this.config.speed * dt);
|
|
|
+ this.node.worldPosition = this.node.worldPosition.add(displacement);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新状态中的当前速度
|
|
|
+ this.state.currentVelocity.set(this.arcDir.x * this.config.speed, this.arcDir.y * this.config.speed, 0);
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 获取当前速度
|
|
|
*/
|
|
|
public getCurrentVelocity(): Vec3 {
|
|
|
- const vel = this.rigidBody.linearVelocity;
|
|
|
+ if (this.config?.type === 'arc') {
|
|
|
+ return this.arcDir ? this.arcDir.clone().multiplyScalar(this.config.speed) : new Vec3();
|
|
|
+ }
|
|
|
+
|
|
|
+ const vel = this.rigidBody?.linearVelocity;
|
|
|
+ if (!vel) return new Vec3();
|
|
|
return new Vec3(vel.x, vel.y, 0);
|
|
|
}
|
|
|
|
|
|
@@ -346,6 +428,7 @@ export class BulletTrajectory extends Component {
|
|
|
if (config.gravity < 0) return false;
|
|
|
if (config.homingStrength < 0 || config.homingStrength > 1) return false;
|
|
|
if (config.homingDelay < 0) return false;
|
|
|
+ if (config.rotateSpeed !== undefined && (config.rotateSpeed < 0 || config.rotateSpeed > 1)) return false;
|
|
|
|
|
|
return true;
|
|
|
}
|