Files
pixelheros/assets/script/game/skill/SMoveSystem.ts
walkpan 7cb87433a1 feat(技能系统): 实现技能移动系统并优化位置计算
- 新增SMoveSystem处理技能移动逻辑,支持多种移动类型
- 使用Vec3.lerp优化直线运动的位置计算
- 调整技能起始位置y轴偏移量
- 为线性移动类型添加方向旋转功能
2026-01-02 23:52:52 +08:00

225 lines
7.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { v3, Vec3 ,Node} from "cc";
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { EType, RType, SkillSet } from "../common/config/SkillSet";
import { SMoveDataComp } from "./SMoveComp";
import { smc } from "../common/SingletonModuleComp";
import { SkillView } from "./SkillView";
/**
* ==================== 技能移动系统 ====================
*
* 职责:
* 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);
}
entityEnter(entity: ecs.Entity): void {
if(!smc.mission.play || smc.mission.pause) return;
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);
// 设置旋转角度
const direction = new Vec3();
Vec3.subtract(direction, moveComp.targetPos, moveComp.startPos);
if (direction.length() > 0.01) {
const angle = Math.atan2(direction.y, direction.x) * (180 / Math.PI);
node.angle = angle;
}
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 {
if(!smc.mission.play || smc.mission.pause) return;
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();
}
}
}