first commit
This commit is contained in:
133
assets/scripts/BallPhysics.ts
Normal file
133
assets/scripts/BallPhysics.ts
Normal file
@@ -0,0 +1,133 @@
|
||||
// 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();
|
||||
|
||||
// ⚽ 保存 spin(KickInput 设置进来)
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user