Files
ball/assets/scripts/KickInput.ts

173 lines
5.1 KiB
TypeScript
Raw Permalink Normal View History

1970-01-01 08:45:37 +08:00
import {
_decorator,
Component,
Node,
EventTouch,
Input,
input,
Vec2,
Vec3,
RigidBody,
math,
Graphics,
MeshRenderer,
geometry,
} from "cc";
import { TrajectoryPreview } from "./TrajectoryPreview";
import { Preview } from "./Preview";
import { BallPhysics } from "./BallPhysics";
const { ccclass, property } = _decorator;
@ccclass("KickInput")
export class KickInput extends Component {
@property(Node) ball!: Node;
// @property(Node) goal!: Node;
@property(Node) cameraRig!: Node;
// 力度与角度控制
@property({ tooltip: "最大初速度m/s" }) maxSpeed = 20;
@property({ tooltip: "最小仰角(度)" }) minPitchDeg = 8;
@property({ tooltip: "最大仰角(度)" }) maxPitchDeg = 18;
// 自旋与弧线
@property({ tooltip: "自旋强度系数" }) spinFactor = 20;
@property(Preview) preview: Preview = null!;
private startPos = new Vec2();
private dragging = false;
private lastPos = new Vec2();
private trail: Vec2[] = [];
private ballPhys: BallPhysics = null;
protected onLoad(): void {
this.ballPhys = this.ball.getComponent(BallPhysics)!;
}
onEnable() {
input.on(Input.EventType.TOUCH_START, this.onStart, this);
input.on(Input.EventType.TOUCH_MOVE, this.onMove, this);
input.on(Input.EventType.TOUCH_END, this.onEnd, this);
input.on(Input.EventType.TOUCH_CANCEL, this.onEnd, this);
}
onDisable() {
input.off(Input.EventType.TOUCH_START, this.onStart, this);
input.off(Input.EventType.TOUCH_MOVE, this.onMove, this);
input.off(Input.EventType.TOUCH_END, this.onEnd, this);
input.off(Input.EventType.TOUCH_CANCEL, this.onEnd, this);
}
private onStart(e: EventTouch) {
this.dragging = true;
e.getLocation(this.startPos);
e.getLocation(this.lastPos);
this.trail.length = 0;
this.trail.push(this.startPos.clone());
}
private onMove(e: EventTouch) {
if (!this.dragging) return;
const p = e.getLocation();
this.trail.push(p.clone());
this.lastPos.set(p);
// 1) 拖拽向量
const drag = this.lastPos.clone().subtract(this.startPos);
const dragLen = drag.length();
// 2) 速度(映射拖拽长度)
const speed = math.clamp(dragLen / 8, 5, this.maxSpeed);
// const speed = 12;
// 3) 仰角 pitch映射拖拽的纵向
const t = math.clamp(drag.y / 300, 0, 1);
const pitchDeg = math.lerp(this.minPitchDeg, this.maxPitchDeg, t);
// 4) 偏航角 yaw映射拖拽的横向
const yawDeg = math.clamp(-drag.x / 8, -45, 45);
// 5) 得到方向向量
const dir = this.dirFromAngles(yawDeg, pitchDeg);
// 6) 自旋(用轨迹估计)
const spin = this.estimateSpin(this.trail, drag).multiplyScalar(
this.spinFactor
);
this.preview.showTrajectory(this.ball.worldPosition, dir, speed, spin);
}
private onEnd() {
if (!this.dragging) return;
this.dragging = false;
this.preview.clear();
// 拖拽向量
const drag = this.lastPos.clone().subtract(this.startPos);
const dragLen = drag.length();
if (!this.ball || !this.ballPhys) return;
// 1) 计算初速度
const speed = math.clamp(dragLen / 8, 5, this.maxSpeed);
// const speed = 12;
// 2) 仰角 pitch
const t = math.clamp(drag.y / 300, 0, 1);
const pitchDeg = math.lerp(this.minPitchDeg, this.maxPitchDeg, t);
// 3) 偏航 yaw
const yawDeg = math.clamp(-drag.x / 8, -45, 45);
// 4) 方向向量
const dir = this.dirFromAngles(yawDeg, pitchDeg);
// 5) 自旋
const spin = this.estimateSpin(this.trail, drag).multiplyScalar(
this.spinFactor
);
this.ballPhys.setSpin(spin);
console.log(`t: ${t}, pitchDeg:${pitchDeg}`);
// 6) 发射
this.ballPhys.shootBall(dir, speed);
}
private dirFromAngles(yawDeg: number, pitchDeg: number) {
const yaw = math.toRadian(yawDeg);
const pitch = math.toRadian(pitchDeg);
// 水平前方Z轴右手坐标根据你场景轴向可能需调整
const x = Math.sin(yaw) * Math.cos(pitch);
const y = Math.sin(pitch);
const z = Math.cos(yaw) * Math.cos(pitch);
return new Vec3(x, y, z).normalize();
}
// 简单估计自旋方向:用拖拽路径的“弯曲方向”
private estimateSpin(path: Vec2[], drag: Vec2): Vec3 {
if (path.length < 4) return new Vec3(0, 0, 0);
// 取开始、中点、终点计算平面曲率符号映射为Y轴为主或Z轴自旋
const a = path[0],
b = path[Math.floor(path.length / 2)],
c = path[path.length - 1];
// 2D 叉积 z 分量(曲率方向)
const ab = new Vec2(b.x - a.x, b.y - a.y);
const bc = new Vec2(c.x - b.x, c.y - b.y);
const cross = ab.x * bc.y - ab.y * bc.x; // >0 逆时针弯曲
// 横向弯 -> 绕Y轴的自旋产生左右弧线
const spinY = math.clamp(drag.x / 200, -3.5, 3.5);
return new Vec3(0, spinY, 0); // 只绕Y轴产生左右弧度
// const spinY = math.clamp(cross / 20000, -1.5, 1.5) * 2.0; // 横向弧度提前
// // 竖向弯 -> 绕X轴/或Z轴的自旋下坠/挑球这里简单给点Z
// const dy = c.y - a.y;
// const spinZ = math.clamp(dy / 1500, -1.0, 1.0);
// return new Vec3(0, spinY, spinZ);
}
}