refactor(skill): 重构技能配置加载逻辑,将参数下沉至prefab配置

1. 新增SkillView组件属性runType/endType/speed/time,替换原配置表字段
2. 批量同步所有技能prefab的配置参数
3. 优化技能运行逻辑,优先使用prefab配置,配置表仅作兜底
4. 新增同步脚本用于批量迁移技能配置
This commit is contained in:
pan
2026-08-13 16:26:19 +08:00
parent c6b5e1431f
commit 695a9e2b66
22 changed files with 268 additions and 95 deletions

View File

@@ -250,7 +250,11 @@
}, },
"atk_x": 10, "atk_x": 10,
"atk_y": 15, "atk_y": 15,
"_id": "" "_id": "",
"runType": 1,
"endType": 2,
"speed": 720,
"time": 0
}, },
{ {
"__type__": "cc.CompPrefabInfo", "__type__": "cc.CompPrefabInfo",
@@ -336,4 +340,4 @@
"instance": null, "instance": null,
"targetOverrides": null "targetOverrides": null
} }
] ]

View File

@@ -250,7 +250,11 @@
}, },
"atk_x": 10, "atk_x": 10,
"atk_y": 15, "atk_y": 15,
"_id": "" "_id": "",
"runType": 1,
"endType": 2,
"speed": 720,
"time": 0
}, },
{ {
"__type__": "cc.CompPrefabInfo", "__type__": "cc.CompPrefabInfo",
@@ -336,4 +340,4 @@
"instance": null, "instance": null,
"targetOverrides": null "targetOverrides": null
} }
] ]

View File

@@ -250,7 +250,11 @@
}, },
"atk_x": 10, "atk_x": 10,
"atk_y": 15, "atk_y": 15,
"_id": "" "_id": "",
"runType": 1,
"endType": 2,
"speed": 720,
"time": 0
}, },
{ {
"__type__": "cc.CompPrefabInfo", "__type__": "cc.CompPrefabInfo",
@@ -336,4 +340,4 @@
"instance": null, "instance": null,
"targetOverrides": null "targetOverrides": null
} }
] ]

View File

@@ -250,7 +250,11 @@
}, },
"atk_x": 10, "atk_x": 10,
"atk_y": 15, "atk_y": 15,
"_id": "" "_id": "",
"runType": 1,
"endType": 2,
"speed": 720,
"time": 0
}, },
{ {
"__type__": "cc.CompPrefabInfo", "__type__": "cc.CompPrefabInfo",
@@ -336,4 +340,4 @@
"instance": null, "instance": null,
"targetOverrides": null "targetOverrides": null
} }
] ]

View File

@@ -250,7 +250,11 @@
}, },
"atk_x": 10, "atk_x": 10,
"atk_y": 15, "atk_y": 15,
"_id": "" "_id": "",
"runType": 1,
"endType": 2,
"speed": 720,
"time": 0
}, },
{ {
"__type__": "cc.CompPrefabInfo", "__type__": "cc.CompPrefabInfo",
@@ -336,4 +340,4 @@
"instance": null, "instance": null,
"targetOverrides": null "targetOverrides": null
} }
] ]

View File

@@ -108,8 +108,8 @@
}, },
"_lscale": { "_lscale": {
"__type__": "cc.Vec3", "__type__": "cc.Vec3",
"x": 1.6, "x": 1.3,
"y": 1.4, "y": 1.1,
"z": 1 "z": 1
}, },
"_mobility": 0, "_mobility": 0,
@@ -252,7 +252,11 @@
"__id__": 11 "__id__": 11
}, },
"atk_x": 20, "atk_x": 20,
"atk_y": 30, "atk_y": 40,
"runType": 2,
"endType": 2,
"speed": 720,
"time": 0,
"_id": "" "_id": ""
}, },
{ {

View File

@@ -282,7 +282,11 @@
}, },
"atk_x": 10, "atk_x": 10,
"atk_y": 25, "atk_y": 25,
"_id": "" "_id": "",
"runType": 1,
"endType": 2,
"speed": 720,
"time": 0
}, },
{ {
"__type__": "cc.CompPrefabInfo", "__type__": "cc.CompPrefabInfo",
@@ -368,4 +372,4 @@
"instance": null, "instance": null,
"targetOverrides": null "targetOverrides": null
} }
] ]

View File

@@ -282,7 +282,11 @@
}, },
"atk_x": 10, "atk_x": 10,
"atk_y": 25, "atk_y": 25,
"_id": "" "_id": "",
"runType": 1,
"endType": 2,
"speed": 720,
"time": 0
}, },
{ {
"__type__": "cc.CompPrefabInfo", "__type__": "cc.CompPrefabInfo",
@@ -368,4 +372,4 @@
"instance": null, "instance": null,
"targetOverrides": null "targetOverrides": null
} }
] ]

View File

@@ -282,7 +282,11 @@
}, },
"atk_x": 10, "atk_x": 10,
"atk_y": 25, "atk_y": 25,
"_id": "" "_id": "",
"runType": 1,
"endType": 2,
"speed": 720,
"time": 0
}, },
{ {
"__type__": "cc.CompPrefabInfo", "__type__": "cc.CompPrefabInfo",
@@ -368,4 +372,4 @@
"instance": null, "instance": null,
"targetOverrides": null "targetOverrides": null
} }
] ]

View File

@@ -250,7 +250,11 @@
}, },
"atk_x": 10, "atk_x": 10,
"atk_y": 15, "atk_y": 15,
"_id": "" "_id": "",
"runType": 1,
"endType": 2,
"speed": 720,
"time": 0
}, },
{ {
"__type__": "cc.CompPrefabInfo", "__type__": "cc.CompPrefabInfo",
@@ -336,4 +340,4 @@
"instance": null, "instance": null,
"targetOverrides": null "targetOverrides": null
} }
] ]

View File

@@ -250,7 +250,11 @@
}, },
"atk_x": 10, "atk_x": 10,
"atk_y": 15, "atk_y": 15,
"_id": "" "_id": "",
"runType": 1,
"endType": 2,
"speed": 720,
"time": 0
}, },
{ {
"__type__": "cc.CompPrefabInfo", "__type__": "cc.CompPrefabInfo",
@@ -336,4 +340,4 @@
"instance": null, "instance": null,
"targetOverrides": null "targetOverrides": null
} }
] ]

View File

@@ -253,7 +253,11 @@
}, },
"atk_x": 0, "atk_x": 0,
"atk_y": 0, "atk_y": 0,
"_id": "" "_id": "",
"runType": 1,
"endType": 2,
"speed": 720,
"time": 0
}, },
{ {
"__type__": "cc.CompPrefabInfo", "__type__": "cc.CompPrefabInfo",
@@ -368,4 +372,4 @@
"instance": null, "instance": null,
"targetOverrides": null "targetOverrides": null
} }
] ]

View File

@@ -389,7 +389,11 @@
}, },
"atk_x": 0, "atk_x": 0,
"atk_y": 0, "atk_y": 0,
"_id": "" "_id": "",
"runType": 0,
"endType": 2,
"speed": 720,
"time": 0
}, },
{ {
"__type__": "cc.CompPrefabInfo", "__type__": "cc.CompPrefabInfo",
@@ -504,4 +508,4 @@
"instance": null, "instance": null,
"targetOverrides": null "targetOverrides": null
} }
] ]

View File

@@ -250,7 +250,11 @@
}, },
"atk_x": 10, "atk_x": 10,
"atk_y": 15, "atk_y": 15,
"_id": "" "_id": "",
"runType": 1,
"endType": 2,
"speed": 720,
"time": 0
}, },
{ {
"__type__": "cc.CompPrefabInfo", "__type__": "cc.CompPrefabInfo",
@@ -336,4 +340,4 @@
"instance": null, "instance": null,
"targetOverrides": null "targetOverrides": null
} }
] ]

View File

@@ -250,7 +250,11 @@
}, },
"atk_x": 10, "atk_x": 10,
"atk_y": 15, "atk_y": 15,
"_id": "" "_id": "",
"runType": 1,
"endType": 2,
"speed": 720,
"time": 0
}, },
{ {
"__type__": "cc.CompPrefabInfo", "__type__": "cc.CompPrefabInfo",
@@ -336,4 +340,4 @@
"instance": null, "instance": null,
"targetOverrides": null "targetOverrides": null
} }
] ]

View File

@@ -250,7 +250,11 @@
}, },
"atk_x": 10, "atk_x": 10,
"atk_y": 15, "atk_y": 15,
"_id": "" "_id": "",
"runType": 1,
"endType": 2,
"speed": 720,
"time": 0
}, },
{ {
"__type__": "cc.CompPrefabInfo", "__type__": "cc.CompPrefabInfo",
@@ -336,4 +340,4 @@
"instance": null, "instance": null,
"targetOverrides": null "targetOverrides": null
} }
] ]

View File

@@ -250,7 +250,11 @@
}, },
"atk_x": 10, "atk_x": 10,
"atk_y": 15, "atk_y": 15,
"_id": "" "_id": "",
"runType": 1,
"endType": 2,
"speed": 720,
"time": 0
}, },
{ {
"__type__": "cc.CompPrefabInfo", "__type__": "cc.CompPrefabInfo",
@@ -336,4 +340,4 @@
"instance": null, "instance": null,
"targetOverrides": null "targetOverrides": null
} }
] ]

View File

@@ -1,4 +1,4 @@
// ========== 从 HeroAttrs.ts 导入属性相关定义 ========== // ========== 从 HeroAttrs.ts 导入属性相关定义 ==========
import { Attrs } from "./HeroAttrs"; import { Attrs } from "./HeroAttrs";
export enum HSSet { export enum HSSet {
@@ -134,6 +134,7 @@ export interface SkillConfig {
gold?: number, // 获取金币技能的金币值 gold?: number, // 获取金币技能的金币值
hit_count: number, // 可命中次数 hit_count: number, // 可命中次数
hitcd: number, // 持续伤害的伤害间隔(秒) hitcd: number, // 持续伤害的伤害间隔(秒)
/** @deprecated 已下沉到 SkillView @propertyprefab 配置),此处仅作兜底 */
speed: number, // 移动速度 speed: number, // 移动速度
with: number, // 宽度(暂时无用) with: number, // 宽度(暂时无用)
ready: number, // 前摇时间 ready: number, // 前摇时间
@@ -142,11 +143,14 @@ export interface SkillConfig {
EAnm: number, // 结束动画ID EAnm: number, // 结束动画ID
DAnm: string, // 命中后动画ID DAnm: string, // 命中后动画ID
IType: IType, // 技能类型(近战/远程/辅助) IType: IType, // 技能类型(近战/远程/辅助)
/** @deprecated 已下沉到 SkillView @propertyprefab 配置),此处仅作兜底 */
RType: RType, // 技能运行类型(直线/贝塞尔/固定起点/固定终点) RType: RType, // 技能运行类型(直线/贝塞尔/固定起点/固定终点)
/** @deprecated 已下沉到 SkillView @propertyprefab 配置),此处仅作兜底 */
EType: EType, // 结束条件(动画结束/时间结束/距离结束/碰撞/次数结束) EType: EType, // 结束条件(动画结束/时间结束/距离结束/碰撞/次数结束)
bezier_start_y?: number, // 贝塞尔起始抬升高度 bezier_start_y?: number, // 贝塞尔起始抬升高度
bezier_mid_y?: number, // 贝塞尔中间高度增量 bezier_mid_y?: number, // 贝塞尔中间高度增量
bezier_arc?: number, // 贝塞尔弧度系数 bezier_arc?: number, // 贝塞尔弧度系数
/** @deprecated 已下沉到 SkillView @propertyprefab 配置),此处仅作兜底 */
time?: number, // timeEnd 持续时间(秒) time?: number, // timeEnd 持续时间(秒)
kind?: SkillKind, // 主效果类型 kind?: SkillKind, // 主效果类型
crt?: number, // 额外暴击率 crt?: number, // 额外暴击率

View File

@@ -1,4 +1,4 @@
import { v3, Vec3 ,Node} from "cc"; import { v3, Vec3, Node } from "cc";
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS"; import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { EType, RType, SkillSet } from "../common/config/SkillSet"; import { EType, RType, SkillSet } from "../common/config/SkillSet";
import { SMoveDataComp } from "./SMoveComp"; import { SMoveDataComp } from "./SMoveComp";
@@ -26,46 +26,46 @@ export class SMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
filter(): ecs.IMatcher { filter(): ecs.IMatcher {
return ecs.allOf(SMoveDataComp, SkillView); return ecs.allOf(SMoveDataComp, SkillView);
} }
entityEnter(entity: ecs.Entity): void { entityEnter(entity: ecs.Entity): void {
const moveComp = entity.get(SMoveDataComp); const moveComp = entity.get(SMoveDataComp);
const skillView = entity.get(SkillView); const skillView = entity.get(SkillView);
if (!moveComp || !skillView || !skillView.node) return; if (!moveComp || !skillView || !skillView.node) return;
// 获取技能配置 // 获取技能配置
const skillConfig = SkillSet[moveComp.s_uuid]; const skillConfig = SkillSet[moveComp.s_uuid];
if (!skillConfig) { if (!skillConfig) {
mLogger.warn(this.debugMode, 'SMoveSystem', `[SMoveSystem] 技能配置不存在: ${moveComp.s_uuid}`); mLogger.warn(this.debugMode, 'SMoveSystem', `[SMoveSystem] 技能配置不存在: ${moveComp.s_uuid}`);
return; return;
} }
// 根据配置设置移动速度与加速度 // 移动参数由 prefab 的 SkillView @property 决定speed<=0 时 Skill.load 已回退配置表值)
if (skillConfig.speed > 0) { if (moveComp.speed <= 0) {
moveComp.speed = skillConfig.speed; moveComp.speed = skillConfig.speed;
} }
if (skillConfig.is_accel) { if (skillConfig.is_accel) {
moveComp.isAccelerate = true; moveComp.isAccelerate = true;
} }
// 根据runType设置初始位置 // 根据runType设置初始位置
this.initializePosition(moveComp, skillView); this.initializePosition(moveComp, skillView);
// 开始移动(除了固定位置类型) // 开始移动(除了固定位置类型)
if (moveComp.runType !== RType.fixed && moveComp.runType !== RType.fixedEnd) { if (moveComp.runType !== RType.fixed && moveComp.runType !== RType.fixedEnd) {
moveComp.startMove(); moveComp.startMove();
} }
mLogger.log(this.debugMode, 'SMoveSystem', `[SMoveSystem] 技能 ${skillConfig.name} 开始移动,类型: ${moveComp.runType}`); mLogger.log(this.debugMode, 'SMoveSystem', `[SMoveSystem] 技能 ${skillConfig.name} 开始移动,类型: ${moveComp.runType}`);
} }
/** /**
* 根据runType初始化技能位置 * 根据runType初始化技能位置
*/ */
private initializePosition(moveComp: SMoveDataComp, skillView: SkillView): void { private initializePosition(moveComp: SMoveDataComp, skillView: SkillView): void {
const node = skillView.node; const node = skillView.node;
switch(moveComp.runType) { switch (moveComp.runType) {
case RType.linear: case RType.linear:
// linear类型根据atk_x和atk_y调整起始位置 // linear类型根据atk_x和atk_y调整起始位置
const adjustedStartPos = v3( const adjustedStartPos = v3(
@@ -73,35 +73,35 @@ export class SMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
moveComp.startPos.y + moveComp.atk_y, moveComp.startPos.y + moveComp.atk_y,
moveComp.startPos.z moveComp.startPos.z
); );
const originTargetPos = v3( const originTargetPos = v3(
moveComp.targetPos.x, moveComp.targetPos.x,
moveComp.targetPos.y, moveComp.targetPos.y,
moveComp.targetPos.z moveComp.targetPos.z
); );
const direction = new Vec3(); const direction = new Vec3();
Vec3.subtract(direction, originTargetPos, adjustedStartPos); Vec3.subtract(direction, originTargetPos, adjustedStartPos);
// 延长终止点统一消亡点的x坐标为 +-500 // 延长终止点统一消亡点的x坐标为 +-500
const targetX = moveComp.scale > 0 ? 500 : -500; const targetX = moveComp.scale > 0 ? 500 : -500;
let targetY = originTargetPos.y; let targetY = originTargetPos.y;
if (Math.abs(direction.x) > 0.01) { if (Math.abs(direction.x) > 0.01) {
const slope = direction.y / direction.x; const slope = direction.y / direction.x;
targetY = adjustedStartPos.y + slope * (targetX - adjustedStartPos.x); targetY = adjustedStartPos.y + slope * (targetX - adjustedStartPos.x);
} }
const adjustedTargetPos = v3( const adjustedTargetPos = v3(
targetX, targetX,
targetY, targetY,
originTargetPos.z originTargetPos.z
); );
moveComp.startPos.set(adjustedStartPos); moveComp.startPos.set(adjustedStartPos);
moveComp.targetPos.set(adjustedTargetPos); moveComp.targetPos.set(adjustedTargetPos);
node.setPosition(adjustedStartPos); node.setPosition(adjustedStartPos);
// 设置旋转角度 // 设置旋转角度
const dirForAngle = new Vec3(); const dirForAngle = new Vec3();
Vec3.subtract(dirForAngle, adjustedTargetPos, adjustedStartPos); Vec3.subtract(dirForAngle, adjustedTargetPos, adjustedStartPos);
@@ -113,12 +113,12 @@ export class SMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
node.angle = angle; node.angle = angle;
} }
break; break;
case RType.fixed: case RType.fixed:
// 固定起始位置 // 固定起始位置
node.setPosition(moveComp.startPos.x, node.position.y, 0); node.setPosition(moveComp.startPos.x, node.position.y, 0);
break; break;
case RType.fixedEnd: case RType.fixedEnd:
// 固定结束位置 // 固定结束位置
node.setPosition(moveComp.targetPos.x > 360 ? 300 : moveComp.targetPos.x, moveComp.targetPos.y, 0); node.setPosition(moveComp.targetPos.x > 360 ? 300 : moveComp.targetPos.x, moveComp.targetPos.y, 0);
@@ -154,26 +154,26 @@ export class SMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
} }
update(entity: ecs.Entity): void { update(entity: ecs.Entity): void {
if(!smc.mission.play ) return; if (!smc.mission.play) return;
if(smc.mission.pause) return if (smc.mission.pause) return
const moveComp = entity.get(SMoveDataComp); const moveComp = entity.get(SMoveDataComp);
const skillView = entity.get(SkillView); const skillView = entity.get(SkillView);
if (!moveComp || !skillView || !skillView.node) return; if (!moveComp || !skillView || !skillView.node) return;
// 更新移动进度 // 更新移动进度
const isMoving = moveComp.updateProgress(this.dt); const isMoving = moveComp.updateProgress(this.dt);
if (isMoving) { if (isMoving) {
// 更新节点位置 // 更新节点位置
skillView.node.setPosition(moveComp.currentPos); skillView.node.setPosition(moveComp.currentPos);
// 处理自动旋转(贝塞尔移动) // 处理自动旋转(贝塞尔移动)
if (moveComp.runType === RType.bezier) { if (moveComp.runType === RType.bezier) {
this.updateRotation(skillView.node, moveComp); this.updateRotation(skillView.node, moveComp);
} }
} }
// 检查移动完成 // 检查移动完成
if (moveComp.isCompleted && moveComp.autoDestroy) { if (moveComp.isCompleted && moveComp.autoDestroy) {
// 根据结束类型决定是否销毁 // 根据结束类型决定是否销毁
@@ -185,7 +185,7 @@ export class SMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
} }
} }
} }
/** /**
* 更新节点旋转(用于贝塞尔移动) * 更新节点旋转(用于贝塞尔移动)
*/ */
@@ -193,29 +193,29 @@ export class SMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
if (moveComp.progress < 1) { if (moveComp.progress < 1) {
// 计算下一帧的位置来确定方向 // 计算下一帧的位置来确定方向
const nextProgress = Math.min(moveComp.progress + 0.01, 1); const nextProgress = Math.min(moveComp.progress + 0.01, 1);
const t = moveComp.isAccelerate ? const t = moveComp.isAccelerate ?
(nextProgress * nextProgress * 0.7 + nextProgress * 0.3) : (nextProgress * nextProgress * 0.7 + nextProgress * 0.3) :
nextProgress; nextProgress;
const nextPos = v3(0, 0, 0); const nextPos = v3(0, 0, 0);
// 计算下一个位置 // 计算下一个位置
const oneMinusT = 1 - t; const oneMinusT = 1 - t;
const oneMinusTSquared = oneMinusT * oneMinusT; const oneMinusTSquared = oneMinusT * oneMinusT;
const tSquared = t * t; const tSquared = t * t;
const twoOneMinusTt = 2 * oneMinusT * t; const twoOneMinusTt = 2 * oneMinusT * t;
nextPos.x = oneMinusTSquared * moveComp.startPos.x + nextPos.x = oneMinusTSquared * moveComp.startPos.x +
twoOneMinusTt * moveComp.controlPoint.x + twoOneMinusTt * moveComp.controlPoint.x +
tSquared * moveComp.targetPos.x; tSquared * moveComp.targetPos.x;
nextPos.y = oneMinusTSquared * moveComp.startPos.y + nextPos.y = oneMinusTSquared * moveComp.startPos.y +
twoOneMinusTt * moveComp.controlPoint.y + twoOneMinusTt * moveComp.controlPoint.y +
tSquared * moveComp.targetPos.y; tSquared * moveComp.targetPos.y;
// 计算方向角度 // 计算方向角度
const direction = new Vec3(); const direction = new Vec3();
Vec3.subtract(direction, nextPos, moveComp.currentPos); Vec3.subtract(direction, nextPos, moveComp.currentPos);
if (direction.length() > 0.01) { if (direction.length() > 0.01) {
let angle = Math.atan2(direction.y, direction.x) * (180 / Math.PI); let angle = Math.atan2(direction.y, direction.x) * (180 / Math.PI);
if (moveComp.scale < 0) { if (moveComp.scale < 0) {
@@ -236,15 +236,15 @@ export class SMoveHelper {
*/ */
static setupMoveForSkill(entity: ecs.Entity, startPos: Vec3, targetPos: Vec3, s_uuid: number): void { static setupMoveForSkill(entity: ecs.Entity, startPos: Vec3, targetPos: Vec3, s_uuid: number): void {
let moveComp = entity.get(SMoveDataComp); let moveComp = entity.get(SMoveDataComp);
if (!moveComp) { if (!moveComp) {
moveComp = entity.add(SMoveDataComp); moveComp = entity.add(SMoveDataComp);
} }
moveComp.startPos.set(startPos); moveComp.startPos.set(startPos);
moveComp.targetPos.set(targetPos); moveComp.targetPos.set(targetPos);
moveComp.s_uuid = s_uuid; moveComp.s_uuid = s_uuid;
// 从技能配置获取默认参数 // 从技能配置获取默认参数
const skillConfig = SkillSet[s_uuid]; const skillConfig = SkillSet[s_uuid];
if (skillConfig) { if (skillConfig) {
@@ -253,7 +253,7 @@ export class SMoveHelper {
if (skillConfig.is_accel) moveComp.isAccelerate = true; if (skillConfig.is_accel) moveComp.isAccelerate = true;
} }
} }
/** /**
* 检查技能是否正在移动 * 检查技能是否正在移动
*/ */
@@ -261,7 +261,7 @@ export class SMoveHelper {
const moveComp = entity.get(SMoveDataComp); const moveComp = entity.get(SMoveDataComp);
return moveComp ? moveComp.isMoving : false; return moveComp ? moveComp.isMoving : false;
} }
/** /**
* 获取技能移动统计信息 * 获取技能移动统计信息
*/ */
@@ -269,7 +269,7 @@ export class SMoveHelper {
const moveComp = entity.get(SMoveDataComp); const moveComp = entity.get(SMoveDataComp);
return moveComp ? moveComp.getMoveStats() : null; return moveComp ? moveComp.getMoveStats() : null;
} }
/** /**
* 停止技能移动 * 停止技能移动
*/ */

View File

@@ -146,7 +146,7 @@ export class Skill extends ecs.Entity {
if (node.isValid) node.destroy(); if (node.isValid) node.destroy();
return; return;
} }
if (config.EType != EType.collision) { if (SView.endType != EType.collision) {
const collider = node.getComponent(BoxCollider2D); const collider = node.getComponent(BoxCollider2D);
if (collider) { if (collider) {
collider.enabled = false collider.enabled = false
@@ -174,8 +174,10 @@ export class Skill extends ecs.Entity {
sMoveCom.targetPos.set(targetPos); sMoveCom.targetPos.set(targetPos);
sMoveCom.s_uuid = s_uuid; sMoveCom.s_uuid = s_uuid;
sMoveCom.scale = caster.node.scale.x < 0 ? -1 : 1; sMoveCom.scale = caster.node.scale.x < 0 ? -1 : 1;
sMoveCom.runType = config.RType; // runType/endType/speed/time 由 prefab 的 SkillView @property 决定,配置表同名字段仅作兜底
sMoveCom.endType = config.EType; sMoveCom.runType = SView.runType;
sMoveCom.endType = SView.endType;
sMoveCom.speed = SView.speed > 0 ? SView.speed : config.speed;
sMoveCom.bezierStartHeight = config.bezier_start_y ?? sMoveCom.bezierStartHeight; sMoveCom.bezierStartHeight = config.bezier_start_y ?? sMoveCom.bezierStartHeight;
sMoveCom.bezierMidHeight = config.bezier_mid_y ?? sMoveCom.bezierMidHeight; sMoveCom.bezierMidHeight = config.bezier_mid_y ?? sMoveCom.bezierMidHeight;
sMoveCom.bezierArc = config.bezier_arc ?? sMoveCom.bezierArc; sMoveCom.bezierArc = config.bezier_arc ?? sMoveCom.bezierArc;
@@ -183,12 +185,12 @@ export class Skill extends ecs.Entity {
sMoveCom.atk_x = SView.atk_x; sMoveCom.atk_x = SView.atk_x;
sMoveCom.atk_y = SView.atk_y; sMoveCom.atk_y = SView.atk_y;
if (config.EType === EType.timeEnd) { if (SView.endType === EType.timeEnd) {
let sTimeCom = this.get(StimeDataComp); let sTimeCom = this.get(StimeDataComp);
if (!sTimeCom) sTimeCom = this.add(StimeDataComp); if (!sTimeCom) sTimeCom = this.add(StimeDataComp);
sTimeCom.reset(); sTimeCom.reset();
sTimeCom.s_uuid = s_uuid; sTimeCom.s_uuid = s_uuid;
sTimeCom.totalTime = Math.max(1, config.time ?? 0); sTimeCom.totalTime = Math.max(1, SView.time > 0 ? SView.time : (config.time ?? 0));
sTimeCom.hitInterval = Math.max(0.5, config.hitcd || 0); sTimeCom.hitInterval = Math.max(0.5, config.hitcd || 0);
} else { } else {
const sTimeCom = this.get(StimeDataComp); const sTimeCom = this.get(StimeDataComp);

View File

@@ -1,4 +1,4 @@
import { _decorator, Animation, CCInteger, Collider2D, Contact2DType, UITransform, v3, Vec3 } from "cc"; import { _decorator, Animation, CCFloat, CCInteger, Collider2D, Contact2DType, Enum, UITransform, v3, Vec3 } from "cc";
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS"; import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp"; import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
import { HeroViewComp } from "../hero/HeroViewComp"; import { HeroViewComp } from "../hero/HeroViewComp";
@@ -22,6 +22,22 @@ export class SkillView extends CCComp {
@property({ type: CCInteger }) @property({ type: CCInteger })
atk_y: number = 0 atk_y: number = 0
/** 运动类型:直线/贝塞尔/固定起点/固定终点(由 prefab 配置,优先级高于 SkillSet */
@property({ type: Enum(RType), tooltip: "技能特效运动类型" })
runType: RType = RType.linear;
/** 消亡条件:动画结束/计时结束/碰撞次数结束(由 prefab 配置,优先级高于 SkillSet */
@property({ type: Enum(EType), tooltip: "技能特效消亡条件" })
endType: EType = EType.collision;
/** 移动速度(由 prefab 配置,<=0 时回退 SkillSet.speed */
@property({ type: CCFloat, slide: true, range: [0, 2000, 10], tooltip: "技能特效移动速度" })
speed: number = 0;
/** timeEnd 持续时间(秒),仅 endType=timeEnd 时生效(由 prefab 配置,<=0 时回退 SkillSet.time */
@property({ type: CCFloat, slide: true, range: [0, 30, 0.1], tooltip: "timeEnd 持续时间(秒)" })
time: number = 0;
private debugMode: boolean = false; private debugMode: boolean = false;
anim: Animation = null; anim: Animation = null;
@@ -45,7 +61,7 @@ export class SkillView extends CCComp {
this.collider.group = this.group; this.collider.group = this.group;
this.collider.off(Contact2DType.BEGIN_CONTACT, this.onBeginContact, this); this.collider.off(Contact2DType.BEGIN_CONTACT, this.onBeginContact, this);
this.collider.on(Contact2DType.BEGIN_CONTACT, this.onBeginContact, this); this.collider.on(Contact2DType.BEGIN_CONTACT, this.onBeginContact, this);
this.collider.enabled = this.SConf?.EType === EType.collision; this.collider.enabled = this.endType === EType.collision;
} }
if (this.node.getComponent(Animation)) { if (this.node.getComponent(Animation)) {
let anim = this.node.getComponent(Animation); let anim = this.node.getComponent(Animation);
@@ -128,13 +144,13 @@ export class SkillView extends CCComp {
onAnimationFinished() { onAnimationFinished() {
// animationEnd 类型:动画结束后再关闭碰撞并销毁 // animationEnd 类型:动画结束后再关闭碰撞并销毁
if (this.SConf.EType == EType.animationEnd) { if (this.endType == EType.animationEnd) {
this.disable_collider_now(); this.disable_collider_now();
this.ent.destroy() this.ent.destroy()
return; return;
} }
// fixed 类型不移动,无法通过移动完成销毁;动画播完作为兜底销毁时机 // fixed 类型不移动,无法通过移动完成销毁;动画播完作为兜底销毁时机
if (this.SConf.RType == RType.fixed) { if (this.runType == RType.fixed) {
this.disable_collider_now(); this.disable_collider_now();
this.ent.destroy() this.ent.destroy()
} }
@@ -142,7 +158,7 @@ export class SkillView extends CCComp {
// 动画帧事件 atk仅负责开启一帧碰撞窗口不负责伤害与计数 // 动画帧事件 atk仅负责开启一帧碰撞窗口不负责伤害与计数
public atk(args: any) { public atk(args: any) {
if (!this.SConf) return; if (!this.SConf) return;
if (this.SConf.EType == EType.collision) return if (this.endType == EType.collision) return
if (this.enable_collider_safely()) { if (this.enable_collider_safely()) {
mLogger.log(this.debugMode, 'SkillView', `[SkillView] [${this.SConf?.name}] 开启碰撞检测`); mLogger.log(this.debugMode, 'SkillView', `[SkillView] [${this.SConf?.name}] 开启碰撞检测`);
this.scheduleOnce(() => { this.scheduleOnce(() => {

View File

@@ -0,0 +1,83 @@
/**
* 一次性迁移脚本:把 SkillSet 中的 RType/EType/speed/time 刷入各技能 prefab 的 SkillView 序列化字段。
* 用法: node tools/sync-skill-prefab.js
* 注意: 直接改写 prefab JSON运行前请保证工作区已提交。
*/
const fs = require('fs');
const path = require('path');
const ROOT = path.resolve(__dirname, '..');
const SKILL_SET = path.join(ROOT, 'assets/script/game/common/config/SkillSet.ts');
const PREFAB_DIR = path.join(ROOT, 'assets/resources/game/skill/atk');
// SkillView 组件在 prefab 中的脚本类 cid
const SKILL_VIEW_CID = '57aabs7TE1J5obTAZczc+64';
const RTYPE = { linear: 0, bezier: 1, fixed: 2, fixedEnd: 3 };
const ETYPE = { animationEnd: 0, timeEnd: 1, collision: 2 };
/** 从 SkillSet.ts 解析 sp_name -> {runType, endType, speed, time} */
function parseSkillSet(src) {
const map = new Map();
// 匹配每个技能条目块
const entryRe = /\d+:\s*\{([\s\S]*?)\n\s*\},/g;
let m;
while ((m = entryRe.exec(src)) !== null) {
const body = m[1];
const sp = body.match(/sp_name:\s*"([^"]+)"/);
if (!sp) continue;
const spName = sp[1];
const rt = body.match(/RType:\s*RType\.(\w+)/);
const et = body.match(/EType:\s*EType\.(\w+)/);
const speed = body.match(/speed:\s*([\d.]+)/);
const time = body.match(/\btime:\s*([\d.]+)/);
const entry = {
runType: rt ? RTYPE[rt[1]] : undefined,
endType: et ? ETYPE[et[1]] : undefined,
speed: speed ? Number(speed[1]) : undefined,
time: time ? Number(time[1]) : 0,
};
// 多个技能可共用同一 sp_name保留首个非 undefined 组合
if (!map.has(spName)) map.set(spName, entry);
}
return map;
}
function main() {
const src = fs.readFileSync(SKILL_SET, 'utf8');
const confMap = parseSkillSet(src);
const files = fs.readdirSync(PREFAB_DIR).filter(f => f.endsWith('.prefab'));
const report = { updated: [], skipped: [], noConf: [] };
for (const file of files) {
const spName = path.basename(file, '.prefab');
const conf = confMap.get(spName);
const fp = path.join(PREFAB_DIR, file);
const json = JSON.parse(fs.readFileSync(fp, 'utf8'));
// 定位 SkillView 组件
const comp = json.find(o => o && o.__type__ === SKILL_VIEW_CID);
if (!comp) { report.skipped.push(file + ' (no SkillView)'); continue; }
if (!conf) { report.noConf.push(file); continue; }
if (conf.runType !== undefined) comp.runType = conf.runType;
if (conf.endType !== undefined) comp.endType = conf.endType;
if (conf.speed !== undefined) comp.speed = conf.speed;
comp.time = conf.time ?? 0;
fs.writeFileSync(fp, JSON.stringify(json, null, 2) + '\n', 'utf8');
report.updated.push(`${file} -> runType=${comp.runType} endType=${comp.endType} speed=${comp.speed} time=${comp.time}`);
}
console.log('=== updated ===');
report.updated.forEach(s => console.log(' ' + s));
console.log('=== no config matched ===');
report.noConf.forEach(s => console.log(' ' + s));
console.log('=== skipped ===');
report.skipped.forEach(s => console.log(' ' + s));
}
main();