Files
ball/assets/scripts/BallPhysics.ts
1970-01-01 08:45:37 +08:00

134 lines
3.7 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// assets/scripts/BallPhysics.ts
import {
_decorator,
Component,
Node,
RigidBody,
Vec3,
math,
SphereCollider,
Contact2DType,
MeshCollider,
ICollisionEvent,
ITriggerEvent,
} from "cc";
const { ccclass, property } = _decorator;
@ccclass("BallPhysics")
export class BallPhysics extends Component {
@property({ tooltip: "马格努斯系数(越大弧线越明显)" }) magnusK = 0.1;
@property({ tooltip: "空气阻力" }) dragK = 0.001;
@property({ tooltip: "风力m/s^2" }) windStrength = 0;
@property({ tooltip: "风向" }) windDir: Vec3 = new Vec3(1, 0, 0);
@property({ tooltip: "自动重置高度阈值(低于视为无效球)" }) resetY = -2;
@property(Node) spawnPoint!: Node;
@property(Node) ball!: Node;
private rb!: RigidBody;
private tmpF = new Vec3();
private v = new Vec3();
private w = new Vec3();
private wind = new Vec3();
private pos = new Vec3();
// ⚽ 保存 spinKickInput 设置进来)
private spin = new Vec3();
onLoad() {
this.rb = this.ball.getComponent(RigidBody)!;
const col = this.ball.getComponent(MeshCollider);
if (col) {
console.log(22223333, col);
col.on("onTriggerEnter", this.onTriggerEnter, this);
col.on("onTriggerStay", this.onTriggerStay, this);
col.on("onCollisionEnter", this.onCollisionEnter, this);
}
this.wind.set(this.windDir).normalize();
}
private onTriggerStay(event: ITriggerEvent) {
console.log(event.type, event);
}
onTriggerEnter(event: ICollisionEvent) {
console.log("⚽ 触发器进入:", event.otherCollider.node.name);
}
onCollisionEnter(event: ICollisionEvent) {
console.log("⚽ 碰撞进入:", event.otherCollider.node.name);
}
init() {
this.rb = this.node.getComponent(RigidBody)!;
this.wind.set(this.windDir).normalize();
this.pos = this.node.worldPosition.clone();
}
setWind(direction: Vec3, strength: number) {
this.wind.set(direction).normalize();
this.windStrength = strength;
}
resetBall() {
if (!this.spawnPoint) return;
this.rb.clearState();
this.node.worldPosition = this.pos; //this.spawnPoint.worldPosition.clone();
this.rb.setLinearVelocity(new Vec3());
this.rb.setAngularVelocity(new Vec3());
}
/** 设置自旋(由 KickInput 调用) */
public setSpin(spin: Vec3) {
this.spin.set(spin);
}
/** 发射足球 */
shootBall(dir: Vec3, speed: number) {
this.rb.clearState();
this.rb.setLinearVelocity(dir.clone().multiplyScalar(speed));
this.rb.setAngularVelocity(this.spin); // 应用横向旋转
}
update(dt: number) {
if (!this.rb) return;
this.rb.getLinearVelocity(this.v);
// 1. 空气阻力(与预测保持一致: -k * v * |v|
const speed = this.v.length();
if (speed > 0.01) {
const drag = this.v.clone().multiplyScalar(-this.dragK * speed);
this.rb.applyForce(drag);
}
// 2. 马格努斯力: F = k * (spin × v)
if (!this.spin.equals(Vec3.ZERO) && speed > 0.01) {
const magnus = new Vec3();
Vec3.cross(magnus, this.spin, this.v);
magnus.multiplyScalar(this.magnusK);
this.rb.applyForce(magnus);
}
// if (!this.spin.equals(Vec3.ZERO) && speed > 0.01) {
// const magnus = new Vec3();
// const vNorm = this.v.clone().normalize();
// Vec3.cross(magnus, this.spin, vNorm);
// magnus.multiplyScalar(this.magnusK * Math.sqrt(speed));
// this.rb.applyForce(magnus);
// }
// 3. 风
if (this.windStrength > 0.0001) {
this.tmpF.set(this.wind).multiplyScalar(this.windStrength);
this.rb.applyForce(this.tmpF);
}
// 4. 掉出场地自动重置
if (this.node.worldPosition.y < this.resetY && this.spawnPoint) {
this.resetBall();
}
}
}