// 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(); } } }