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