46 lines
1.5 KiB
TypeScript
46 lines
1.5 KiB
TypeScript
// assets/scripts/GoalkeeperAI.ts
|
||
import { _decorator, Component, Node, RigidBody, Vec3, math } from "cc";
|
||
const { ccclass, property } = _decorator;
|
||
|
||
@ccclass("GoalkeeperAI")
|
||
export class GoalkeeperAI extends Component {
|
||
@property(Node) ball!: Node;
|
||
@property({ tooltip: "球门线Z(世界坐标)" }) goalLineZ = 10;
|
||
@property({ tooltip: "守门员水平活动范围X" }) rangeX = 3.5;
|
||
@property({ tooltip: "移动速度(m/s)" }) speed = 6;
|
||
@property({ tooltip: "反应延迟(秒)" }) reactDelay = 0.15;
|
||
|
||
private targetX = 0;
|
||
private tAcc = 0;
|
||
private tmp = new Vec3();
|
||
|
||
update(dt: number) {
|
||
if (!this.ball) return;
|
||
|
||
// 简化预测:用当前速度线性外推到球门线的交点
|
||
const rb = this.ball.getComponent(RigidBody);
|
||
if (!rb) return;
|
||
rb.getLinearVelocity(this.tmp);
|
||
const v = this.tmp.clone();
|
||
const bp = this.ball.worldPosition.clone();
|
||
|
||
if (Math.abs(v.z) < 0.1) return; // 几乎不朝球门方向飞
|
||
|
||
const timeToLine = (this.goalLineZ - bp.z) / v.z; // z 轴朝向球门
|
||
if (timeToLine < 0) return; // 已过线或背离
|
||
|
||
this.tAcc += dt;
|
||
if (this.tAcc >= this.reactDelay) {
|
||
const predictX = bp.x + v.x * timeToLine;
|
||
this.targetX = math.clamp(predictX, -this.rangeX, this.rangeX);
|
||
this.tAcc = 0; // 下次再延迟
|
||
}
|
||
|
||
// 移动守门员
|
||
const p = this.node.worldPosition;
|
||
const dx = this.targetX - p.x;
|
||
const step = math.clamp(dx, -this.speed * dt, this.speed * dt);
|
||
this.node.setWorldPosition(p.x + step, p.y, p.z);
|
||
}
|
||
}
|