Files
pixelheros/assets/script/game/skill/SMoveComp.ts
panw 3d97da8271 fix(技能): 调整贝塞尔曲线起始高度并修复范围技能目标位置
当技能类型为贝塞尔曲线时,直接使用最近敌人的位置作为目标位置,避免范围技能目标位置计算错误。同时将贝塞尔曲线的起始高度从18调整为25,以优化技能表现。
2026-03-30 16:48:30 +08:00

262 lines
7.6 KiB
TypeScript

import { Vec3, v3, Node } from "cc";
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { RType, EType, SkillSet } from "../common/config/SkillSet";
import { BoxSet } from "../common/config/GameSet";
import { SkillView } from "./SkillView";
import { smc } from "../common/SingletonModuleComp";
/**
* ==================== 技能移动数据组件 ====================
*
* 用途:
* - 存储技能实体的移动相关数据
* - 管理移动状态和参数
* - 支持多种移动类型(线性、贝塞尔、固定位置等)
*/
@ecs.register('SMoveDataComp')
export class SMoveDataComp extends ecs.Comp {
/** 起始位置 */
startPos: Vec3 = v3(0, 0, 0);
/** 目标位置 */
targetPos: Vec3 = v3(0, 0, 0);
/** 当前位置 */
currentPos: Vec3 = v3(0, 0, 0);
/** 移动速度 */
speed: number = 500;
/** 移动进度 (0-1) */
progress: number = 0;
/** 移动方向缩放 */
scale: number = 1;
/** 技能UUID */
s_uuid: number = 0;
/** 运行类型 */
runType: RType = RType.linear;
/** 结束类型 */
endType: EType = EType.collision;
/** 是否强制水平移动 */
isHorizontal: boolean = true;
/** 攻击偏移X - 从视图层传递 */
atk_x: number = 0;
/** 攻击偏移Y - 从视图层传递 */
atk_y: number = 0;
/** 是否正在移动 */
isMoving: boolean = false;
/** 是否已完成移动 */
isCompleted: boolean = false;
/** 移动开始时间 */
startTime: number = 0;
/** 移动总时间 */
totalTime: number = 0;
/** 贝塞尔曲线控制点 */
controlPoint: Vec3 = v3(0, 0, 0);
bezierStartHeight: number = 25;
bezierMidHeight: number = 140;
bezierArc: number = 1;
/** 是否自动销毁(到达目标后) */
autoDestroy: boolean = true;
reset() {
this.startPos.set(0, 0, 0);
this.targetPos.set(0, 0, 0);
this.currentPos.set(0, 0, 0);
this.controlPoint.set(0, 0, 0);
this.bezierStartHeight = 18;
this.bezierMidHeight = 140;
this.bezierArc = 1;
this.speed = 500;
this.progress = 0;
this.scale = 1;
this.s_uuid = 0;
this.runType = RType.linear;
this.endType = EType.collision;
this.isHorizontal = true;
this.atk_x = 0;
this.atk_y = 0;
this.isMoving = false;
this.isCompleted = false;
this.startTime = 0;
this.totalTime = 0;
this.autoDestroy = true;
}
/**
* 开始移动
*/
startMove() {
this.isMoving = true;
this.isCompleted = false;
this.progress = 0;
this.startTime = Date.now();
this.currentPos.set(this.startPos);
// 根据移动类型计算总时间和控制点
this.calculateMoveParameters();
}
/**
* 计算移动参数
*/
private calculateMoveParameters() {
const distance = Vec3.distance(this.startPos, this.targetPos);
const safeSpeed = Math.max(1, this.speed);
this.totalTime = Math.max(0.05, distance / safeSpeed);
// 为贝塞尔移动生成控制点
if (this.runType === RType.bezier) {
this.generateBezierControlPoint();
}
}
/**
* 生成贝塞尔曲线控制点
*/
private generateBezierControlPoint() {
const midPoint = new Vec3();
Vec3.lerp(midPoint, this.startPos, this.targetPos, 0.5);
// 计算垂直方向
const direction = new Vec3();
Vec3.subtract(direction, this.targetPos, this.startPos);
if (direction.length() < 0.0001) {
this.controlPoint.set(midPoint.x, midPoint.y + this.bezierMidHeight, midPoint.z);
return;
}
const perpendicular = v3(-direction.y, direction.x, 0);
perpendicular.normalize();
const distance = Vec3.distance(this.startPos, this.targetPos);
const baseOffset = Math.max(120, Math.min(220, distance * 0.35));
const offset = baseOffset * Math.max(0.2, this.bezierArc);
if (perpendicular.y < 0) {
perpendicular.x *= -1;
perpendicular.y *= -1;
}
Vec3.scaleAndAdd(this.controlPoint, midPoint, perpendicular, offset);
const minPeakY = midPoint.y + Math.max(0, this.bezierMidHeight);
if (this.controlPoint.y < minPeakY) {
this.controlPoint.y = minPeakY;
}
}
/**
* 更新移动进度
*/
updateProgress(deltaTime: number): boolean {
if (!this.isMoving || this.isCompleted) {
return false;
}
this.progress += deltaTime / this.totalTime;
if (this.progress >= 1) {
this.progress = 1;
this.isCompleted = true;
this.isMoving = false;
}
// 根据移动类型计算当前位置
this.calculateCurrentPosition();
return true;
}
/**
* 根据移动类型计算当前位置
*/
private calculateCurrentPosition() {
switch (this.runType) {
case RType.linear:
// 直线运动
Vec3.lerp(this.currentPos, this.startPos, this.targetPos, this.progress);
break;
case RType.bezier:
this.calculateBezierPosition(this.progress);
break;
case RType.fixed:
case RType.fixedEnd:
// 固定位置类型不需要移动
this.currentPos.set(this.startPos);
break;
default:
Vec3.lerp(this.currentPos, this.startPos, this.targetPos, this.progress);
break;
}
}
/**
* 计算贝塞尔曲线位置
*/
private calculateBezierPosition(t: number) {
// 二次贝塞尔曲线公式: B(t) = (1-t)²P0 + 2(1-t)tP1 + t²P2
const oneMinusT = 1 - t;
const oneMinusTSquared = oneMinusT * oneMinusT;
const tSquared = t * t;
const twoOneMinusTt = 2 * oneMinusT * t;
this.currentPos.x = oneMinusTSquared * this.startPos.x +
twoOneMinusTt * this.controlPoint.x +
tSquared * this.targetPos.x;
this.currentPos.y = oneMinusTSquared * this.startPos.y +
twoOneMinusTt * this.controlPoint.y +
tSquared * this.targetPos.y;
this.currentPos.z = oneMinusTSquared * this.startPos.z +
twoOneMinusTt * this.controlPoint.z +
tSquared * this.targetPos.z;
}
/**
* 停止移动
*/
stopMove() {
this.isMoving = false;
this.isCompleted = true;
}
/**
* 获取移动统计信息(用于调试)
*/
getMoveStats(): {
isMoving: boolean;
isCompleted: boolean;
progress: number;
runType: RType;
endType: EType;
totalTime: number;
elapsedTime: number;
} {
const currentTime = Date.now();
return {
isMoving: this.isMoving,
isCompleted: this.isCompleted,
progress: this.progress,
runType: this.runType,
endType: this.endType,
totalTime: this.totalTime,
elapsedTime: (currentTime - this.startTime) / 1000
};
}
}