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

173 lines
5.1 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.
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);
}
}