You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
AdLike2048/assets/script/battle/skill/SkillParentAim.ts

137 lines
4.5 KiB

3 weeks ago
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<Vec3>,
targetWorld: Readonly<Vec3>,
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<Vec3>,
targetWorld: Readonly<Vec3>,
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<Vec3>,
targetWorld: Readonly<Vec3>,
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<Vec3>,
targetWorld: Readonly<Vec3>,
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<Vec3>,
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<Vec3>,
targetWorld: Readonly<Vec3>,
): 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<Vec3>,
targetWorld: Readonly<Vec3>,
baseReach: number,
initScale: Readonly<Vec3>,
): 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);
}