refactor(skill): 重构技能移动系统,整合移动逻辑到SMoveComp

将技能移动逻辑从SkillView迁移到SMoveComp,实现统一的移动管理
添加多种移动类型支持(线性、贝塞尔、固定位置)
优化移动参数配置,从SkillView获取攻击偏移量
This commit is contained in:
2025-11-01 12:16:21 +08:00
parent ab03e32278
commit d014e63d27
5 changed files with 465 additions and 118 deletions

View File

@@ -1,68 +1,471 @@
import { Vec3, v3 } from "cc";
import { Vec3, v3, Node } from "cc";
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { BezierMove } from "../BezierMove/BezierMove";
import { RType, SkillSet } from "../common/config/SkillSet";
import { RType, EType, SkillSet } from "../common/config/SkillSet";
import { BoxSet } from "../common/config/BoxSet";
import { ECSEntity } from "db://oops-framework/libs/ecs/ECSEntity";
import { SkillView } from "./SkillView";
/**
* 技能移动数据组件
* 存储技能实体的移动相关数据
* ==================== 技能移动数据组件 ====================
*
* 用途:
* - 存储技能实体的移动相关数据
* - 管理移动状态和参数
* - 支持多种移动类型(线性、贝塞尔、固定位置等)
*/
@ecs.register('SMoveDataComp')
export class SMoveDataComp extends ecs.Comp {
/** 起始位置 */
startPos: Vec3 = null;
startPos: Vec3 = v3(0, 0, 0);
/** 目标位置 */
targetPos: Vec3 = null;
targetPos: Vec3 = v3(0, 0, 0);
/** 当前位置 */
currentPos: Vec3 = v3(0, 0, 0);
/** 移动速度 */
speed: number = 500;
/** 移动持续时间 */
duration: number = 0;
/** 移动方向 */
/** 移动进度 (0-1) */
progress: number = 0;
/** 移动方向缩放 */
scale: number = 1;
/** 技能UUID */
s_uuid: number = 0;
/** 运行类型 */
runType: RType = RType.linear;
/** 结束类型 */
endType: EType = EType.collision;
/** 攻击偏移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);
/** 是否自动销毁(到达目标后) */
autoDestroy: boolean = true;
s_uuid:number=0;
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.speed = 500;
this.duration = 0;
this.scale=1;
this.progress = 0;
this.scale = 1;
this.s_uuid = 0;
this.runType = RType.linear;
this.endType = EType.collision;
this.atk_x = 0;
this.atk_y = 0;
this.isMoving = false;
this.isCompleted = false;
this.startTime = 0;
this.totalTime = 0;
this.autoDestroy = true;
}
rePos(originalStart:Vec3){
if(!originalStart){
return
/**
* 重新计算位置(用于线性移动的延长)
*/
rePos(originalStart: Vec3) {
if (!originalStart) {
return;
}
console.log("[SMoveDataComp]rePos",originalStart,this.targetPos)
// 计算延长后的目标点坐标
// 对于直线运动只在x轴方向延长y轴保持不变
const originalTarget = v3(this.targetPos.x, this.targetPos.y + BoxSet.ATK_Y);
const direction = new Vec3();
Vec3.subtract(direction, originalTarget, originalStart);
const distance = direction.length();
direction.normalize();
const extendedTarget = new Vec3();
Vec3.scaleAndAdd(extendedTarget, originalTarget, direction, 720);
// 计算x轴方向
const xDirection = originalTarget.x > originalStart.x ? 1 : -1;
// 延长720像素但只在x轴方向
const extendedTarget = v3(
originalTarget.x + (xDirection * 720),
originalStart.y, // y轴保持起始位置不变
originalStart.z // z轴保持起始位置不变
);
this.startPos.set(originalStart);
this.targetPos.set(extendedTarget);
}
/**
* 开始移动
*/
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);
this.totalTime = distance / this.speed;
// 为贝塞尔移动生成控制点
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);
const perpendicular = v3(-direction.y, direction.x, 0);
perpendicular.normalize();
// 根据scale决定控制点方向
const offset = 100 * this.scale;
Vec3.scaleAndAdd(this.controlPoint, midPoint, perpendicular, offset);
}
/**
* 更新移动进度
*/
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:
// 直线运动只在x轴移动y轴保持不变
this.currentPos.x = this.startPos.x + (this.targetPos.x - this.startPos.x) * this.progress;
this.currentPos.y = this.startPos.y; // y轴保持不变
this.currentPos.z = this.startPos.z; // z轴保持不变
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
};
}
}
/** 业务层业务逻辑处理对象 */
/**
* ==================== 技能移动系统 ====================
*
* 职责:
* 1. 处理技能实体的移动逻辑
* 2. 更新技能位置
* 3. 管理移动生命周期
* 4. 支持多种移动类型
*
* 设计理念:
* - 参考DamageQueueComp的实现方式
* - 独立的移动处理系统
* - 支持从视图层获取的参数
*/
@ecs.register('SMoveSystem')
export class SMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate {
filter(): ecs.IMatcher {
return ecs.allOf(SMoveDataComp,SkillView);
return ecs.allOf(SMoveDataComp, SkillView);
}
entityEnter(e: ecs.Entity): void {
// 注:自定义业务逻辑
let s_uuid=e.get(SMoveDataComp).s_uuid
let SConf=SkillSet[s_uuid]
}
update(entity: ECSEntity): void {
entityEnter(entity: ecs.Entity): void {
const moveComp = entity.get(SMoveDataComp);
const skillView = entity.get(SkillView);
if (!moveComp || !skillView || !skillView.node) return;
// 获取技能配置
const skillConfig = SkillSet[moveComp.s_uuid];
if (!skillConfig) {
console.warn(`[SMoveSystem] 技能配置不存在: ${moveComp.s_uuid}`);
return;
}
// 根据配置设置移动速度
if (skillConfig.speed > 0) {
moveComp.speed = skillConfig.speed;
}
// 根据runType设置初始位置
this.initializePosition(moveComp, skillView);
// 开始移动(除了固定位置类型)
if (moveComp.runType !== RType.fixed && moveComp.runType !== RType.fixedEnd) {
moveComp.startMove();
}
// console.log(`[SMoveSystem] 技能 ${skillConfig.name} 开始移动,类型: ${moveComp.runType}`);
}
/**
* 根据runType初始化技能位置
*/
private initializePosition(moveComp: SMoveDataComp, skillView: SkillView): void {
const node = skillView.node;
switch(moveComp.runType) {
case RType.linear:
// linear类型根据atk_x和atk_y调整位置
const linearPos = v3(
node.position.x + moveComp.atk_x,
node.position.y + moveComp.atk_y,
node.position.z
);
moveComp.rePos(linearPos);
break;
case RType.fixed:
// 固定起始位置
node.setPosition(moveComp.startPos.x, node.position.y, 0);
break;
case RType.fixedEnd:
// 固定结束位置
node.setPosition(moveComp.targetPos.x > 360 ? 300 : moveComp.targetPos.x, node.position.y, 0);
break;
default:
// 其他类型包括bezier根据atk_x和atk_y调整起始位置
if (moveComp.atk_x !== 0 || moveComp.atk_y !== 0) {
const adjustedStartPos = v3(
moveComp.startPos.x + moveComp.atk_x * moveComp.scale,
moveComp.startPos.y + moveComp.atk_y,
moveComp.startPos.z
);
moveComp.startPos.set(adjustedStartPos);
node.setPosition(adjustedStartPos);
}
break;
}
}
update(entity: ecs.Entity): void {
const moveComp = entity.get(SMoveDataComp);
const skillView = entity.get(SkillView);
if (!moveComp || !skillView || !skillView.node) return;
// 更新移动进度
const isMoving = moveComp.updateProgress(this.dt);
if (isMoving) {
// 更新节点位置
skillView.node.setPosition(moveComp.currentPos);
// 处理自动旋转(贝塞尔移动)
if (moveComp.runType === RType.bezier) {
this.updateRotation(skillView.node, moveComp);
}
}
// 检查移动完成
if (moveComp.isCompleted && moveComp.autoDestroy) {
// 根据结束类型决定是否销毁
if (moveComp.endType === EType.timeEnd || moveComp.endType === EType.collision) {
entity.destroy();
}
}
}
/**
* 更新节点旋转(用于贝塞尔移动)
*/
private updateRotation(node: Node, moveComp: SMoveDataComp) {
if (moveComp.progress < 1) {
// 计算下一帧的位置来确定方向
const nextProgress = Math.min(moveComp.progress + 0.01, 1);
const nextPos = v3(0, 0, 0);
// 计算下一个位置
const t = nextProgress;
const oneMinusT = 1 - t;
const oneMinusTSquared = oneMinusT * oneMinusT;
const tSquared = t * t;
const twoOneMinusTt = 2 * oneMinusT * t;
nextPos.x = oneMinusTSquared * moveComp.startPos.x +
twoOneMinusTt * moveComp.controlPoint.x +
tSquared * moveComp.targetPos.x;
nextPos.y = oneMinusTSquared * moveComp.startPos.y +
twoOneMinusTt * moveComp.controlPoint.y +
tSquared * moveComp.targetPos.y;
// 计算方向角度
const direction = new Vec3();
Vec3.subtract(direction, nextPos, moveComp.currentPos);
if (direction.length() > 0.01) {
const angle = Math.atan2(direction.y, direction.x) * (180 / Math.PI);
node.angle = angle;
}
}
}
}
/**
* ==================== 技能移动辅助工具 ====================
*/
export class SMoveHelper {
/**
* 为技能实体设置移动参数
*/
static setupMoveForSkill(entity: ecs.Entity, startPos: Vec3, targetPos: Vec3, s_uuid: number): void {
let moveComp = entity.get(SMoveDataComp);
if (!moveComp) {
moveComp = entity.add(SMoveDataComp);
}
moveComp.startPos.set(startPos);
moveComp.targetPos.set(targetPos);
moveComp.s_uuid = s_uuid;
// 从技能配置获取默认参数
const skillConfig = SkillSet[s_uuid];
if (skillConfig) {
moveComp.runType = skillConfig.RType || RType.linear;
moveComp.speed = skillConfig.speed || 500;
}
}
/**
* 检查技能是否正在移动
*/
static isSkillMoving(entity: ecs.Entity): boolean {
const moveComp = entity.get(SMoveDataComp);
return moveComp ? moveComp.isMoving : false;
}
/**
* 获取技能移动统计信息
*/
static getSkillMoveStats(entity: ecs.Entity): any {
const moveComp = entity.get(SMoveDataComp);
return moveComp ? moveComp.getMoveStats() : null;
}
/**
* 停止技能移动
*/
static stopSkillMove(entity: ecs.Entity): void {
const moveComp = entity.get(SMoveDataComp);
if (moveComp) {
moveComp.stopMove();
}
}
}