83 lines
2.3 KiB
TypeScript
83 lines
2.3 KiB
TypeScript
|
|
// assets/scripts/Preview.ts
|
|||
|
|
import { _decorator, Component, Node, Prefab, instantiate, Vec3 } from "cc";
|
|||
|
|
import { BallPhysics } from "./BallPhysics";
|
|||
|
|
const { ccclass, property } = _decorator;
|
|||
|
|
|
|||
|
|
@ccclass("Preview")
|
|||
|
|
export class Preview extends Component {
|
|||
|
|
@property(Prefab) pointPrefab: Prefab = null!;
|
|||
|
|
@property(Node) goal: Node = null!;
|
|||
|
|
|
|||
|
|
@property({ tooltip: "轨迹点数量" }) maxPoints = 30;
|
|||
|
|
@property({ tooltip: "步长时间(秒)" }) timeStep = 0.05;
|
|||
|
|
|
|||
|
|
@property(BallPhysics) ballPhys: BallPhysics = null!;
|
|||
|
|
|
|||
|
|
private points: Node[] = [];
|
|||
|
|
|
|||
|
|
clear() {
|
|||
|
|
for (const p of this.points) p.destroy();
|
|||
|
|
this.points.length = 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/** 显示预测轨迹 */
|
|||
|
|
showTrajectory(startPos: Vec3, dir: Vec3, speed: number, spin: Vec3) {
|
|||
|
|
this.clear();
|
|||
|
|
if (!this.pointPrefab || !this.ballPhys) return;
|
|||
|
|
|
|||
|
|
const goalZ = this.goal?.worldPosition?.z;
|
|||
|
|
|
|||
|
|
// 初始状态
|
|||
|
|
let pos = startPos.clone();
|
|||
|
|
let v = dir.clone().multiplyScalar(speed);
|
|||
|
|
let w = spin.clone(); // 自旋
|
|||
|
|
|
|||
|
|
// 迭代预测
|
|||
|
|
// let age = 0;
|
|||
|
|
for (let i = 0; i < this.maxPoints; i++) {
|
|||
|
|
// age += this.timeStep;
|
|||
|
|
// 保存点
|
|||
|
|
const p = instantiate(this.pointPrefab);
|
|||
|
|
p.setWorldPosition(pos);
|
|||
|
|
p.setParent(this.node);
|
|||
|
|
this.points.push(p);
|
|||
|
|
|
|||
|
|
// === 模拟 BallPhysics.update(dt) ===
|
|||
|
|
// const dt = this.timeStep;
|
|||
|
|
const dt = this.timeStep * 0.5;
|
|||
|
|
|
|||
|
|
// 空气阻力
|
|||
|
|
const speedNow = v.length();
|
|||
|
|
if (speedNow > 0.01) {
|
|||
|
|
const drag = v.clone().multiplyScalar(-this.ballPhys.dragK * speedNow);
|
|||
|
|
v.add(drag.multiplyScalar(dt));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 马格努斯效应:F = k * (ω × v)
|
|||
|
|
if (w.lengthSqr() > 0.0001 && v.lengthSqr() > 0.0001) {
|
|||
|
|
// const tFactor = 1.0 / (1.0 + age * 2.0); // age 是飞行时间,越久衰减越快
|
|||
|
|
// const magnus = w
|
|||
|
|
// .clone()
|
|||
|
|
// .cross(v)
|
|||
|
|
// .multiplyScalar(this.ballPhys.magnusK * tFactor);
|
|||
|
|
// v.add(magnus.multiplyScalar(dt));
|
|||
|
|
const magnus = w.clone().cross(v).multiplyScalar(this.ballPhys.magnusK);
|
|||
|
|
v.add(magnus.multiplyScalar(dt));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 重力
|
|||
|
|
v.y += -9.8 * dt;
|
|||
|
|
|
|||
|
|
// 移动
|
|||
|
|
pos = pos.add(v.clone().multiplyScalar(dt));
|
|||
|
|
|
|||
|
|
if (
|
|||
|
|
(startPos.z > goalZ && pos.z <= goalZ) ||
|
|||
|
|
(startPos.z < goalZ && pos.z >= goalZ)
|
|||
|
|
) {
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|