first commit
This commit is contained in:
172
assets/scripts/KickInput.ts
Normal file
172
assets/scripts/KickInput.ts
Normal file
@@ -0,0 +1,172 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user