import { Node, Vec3 } from 'cc'; import { computeAimAngleDeg } from './SkillTransformDriver'; /** 悟/棍 skillParent 预制体默认朝向(euler.z) */ export const SKILL_PARENT_REST_ANGLE = 90; /** * 箭发射相对目标连线的偏角(度)。 * 默认朝上=90°;按象限决定 ±: * - 第1、4象限(目标在右侧):目标角 + offset(抛物线向上拱) * - 第2、3象限(目标在左侧):目标角 − offset * 例:150→120,25→55,225→195,320→350 */ export const ARROW_BOW_OFFSET_DEG = 30; /** * 计算箭发射/拉弓朝向(度)。 * 不写节点角度;由表现层插值到该角,再沿此角出弓并做抛物线飞向真实目标。 */ export function computeArrowBowAimDeg( pivotWorld: Readonly, targetWorld: Readonly, offsetDeg = ARROW_BOW_OFFSET_DEG, ): number | null { const raw = computeAimAngleDeg(pivotWorld, targetWorld); if (raw == null) return null; const absOff = Math.abs(offsetDeg); // atan2:右0°、上90°、左±180°、下-90°。dx>=0 为第1/4象限,dx<0 为第2/3象限 const dx = targetWorld.x - pivotWorld.x; return dx >= 0 ? raw + absOff : raw - absOff; } /** * 立刻把 skillParent 拧到偏转后的瞄准角(无插值时用)。 * @returns 实际拉弓角(度);无有效朝向时返回 null */ export function applyArrowBowAim( skillParent: Node | null | undefined, pivotWorld: Readonly, targetWorld: Readonly, offsetDeg = ARROW_BOW_OFFSET_DEG, ): number | null { const aimDeg = computeArrowBowAimDeg(pivotWorld, targetWorld, offsetDeg); if (aimDeg != null && skillParent?.isValid) { skillParent.angle = aimDeg; } return aimDeg; } /** * 将 skillParent 旋转到指向目标,并限制在 [minDeg, maxDeg]。 * @returns 实际使用的瞄准角(度) */ export function aimSkillParentClamped( skillParent: Node | null | undefined, pivotWorld: Readonly, targetWorld: Readonly, minDeg: number, maxDeg: number, ): number { const angle = computeClampedAimAngleDeg(pivotWorld, targetWorld, minDeg, maxDeg); if (skillParent?.isValid && angle != null) { skillParent.angle = angle; } return angle ?? (minDeg + maxDeg) * 0.5; } /** pivot→target 瞄准角,限制在 [minDeg, maxDeg] */ export function computeClampedAimAngleDeg( pivotWorld: Readonly, targetWorld: Readonly, minDeg: number, maxDeg: number, ): number | null { const raw = computeAimAngleDeg(pivotWorld, targetWorld); if (raw == null) return null; return Math.max(minDeg, Math.min(maxDeg, raw)); } /** 沿瞄准角与距离计算落点世界坐标 */ export function worldPointAtAimAngle( pivotWorld: Readonly, angleDeg: number, distancePx: number, ): Vec3 { const rad = angleDeg * Math.PI / 180; return new Vec3( pivotWorld.x + Math.cos(rad) * distancePx, pivotWorld.y + Math.sin(rad) * distancePx, pivotWorld.z, ); } /** * 将 skillParent 旋转到指向目标(与悟一致:以 pivot 为原点,atan2 得 Cocos angle)。 * 右=0°,上=90°,左=±180°。 */ export function aimSkillParentAtTarget( skillParent: Node | null | undefined, pivotWorld: Readonly, targetWorld: Readonly, ): void { if (!skillParent?.isValid) return; const dx = targetWorld.x - pivotWorld.x; const dy = targetWorld.y - pivotWorld.y; if (Math.abs(dx) < 1e-4 && Math.abs(dy) < 1e-4) return; skillParent.angle = Math.atan2(dy, dx) * 180 / Math.PI; } export function resetSkillParentAngle( skillParent: Node | null | undefined, restAngle = SKILL_PARENT_REST_ANGLE, ): void { if (skillParent?.isValid) { skillParent.angle = restAngle; } } /** * 按 pivot→目标距离拉伸 skillParent.localScale.x(金箍棒/棍等沿父节点 X 轴延伸)。 * @param baseReach 预制体 scale=1 时 skill 子节点相对 pivot 的基准长度 */ export function scaleSkillParentReach( skillParent: Node | null | undefined, pivotWorld: Readonly, targetWorld: Readonly, baseReach: number, initScale: Readonly, ): void { if (!skillParent?.isValid || baseReach <= 1e-4) return; const dx = targetWorld.x - pivotWorld.x; const dy = targetWorld.y - pivotWorld.y; const dist = Math.hypot(dx, dy); const factor = Math.max(0.1, dist / baseReach); skillParent.setScale(initScale.x * factor, initScale.y, initScale.z); }