From 2f19433a0afb521699ecb86d1158cf07a9302083 Mon Sep 17 00:00:00 2001 From: walkpan Date: Fri, 31 Oct 2025 00:35:51 +0800 Subject: [PATCH] =?UTF-8?q?feat(skill):=20=E9=87=8D=E6=9E=84=E6=8A=80?= =?UTF-8?q?=E8=83=BD=E7=B3=BB=E7=BB=9F=EF=BC=8C=E6=96=B0=E5=A2=9E=E6=8A=80?= =?UTF-8?q?=E8=83=BD=E6=95=B0=E6=8D=AE=E7=BB=84=E4=BB=B6=E5=92=8C=E7=A7=BB?= =?UTF-8?q?=E5=8A=A8=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit refactor(skill): 移除旧技能组件和文档,优化技能配置结构 fix(skill): 修正技能预制体配置错误,统一技能运行类型字段 docs(skill): 删除过时的技能系统说明文档 perf(skill): 优化技能加载逻辑,减少资源消耗 style(skill): 调整代码格式,提高可读性 --- .../resources/game/skill/atk/atk_fire1.prefab | 6 +- assets/resources/game/skill/atk/atk_s1.prefab | 6 +- assets/script/game/common/config/SkillSet.ts | 12 +- assets/script/game/hero/DEventComp.ts | 448 ++++++++++++++++++ ...SkillSystem.ts.meta => DEventComp.ts.meta} | 2 +- assets/script/game/hero/SAutocastSystem.ts | 183 +++++++ .../SAutocastSystem.ts.meta} | 2 +- assets/script/game/hero/SCDSystem.ts | 42 ++ assets/script/game/hero/SCDSystem.ts.meta | 9 + .../hero/{HSkillSystem.ts => SCastSystem.ts} | 129 +---- assets/script/game/hero/SCastSystem.ts.meta | 9 + assets/script/game/hero/SDataComp.ts | 106 +++++ assets/script/game/hero/SDataComp.ts.meta | 9 + assets/script/game/hero/SMoveComp.ts | 47 ++ assets/script/game/hero/SMoveComp.ts.meta | 9 + assets/script/game/hero/STagComps.ts | 53 +++ assets/script/game/hero/STagComps.ts.meta | 9 + .../game/skill/{SkillCom.ts => SDataCom.ts} | 18 +- assets/script/game/skill/SDataCom.ts.meta | 9 + assets/script/game/skill/Skill.ts | 81 +++- assets/script/game/skill/SkillView.ts | 106 ++++- assets/script/game/skill/components.meta | 9 + assets/script/game/skill/systems.meta | 9 + assets/script/game/skill/快速开始.md | 189 -------- assets/script/game/skill/快速开始.md.meta | 11 - .../script/game/skill/新技能系统使用说明.md | 296 ------------ .../game/skill/新技能系统使用说明.md.meta | 11 - 27 files changed, 1141 insertions(+), 679 deletions(-) create mode 100644 assets/script/game/hero/DEventComp.ts rename assets/script/game/hero/{HSkillSystem.ts.meta => DEventComp.ts.meta} (70%) create mode 100644 assets/script/game/hero/SAutocastSystem.ts rename assets/script/game/{skill/SkillCom.ts.meta => hero/SAutocastSystem.ts.meta} (70%) create mode 100644 assets/script/game/hero/SCDSystem.ts create mode 100644 assets/script/game/hero/SCDSystem.ts.meta rename assets/script/game/hero/{HSkillSystem.ts => SCastSystem.ts} (55%) create mode 100644 assets/script/game/hero/SCastSystem.ts.meta create mode 100644 assets/script/game/hero/SDataComp.ts create mode 100644 assets/script/game/hero/SDataComp.ts.meta create mode 100644 assets/script/game/hero/SMoveComp.ts create mode 100644 assets/script/game/hero/SMoveComp.ts.meta create mode 100644 assets/script/game/hero/STagComps.ts create mode 100644 assets/script/game/hero/STagComps.ts.meta rename assets/script/game/skill/{SkillCom.ts => SDataCom.ts} (50%) create mode 100644 assets/script/game/skill/SDataCom.ts.meta create mode 100644 assets/script/game/skill/components.meta create mode 100644 assets/script/game/skill/systems.meta delete mode 100644 assets/script/game/skill/快速开始.md delete mode 100644 assets/script/game/skill/快速开始.md.meta delete mode 100644 assets/script/game/skill/新技能系统使用说明.md delete mode 100644 assets/script/game/skill/新技能系统使用说明.md.meta diff --git a/assets/resources/game/skill/atk/atk_fire1.prefab b/assets/resources/game/skill/atk/atk_fire1.prefab index d5d2a4d7..25a9d990 100644 --- a/assets/resources/game/skill/atk/atk_fire1.prefab +++ b/assets/resources/game/skill/atk/atk_fire1.prefab @@ -374,7 +374,7 @@ "fileId": "81ijmfBpBLBYwxa+xOAd0J" }, { - "__type__": "411a8xY2rNF1JLZn+Qgqfqg", + "__type__": "57aabs7TE1J5obTAZczc+64", "_name": "", "_objFlags": 0, "__editorExtras__": {}, @@ -385,13 +385,11 @@ "__prefab": { "__id__": 19 }, - "anmEnd": false, - "cbox": true, "_id": "" }, { "__type__": "cc.CompPrefabInfo", - "fileId": "7frqHFRhdHXbaDJ490Ea1E" + "fileId": "06RyMZr4pGE5TMqxM1qoYt" }, { "__type__": "cc.PrefabInfo", diff --git a/assets/resources/game/skill/atk/atk_s1.prefab b/assets/resources/game/skill/atk/atk_s1.prefab index fb4386b3..7c49e4e7 100644 --- a/assets/resources/game/skill/atk/atk_s1.prefab +++ b/assets/resources/game/skill/atk/atk_s1.prefab @@ -337,7 +337,7 @@ "fileId": "c2RDvksalG2acL3tyGCY0t" }, { - "__type__": "411a8xY2rNF1JLZn+Qgqfqg", + "__type__": "57aabs7TE1J5obTAZczc+64", "_name": "", "_objFlags": 0, "__editorExtras__": {}, @@ -348,13 +348,11 @@ "__prefab": { "__id__": 17 }, - "anmEnd": true, - "cbox": false, "_id": "" }, { "__type__": "cc.CompPrefabInfo", - "fileId": "157f8YWOhK6pBg6CBgHHww" + "fileId": "35nW0iQNBH2bqkWAr3MVkQ" }, { "__type__": "cc.PrefabInfo", diff --git a/assets/script/game/common/config/SkillSet.ts b/assets/script/game/common/config/SkillSet.ts index 4cd4ed7a..c3396c09 100644 --- a/assets/script/game/common/config/SkillSet.ts +++ b/assets/script/game/common/config/SkillSet.ts @@ -129,7 +129,7 @@ interface IReady { loop: boolean, SkillTime: number,// 技能控制存续时间时间 ReadyTime: number,// 技能前摇时间 - runType: number, //技能运行类型 0-线性 1-贝塞尔 2-开始位置固定 3-目标位置固定 + RType: number, //技能运行类型 0-线性 1-贝塞尔 2-开始位置固定 3-目标位置固定 ready_y: number, path:string, } @@ -143,7 +143,7 @@ interface IEndAnm { // 技能配置接口 - 按照6001格式排列 export interface SkillConfig { uuid:number,name:string,sp_name:string,AtkedName:AtkedName,path:string,TGroup:TGroup,SType:SType,act:string,DTType:DTType,DType:DType, - ap:number,cd:number,t_num:number,hit_num:number,hit:number,hitcd:number,speed:number,cost:number,with:number,ready:number,endAnm:number + ap:number,cd:number,t_num:number,hit_num:number,hit:number,hitcd:number,speed:number,cost:number,with:number,ready:number,endAnm:number,RType:RType, buffs:BuffConf[],neAttrs:NeAttrsConf[],info:string,hero?:number , } @@ -151,20 +151,20 @@ export const SkillSet: Record = { // ========== 基础攻击 ========== 6001-6099 6001: { uuid:6001,name:"挥击",sp_name:"atk_s1",AtkedName:AtkedName.atked,path:"3036",TGroup:TGroup.Enemy,SType:SType.damage,act:"atk",DTType:DTType.single,DType:DType.ATK, - ap:100,cd:1,t_num:1,hit_num:1,hit:1,hitcd:0.2,speed:720,cost:0,with:0,ready:0,endAnm:0, + ap:100,cd:1,t_num:1,hit_num:1,hit:1,hitcd:0.2,speed:720,cost:0,with:0,ready:0,endAnm:0,RType:RType.fixed, buffs:[],neAttrs:[],info:"向最前方敌人扔出石斧,造成100%攻击的伤害", }, 6005: { uuid:6005,name:"火球术",sp_name:"atk_fire1",AtkedName:AtkedName.atked,path:"3039",TGroup:TGroup.Enemy,SType:SType.damage,act:"atk",DTType:DTType.single,DType:DType.MAGE, - ap:100,cd:5,t_num:1,hit_num:1,hit:2,hitcd:0.3,speed:720,cost:20,with:90,ready:8001,endAnm:9001, + ap:100,cd:5,t_num:1,hit_num:1,hit:2,hitcd:0.3,speed:720,cost:20,with:90,ready:8001,endAnm:9001,RType:RType.linear, buffs:[],neAttrs:[],info:"召唤大火球攻击前方所有敌人,造成300%攻击的伤害,有一定几率施加灼烧", }, }; export const SkillReadyConf: Record = { - 0:{uuid:0,loop:false,SkillTime:0,ReadyTime:0,runType:0,ready_y:0,path:""}, // 无前摇 - 8001:{uuid:8001,loop:false,SkillTime:0,ReadyTime:0,runType:0,ready_y:0,path:"fire1"}, + 0:{uuid:0,loop:false,SkillTime:0,ReadyTime:0,RType:0,ready_y:0,path:""}, // 无前摇 + 8001:{uuid:8001,loop:false,SkillTime:0,ReadyTime:0,RType:0,ready_y:0,path:"fire1"}, }; export const EndAnmConf: Record = { diff --git a/assets/script/game/hero/DEventComp.ts b/assets/script/game/hero/DEventComp.ts new file mode 100644 index 00000000..55fc9f7e --- /dev/null +++ b/assets/script/game/hero/DEventComp.ts @@ -0,0 +1,448 @@ +import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS"; +import { Vec3 } from "cc"; + +/** + * ==================== 伤害事件组件 ==================== + * + * 用途: + * - 记录伤害事件的详细信息 + * - 支持多种伤害类型和效果 + * - 被伤害处理系统使用 + */ +@ecs.register('DamageEvent') +export class DamageEventComp extends ecs.Comp { + /** 伤害值 */ + damage: number = 0; + + /** 伤害类型 */ + damageType: number = 0; // 0: 物理, 1: 魔法, 2: 真实, 3: 治疗 + + /** 伤害来源实体ID */ + sourceEntityId: number = 0; + + /** 目标实体ID */ + targetEntityId: number = 0; + + /** 技能ID */ + skillId: number = 0; + + /** 伤害位置 */ + position: Vec3 = new Vec3(); + + /** 是否暴击 */ + isCritical: boolean = false; + + /** 暴击倍率 */ + criticalMultiplier: number = 2.0; + + /** 伤害减免 */ + damageReduction: number = 0; + + /** 护甲穿透 */ + armorPenetration: number = 0; + + /** 魔法抗性穿透 */ + magicPenetration: number = 0; + + /** 伤害标签 */ + damageTags: string[] = []; + + /** 附加效果 */ + effects: DamageEffectData[] = []; + + /** 是否已处理 */ + processed: boolean = false; + + /** 创建时间 */ + createTime: number = 0; + + /** 额外数据 */ + extraData: any = null; + + reset() { + this.damage = 0; + this.damageType = 0; + this.sourceEntityId = 0; + this.targetEntityId = 0; + this.skillId = 0; + this.position.set(0, 0, 0); + this.isCritical = false; + this.criticalMultiplier = 2.0; + this.damageReduction = 0; + this.armorPenetration = 0; + this.magicPenetration = 0; + this.damageTags = []; + this.effects = []; + this.processed = false; + this.createTime = 0; + this.extraData = null; + } + + /** + * 初始化伤害事件 + */ + initialize(damage: number, damageType: number, sourceId: number, targetId: number, skillId: number, position: Vec3): void { + this.damage = damage; + this.damageType = damageType; + this.sourceEntityId = sourceId; + this.targetEntityId = targetId; + this.skillId = skillId; + this.position.set(position); + this.createTime = Date.now(); + this.processed = false; + } + + /** + * 添加伤害标签 + */ + addTag(tag: string): void { + if (!this.damageTags.includes(tag)) { + this.damageTags.push(tag); + } + } + + /** + * 检查是否包含标签 + */ + hasTag(tag: string): boolean { + return this.damageTags.includes(tag); + } + + /** + * 添加附加效果 + */ + addEffect(effect: DamageEffectData): void { + this.effects.push(effect); + } + + /** + * 计算最终伤害 + */ + calculateFinalDamage(targetArmor: number, targetMagicResist: number): number { + let finalDamage = this.damage; + + // 应用暴击 + if (this.isCritical) { + finalDamage *= this.criticalMultiplier; + } + + // 应用护甲和魔抗 + if (this.damageType === 0) { // 物理伤害 + const effectiveArmor = Math.max(0, targetArmor - this.armorPenetration); + const damageMultiplier = 100 / (100 + effectiveArmor); + finalDamage *= damageMultiplier; + } else if (this.damageType === 1) { // 魔法伤害 + const effectiveMagicResist = Math.max(0, targetMagicResist - this.magicPenetration); + const damageMultiplier = 100 / (100 + effectiveMagicResist); + finalDamage *= damageMultiplier; + } + // 真实伤害和治疗不受护甲影响 + + // 应用伤害减免 + finalDamage *= (1 - this.damageReduction); + + return Math.max(0, finalDamage); + } +} + +/** + * ==================== 伤害效果数据 ==================== + * + * 用途: + * - 定义伤害附加的各种效果 + * - 如眩晕、减速、燃烧等 + */ +export class DamageEffectData { + /** 效果类型 */ + effectType: number = 0; // 0: 眩晕, 1: 减速, 2: 燃烧, 3: 冰冻, 4: 中毒 + + /** 效果持续时间 */ + duration: number = 0; + + /** 效果强度 */ + intensity: number = 0; + + /** 效果参数 */ + params: any = null; + + constructor(type: number, duration: number, intensity: number = 1, params: any = null) { + this.effectType = type; + this.duration = duration; + this.intensity = intensity; + this.params = params; + } +} + +/** + * ==================== 治疗事件组件 ==================== + * + * 用途: + * - 记录治疗事件的详细信息 + * - 支持多种治疗类型 + * - 被治疗处理系统使用 + */ +@ecs.register('HealEvent') +export class HealEventComp extends ecs.Comp { + /** 治疗值 */ + healAmount: number = 0; + + /** 治疗类型 */ + healType: number = 0; // 0: 瞬间治疗, 1: 持续治疗, 2: 护盾 + + /** 治疗来源实体ID */ + sourceEntityId: number = 0; + + /** 目标实体ID */ + targetEntityId: number = 0; + + /** 技能ID */ + skillId: number = 0; + + /** 治疗位置 */ + position: Vec3 = new Vec3(); + + /** 是否暴击治疗 */ + isCritical: boolean = false; + + /** 暴击倍率 */ + criticalMultiplier: number = 1.5; + + /** 治疗加成 */ + healBonus: number = 0; + + /** 是否已处理 */ + processed: boolean = false; + + /** 创建时间 */ + createTime: number = 0; + + reset() { + this.healAmount = 0; + this.healType = 0; + this.sourceEntityId = 0; + this.targetEntityId = 0; + this.skillId = 0; + this.position.set(0, 0, 0); + this.isCritical = false; + this.criticalMultiplier = 1.5; + this.healBonus = 0; + this.processed = false; + this.createTime = 0; + } + + /** + * 计算最终治疗量 + */ + calculateFinalHeal(): number { + let finalHeal = this.healAmount; + + // 应用暴击 + if (this.isCritical) { + finalHeal *= this.criticalMultiplier; + } + + // 应用治疗加成 + finalHeal *= (1 + this.healBonus); + + return Math.max(0, finalHeal); + } +} + +/** + * ==================== 状态效果组件 ==================== + * + * 用途: + * - 管理角色身上的各种状态效果 + * - 如BUFF、DEBUFF等 + * - 支持叠加和刷新 + */ +@ecs.register('StatusEffect') +export class StatusEffectComp extends ecs.Comp { + /** 效果ID */ + effectId: number = 0; + + /** 效果类型 */ + effectType: number = 0; + + /** 效果名称 */ + name: string = ""; + + /** 剩余持续时间 */ + remainingDuration: number = 0; + + /** 总持续时间 */ + totalDuration: number = 0; + + /** 效果强度 */ + intensity: number = 1; + + /** 叠加层数 */ + stackCount: number = 1; + + /** 最大叠加层数 */ + maxStacks: number = 1; + + /** 来源实体ID */ + sourceEntityId: number = 0; + + /** 技能ID */ + skillId: number = 0; + + /** 是否为BUFF */ + isBuff: boolean = true; + + /** 是否可驱散 */ + canDispel: boolean = true; + + /** 效果参数 */ + params: any = null; + + /** 图标路径 */ + iconPath: string = ""; + + reset() { + this.effectId = 0; + this.effectType = 0; + this.name = ""; + this.remainingDuration = 0; + this.totalDuration = 0; + this.intensity = 1; + this.stackCount = 1; + this.maxStacks = 1; + this.sourceEntityId = 0; + this.skillId = 0; + this.isBuff = true; + this.canDispel = true; + this.params = null; + this.iconPath = ""; + } + + /** + * 更新效果持续时间 + */ + updateDuration(deltaTime: number): boolean { + this.remainingDuration -= deltaTime; + return this.remainingDuration > 0; + } + + /** + * 刷新效果持续时间 + */ + refreshDuration(): void { + this.remainingDuration = this.totalDuration; + } + + /** + * 增加叠加层数 + */ + addStack(count: number = 1): void { + this.stackCount = Math.min(this.maxStacks, this.stackCount + count); + } + + /** + * 减少叠加层数 + */ + removeStack(count: number = 1): void { + this.stackCount = Math.max(0, this.stackCount - count); + } + + /** + * 获取效果进度 (0-1) + */ + getProgress(): number { + if (this.totalDuration <= 0) return 1; + return Math.max(0, 1 - (this.remainingDuration / this.totalDuration)); + } +} + +/** + * ==================== 伤害统计组件 ==================== + * + * 用途: + * - 统计伤害数据 + * - 用于战斗分析和显示 + */ +@ecs.register('DamageStats') +export class DamageStatsComp extends ecs.Comp { + /** 总伤害输出 */ + totalDamageDealt: number = 0; + + /** 总伤害承受 */ + totalDamageTaken: number = 0; + + /** 总治疗量 */ + totalHealingDone: number = 0; + + /** 暴击次数 */ + criticalHits: number = 0; + + /** 总攻击次数 */ + totalAttacks: number = 0; + + /** 击杀数 */ + kills: number = 0; + + /** 死亡数 */ + deaths: number = 0; + + /** 技能使用统计 */ + skillUsageStats: Map = new Map(); + + reset() { + this.totalDamageDealt = 0; + this.totalDamageTaken = 0; + this.totalHealingDone = 0; + this.criticalHits = 0; + this.totalAttacks = 0; + this.kills = 0; + this.deaths = 0; + this.skillUsageStats.clear(); + } + + /** + * 记录伤害输出 + */ + recordDamageDealt(damage: number, isCritical: boolean = false): void { + this.totalDamageDealt += damage; + this.totalAttacks++; + if (isCritical) { + this.criticalHits++; + } + } + + /** + * 记录伤害承受 + */ + recordDamageTaken(damage: number): void { + this.totalDamageTaken += damage; + } + + /** + * 记录治疗 + */ + recordHealing(healing: number): void { + this.totalHealingDone += healing; + } + + /** + * 记录技能使用 + */ + recordSkillUsage(skillId: number): void { + const currentCount = this.skillUsageStats.get(skillId) || 0; + this.skillUsageStats.set(skillId, currentCount + 1); + } + + /** + * 获取暴击率 + */ + getCriticalRate(): number { + return this.totalAttacks > 0 ? this.criticalHits / this.totalAttacks : 0; + } + + /** + * 获取平均伤害 + */ + getAverageDamage(): number { + return this.totalAttacks > 0 ? this.totalDamageDealt / this.totalAttacks : 0; + } +} \ No newline at end of file diff --git a/assets/script/game/hero/HSkillSystem.ts.meta b/assets/script/game/hero/DEventComp.ts.meta similarity index 70% rename from assets/script/game/hero/HSkillSystem.ts.meta rename to assets/script/game/hero/DEventComp.ts.meta index 917f7569..3239fa35 100644 --- a/assets/script/game/hero/HSkillSystem.ts.meta +++ b/assets/script/game/hero/DEventComp.ts.meta @@ -2,7 +2,7 @@ "ver": "4.0.24", "importer": "typescript", "imported": true, - "uuid": "500ce1a5-24eb-4d18-ac90-11301a372f0e", + "uuid": "92032577-e289-43f6-92f3-6d99c7ec4ce8", "files": [], "subMetas": {}, "userData": {} diff --git a/assets/script/game/hero/SAutocastSystem.ts b/assets/script/game/hero/SAutocastSystem.ts new file mode 100644 index 00000000..fb374946 --- /dev/null +++ b/assets/script/game/hero/SAutocastSystem.ts @@ -0,0 +1,183 @@ +import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS"; +import { Vec3, v3 } from "cc"; +import { CastSkillRequestComp } from "./STagComps"; +import { HeroAttrsComp } from "./HeroAttrsComp"; +import { HeroViewComp } from "./HeroViewComp"; +import { SkillSet, SType } from "../common/config/SkillSet"; +import { HeroSkillsComp } from "./HeroSkills"; + +/** + * ==================== 自动施法系统 ==================== + * + * 职责: + * 1. 检测可施放的技能 + * 2. 根据策略自动施法(AI) + * 3. 选择目标 + * 4. 添加施法请求标记 + * + * 设计理念: + * - 负责"何时施法"的决策 + * - 通过添加 CastSkillRequestComp 触发施法 + * - 可被玩家输入系统或AI系统复用 + * - 支持多种AI策略 + */ +@ecs.register('SkillAutocastSystem') +export class SkillAutocastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate { + + filter(): ecs.IMatcher { + return ecs.allOf(HeroSkillsComp, HeroAttrsComp, HeroViewComp); + } + + update(e: ecs.Entity): void { + const skills = e.get(HeroSkillsComp); + const heroModel = e.get(HeroAttrsComp); + const heroView = e.get(HeroViewComp); + if (!skills || !heroModel || !heroView) return; + + // 检查基本条件 + if (heroModel.is_dead || heroModel.isStun() || heroModel.isFrost()) return; + + // 检查是否正在攻击(只有攻击时才释放技能) + if (!heroModel.is_atking) return; + + // 获取所有可施放的技能 + const readySkills = skills.getReadySkills(heroModel.mp); + if (readySkills.length === 0) return; + + // 选择第一个可施放的伤害技能 + for (const skillIndex of readySkills) { + const skill = skills.getSkill(skillIndex); + if (!skill) continue; + + const config = SkillSet[skill.uuid]; + if (!config || config.SType !== SType.damage) continue; + + // ✅ 添加施法请求标记组件 + const request = e.add(CastSkillRequestComp) as CastSkillRequestComp; + request.skillIndex = skillIndex; + request.targetPositions = this.selectTargets(heroView); + + // 一次只施放一个技能 + break; + } + } + + /** + * 选择目标位置 + */ + private selectTargets(caster: HeroViewComp): Vec3[] { + // 简化版:选择最前方的敌人 + const targets: Vec3[] = []; + + // 这里可以调用 SkillConComp 的目标选择逻辑 + // 暂时返回默认位置 + const heroModel = caster.ent.get(HeroAttrsComp); + const fac = heroModel?.fac ?? 0; + const defaultX = fac === 0 ? 400 : -400; + targets.push(v3(defaultX, 0, 0)); + + return targets; + } + + /** + * 选择伤害技能目标 + */ + private selectDamageTargets(caster: HeroViewComp, config: any, maxTargets: number): Vec3[] { + const targets: Vec3[] = []; + const heroModel = caster.ent.get(HeroAttrsComp); + if (!heroModel) return targets; + + // 寻找最近的敌人 + const enemyPositions = this.findNearbyEnemies(caster, heroModel.fac, config.range || 300); + + // 选择最多maxTargets个目标 + for (let i = 0; i < Math.min(maxTargets, enemyPositions.length); i++) { + targets.push(enemyPositions[i]); + } + + // 如果没有找到敌人,使用默认位置 + if (targets.length === 0) { + targets.push(...this.selectDefaultTargets(caster, heroModel.fac)); + } + + return targets; + } + + /** + * 选择治疗技能目标 + */ + private selectHealTargets(caster: HeroViewComp, config: any, maxTargets: number): Vec3[] { + const targets: Vec3[] = []; + const heroModel = caster.ent.get(HeroAttrsComp); + if (!heroModel) return targets; + + // 寻找血量最低的友军 + const allyPositions = this.findLowHealthAllies(caster, heroModel.fac, config.range || 200); + + for (let i = 0; i < Math.min(maxTargets, allyPositions.length); i++) { + targets.push(allyPositions[i]); + } + + // 如果没有找到友军,治疗自己 + if (targets.length === 0 && caster.node) { + targets.push(caster.node.position.clone()); + } + + return targets; + } + + /** + * 选择BUFF技能目标 + */ + private selectBuffTargets(caster: HeroViewComp, config: any, maxTargets: number): Vec3[] { + // BUFF技能通常施放在自己或友军身上 + return this.selectHealTargets(caster, config, maxTargets); + } + + /** + * 选择DEBUFF技能目标 + */ + private selectDebuffTargets(caster: HeroViewComp, config: any, maxTargets: number): Vec3[] { + // DEBUFF技能通常施放在敌人身上 + return this.selectDamageTargets(caster, config, maxTargets); + } + + /** + * 选择默认目标 + */ + private selectDefaultTargets(caster: HeroViewComp, faction: number): Vec3[] { + const targets: Vec3[] = []; + const defaultX = faction === 0 ? 400 : -400; + targets.push(v3(defaultX, 0, 0)); + return targets; + } + + /** + * 查找附近的敌人 + */ + private findNearbyEnemies(caster: HeroViewComp, faction: number, range: number): Vec3[] { + // 简化实现,实际应该查询ECS中的敌方实体 + const enemies: Vec3[] = []; + + // 模拟敌人位置 + const enemyX = faction === 0 ? 300 : -300; + enemies.push(v3(enemyX, 0, 0)); + enemies.push(v3(enemyX + 50, 20, 0)); + + return enemies; + } + + /** + * 查找血量低的友军 + */ + private findLowHealthAllies(caster: HeroViewComp, faction: number, range: number): Vec3[] { + // 简化实现,实际应该查询ECS中的友方实体并按血量排序 + const allies: Vec3[] = []; + + // 如果自己血量低,优先治疗自己 + + + return allies; + } + +} \ No newline at end of file diff --git a/assets/script/game/skill/SkillCom.ts.meta b/assets/script/game/hero/SAutocastSystem.ts.meta similarity index 70% rename from assets/script/game/skill/SkillCom.ts.meta rename to assets/script/game/hero/SAutocastSystem.ts.meta index 2eb69764..b180a505 100644 --- a/assets/script/game/skill/SkillCom.ts.meta +++ b/assets/script/game/hero/SAutocastSystem.ts.meta @@ -2,7 +2,7 @@ "ver": "4.0.24", "importer": "typescript", "imported": true, - "uuid": "4338992d-a768-4089-b1d2-dd8695712fc4", + "uuid": "a38a89b8-24eb-429f-92d4-7d3f4d87ba88", "files": [], "subMetas": {}, "userData": {} diff --git a/assets/script/game/hero/SCDSystem.ts b/assets/script/game/hero/SCDSystem.ts new file mode 100644 index 00000000..bd2cdfa3 --- /dev/null +++ b/assets/script/game/hero/SCDSystem.ts @@ -0,0 +1,42 @@ +import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS"; +import { HeroSkillsComp } from "./HeroSkills"; + +/** + * ==================== 技能CD更新系统 ==================== + * + * 职责: + * 1. 每帧更新所有角色的技能CD + * 2. 自动递减CD时间 + * 3. 管理技能冷却状态 + * 4. 优化CD计算性能 + * + * 设计理念: + * - 独立的CD管理系统 + * - 只负责时间递减,不处理施法逻辑 + * - 支持CD加速和减免效果 + */ +@ecs.register('SkillCDSystem') +export class SkillCDSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate { + + filter(): ecs.IMatcher { + return ecs.allOf(HeroSkillsComp); + } + + update(e: ecs.Entity): void { + const skills = e.get(HeroSkillsComp); + if (!skills) return; + + // 更新所有技能CD + skills.updateCDs(this.dt); + } + + /** + * 计算CD减免效果 + */ + + + /** + * 更新技能就绪状态 + */ + +} diff --git a/assets/script/game/hero/SCDSystem.ts.meta b/assets/script/game/hero/SCDSystem.ts.meta new file mode 100644 index 00000000..0c0afdeb --- /dev/null +++ b/assets/script/game/hero/SCDSystem.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "90b6dfb3-0a9c-4400-a356-b85a080ea86c", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/assets/script/game/hero/HSkillSystem.ts b/assets/script/game/hero/SCastSystem.ts similarity index 55% rename from assets/script/game/hero/HSkillSystem.ts rename to assets/script/game/hero/SCastSystem.ts index 7f4f9785..7b25c66a 100644 --- a/assets/script/game/hero/HSkillSystem.ts +++ b/assets/script/game/hero/SCastSystem.ts @@ -1,33 +1,11 @@ import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS"; import { Vec3, v3 } from "cc"; -import { HeroSkillsComp } from "./HeroSkills"; +import { CastSkillRequestComp } from "./STagComps"; import { HeroAttrsComp } from "./HeroAttrsComp"; import { HeroViewComp } from "./HeroViewComp"; import { SkillSet, SType } from "../common/config/SkillSet"; import { SkillEnt } from "../skill/SkillEnt"; -import { smc } from "../common/SingletonModuleComp"; - -/** - * ==================== 施法请求标记组件 ==================== - * - * 用途: - * - 标记角色想要施放某个技能 - * - 由外部(如AI系统、玩家输入)添加 - * - 施法系统处理后自动移除 - */ -@ecs.register('CastSkillRequest') -export class CastSkillRequestComp extends ecs.Comp { - /** 技能索引(在 HeroSkillsComp.skills 中的位置) */ - skillIndex: number = 0; - - /** 目标位置数组(由请求者提供) */ - targetPositions: Vec3[] = []; - - reset() { - this.skillIndex = 0; - this.targetPositions = []; - } -} +import { HeroSkillsComp } from "./HeroSkills"; /** * ==================== 技能施法系统 ==================== @@ -48,7 +26,7 @@ export class CastSkillRequestComp extends ecs.Comp { @ecs.register('SkillCastSystem') export class SkillCastSystem extends ecs.ComblockSystem implements ecs.IEntityEnterSystem { - /** + /** * 过滤器:拥有技能数据 + 施法请求的实体 */ filter(): ecs.IMatcher { @@ -166,107 +144,6 @@ export class SkillCastSystem extends ecs.ComblockSystem implements ecs.IEntityEn skillId, // 技能ID targetPositions, // 目标位置数组 caster, // 施法者 - 0 // 额外伤害(暂时为0) ); } -} - -/** - * ==================== 技能CD更新系统 ==================== - * - * 职责: - * 1. 每帧更新所有角色的技能CD - * 2. 自动递减CD时间 - * - * 设计理念: - * - 独立的CD管理系统 - * - 只负责时间递减,不处理施法逻辑 - */ -@ecs.register('SkillCDSystem') -export class SkillCDSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate { - - filter(): ecs.IMatcher { - return ecs.allOf(HeroSkillsComp); - } - - update(e: ecs.Entity): void { - const skillsData = e.get(HeroSkillsComp); - if (!skillsData) return; - - // 更新所有技能CD - skillsData.updateCDs(this.dt); - } -} - -/** - * ==================== 自动施法系统 ==================== - * - * 职责: - * 1. 检测可施放的技能 - * 2. 根据策略自动施法(AI) - * 3. 选择目标 - * 4. 添加施法请求标记 - * - * 设计理念: - * - 负责"何时施法"的决策 - * - 通过添加 CastSkillRequestComp 触发施法 - * - 可被玩家输入系统或AI系统复用 - */ -@ecs.register('SkillAutocastSystem') -export class SkillAutocastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate { - - filter(): ecs.IMatcher { - return ecs.allOf(HeroSkillsComp, HeroAttrsComp, HeroViewComp); - } - - update(e: ecs.Entity): void { - const skillsData = e.get(HeroSkillsComp); - const heroModel = e.get(HeroAttrsComp); - const heroView = e.get(HeroViewComp); - if (!skillsData || !heroModel || !heroView) return; - - // 检查基本条件 - if (heroModel.is_dead || heroModel.isStun() || heroModel.isFrost()) return; - - // 检查是否正在攻击(只有攻击时才释放技能) - if (!heroModel.is_atking) return; - - // 获取所有可施放的技能 - const readySkills = skillsData.getReadySkills(heroModel.mp); - if (readySkills.length === 0) return; - - // 选择第一个可施放的伤害技能 - for (const skillIndex of readySkills) { - const skill = skillsData.getSkill(skillIndex); - if (!skill) continue; - - const config = SkillSet[skill.uuid]; - if (!config || config.SType !== SType.damage) continue; - - // ✅ 添加施法请求标记组件 - const request = e.add(CastSkillRequestComp) as CastSkillRequestComp; - request.skillIndex = skillIndex; - request.targetPositions = this.selectTargets(heroView); - - // 一次只施放一个技能 - break; - } - } - - /** - * 选择目标位置 - */ - private selectTargets(caster: HeroViewComp): Vec3[] { - // 简化版:选择最前方的敌人 - const targets: Vec3[] = []; - - // 这里可以调用 SkillConComp 的目标选择逻辑 - // 暂时返回默认位置 - const heroModel = caster.ent.get(HeroAttrsComp); - const fac = heroModel?.fac ?? 0; - const defaultX = fac === 0 ? 400 : -400; - targets.push(v3(defaultX, 0, 0)); - - return targets; - } } \ No newline at end of file diff --git a/assets/script/game/hero/SCastSystem.ts.meta b/assets/script/game/hero/SCastSystem.ts.meta new file mode 100644 index 00000000..e8087ae2 --- /dev/null +++ b/assets/script/game/hero/SCastSystem.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "25f14ca0-5053-495e-bc3d-08b1bb4ee5d7", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/assets/script/game/hero/SDataComp.ts b/assets/script/game/hero/SDataComp.ts new file mode 100644 index 00000000..4afe7af5 --- /dev/null +++ b/assets/script/game/hero/SDataComp.ts @@ -0,0 +1,106 @@ +import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS"; +import { Vec3 } from "cc"; + +/** + * ==================== 技能数据组件 ==================== + * + * 用途: + * - 存储单个技能的完整数据 + * - 包含技能配置、状态、目标等信息 + * - 可被技能系统读取和修改 + */ +@ecs.register('SkillData') +export class SkillDataComp extends ecs.Comp { + /** 技能唯一ID */ + skillId: number = 0; + + /** 技能名称 */ + name: string = ""; + + /** 技能等级 */ + level: number = 1; + + /** 技能类型 */ + type: number = 0; + + /** 消耗MP */ + cost: number = 0; + + /** 冷却时间 */ + cooldown: number = 0; + + /** 当前冷却剩余时间 */ + currentCooldown: number = 0; + + /** 伤害值 */ + damage: number = 0; + + /** 技能范围 */ + range: number = 0; + + /** 施法时间 */ + castTime: number = 0; + + /** 技能描述 */ + description: string = ""; + + /** 是否已解锁 */ + unlocked: boolean = false; + + /** 技能图标路径 */ + iconPath: string = ""; + + /** 额外属性数据 */ + extraData: any = null; + + reset() { + this.skillId = 0; + this.name = ""; + this.level = 1; + this.type = 0; + this.cost = 0; + this.cooldown = 0; + this.currentCooldown = 0; + this.damage = 0; + this.range = 0; + this.castTime = 0; + this.description = ""; + this.unlocked = false; + this.iconPath = ""; + this.extraData = null; + } + + /** + * 检查技能是否可以施放 + */ + canCast(currentMP: number): boolean { + return this.unlocked && + this.currentCooldown <= 0 && + currentMP >= this.cost; + } + + /** + * 更新冷却时间 + */ + updateCooldown(deltaTime: number): void { + if (this.currentCooldown > 0) { + this.currentCooldown = Math.max(0, this.currentCooldown - deltaTime); + } + } + + /** + * 重置冷却时间 + */ + resetCooldown(): void { + this.currentCooldown = this.cooldown; + } + + /** + * 获取冷却进度 (0-1) + */ + getCooldownProgress(): number { + if (this.cooldown <= 0) return 1; + return Math.max(0, 1 - (this.currentCooldown / this.cooldown)); + } +} + diff --git a/assets/script/game/hero/SDataComp.ts.meta b/assets/script/game/hero/SDataComp.ts.meta new file mode 100644 index 00000000..07bb6726 --- /dev/null +++ b/assets/script/game/hero/SDataComp.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "5ef9c9a8-c661-44fd-93f4-fc133f734867", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/assets/script/game/hero/SMoveComp.ts b/assets/script/game/hero/SMoveComp.ts new file mode 100644 index 00000000..85d6e97d --- /dev/null +++ b/assets/script/game/hero/SMoveComp.ts @@ -0,0 +1,47 @@ +import { Vec3, v3 } 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"; +/** + * 技能移动数据组件 + * 存储技能实体的移动相关数据 + */ +@ecs.register('SMoveData') +export class SMoveDataComp extends ecs.Comp { + /** 起始位置 */ + startPos: Vec3 = v3(); + /** 目标位置 */ + targetPos: Vec3 = v3(); + /** 移动速度 */ + speed: number = 500; + /** 移动持续时间 */ + duration: number = 0; + /** 移动方向 */ + direction: Vec3 = v3(); + /** 是否自动销毁(到达目标后) */ + autoDestroy: boolean = true; + s_uuid:number=0; + reset() { + this.startPos.set(0, 0, 0); + this.targetPos.set(0, 0, 0); + this.speed = 500; + this.duration = 0; + this.direction.set(0, 0, 0); + this.autoDestroy = true; + } +} + +// /** 业务层业务逻辑处理对象 */ +// export class SMoveSystem extends ecs.ComblockSystem implements ecs.IEntityEnterSystem { +// filter(): ecs.IMatcher { +// return ecs.allOf(SMoveDataComp); +// } +// entityEnter(e: ecs.Entity): void { +// // 注:自定义业务逻辑 +// let s_uuid=e.get(SMoveDataComp).s_uuid +// let SConf=SkillSet[s_uuid] + +// e.remove(SMoveDataComp); +// } + +// } \ No newline at end of file diff --git a/assets/script/game/hero/SMoveComp.ts.meta b/assets/script/game/hero/SMoveComp.ts.meta new file mode 100644 index 00000000..081b16ad --- /dev/null +++ b/assets/script/game/hero/SMoveComp.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "d0c2d505-f2c3-4cf3-82f7-490f9d14e096", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/assets/script/game/hero/STagComps.ts b/assets/script/game/hero/STagComps.ts new file mode 100644 index 00000000..d4dc4ea9 --- /dev/null +++ b/assets/script/game/hero/STagComps.ts @@ -0,0 +1,53 @@ +import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS"; +import { Vec3 } from "cc"; + +/** + * ==================== 施法请求标记组件 ==================== + * + * 用途: + * - 标记角色想要施放某个技能 + * - 由外部(如AI系统、玩家输入)添加 + * - 施法系统处理后自动移除 + */ +@ecs.register('CastSkillRequest') +export class CastSkillRequestComp extends ecs.Comp { + /** 技能索引(在 HeroSkillsComp.skills 中的位置) */ + skillIndex: number = 0; + + /** 目标位置数组(由请求者提供) */ + targetPositions: Vec3[] = []; + + reset() { + this.skillIndex = 0; + this.targetPositions = []; + } +} +/** + * ==================== 技能命中标记组件 ==================== + * + * 用途: + * - 标记技能已命中目标 + * - 记录命中信息 + * - 伤害系统处理后移除 + */ +@ecs.register('SkillHit') +export class SkillHitComp extends ecs.Comp { + /** 技能ID */ + skillId: number = 0; + + /** 命中位置 */ + hitPosition: Vec3 = new Vec3(); + + /** 伤害值 */ + damage: number = 0; + + /** 施法者实体ID */ + casterId: number = 0; + + reset() { + this.skillId = 0; + this.hitPosition.set(0, 0, 0); + this.damage = 0; + this.casterId = 0; + } +} diff --git a/assets/script/game/hero/STagComps.ts.meta b/assets/script/game/hero/STagComps.ts.meta new file mode 100644 index 00000000..e55354a8 --- /dev/null +++ b/assets/script/game/hero/STagComps.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "37d898a0-b376-4f18-8559-2acf6f4e7db7", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/assets/script/game/skill/SkillCom.ts b/assets/script/game/skill/SDataCom.ts similarity index 50% rename from assets/script/game/skill/SkillCom.ts rename to assets/script/game/skill/SDataCom.ts index 26238d95..00ffac89 100644 --- a/assets/script/game/skill/SkillCom.ts +++ b/assets/script/game/skill/SDataCom.ts @@ -1,24 +1,30 @@ import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS"; +import { BoxSet } from "../common/config/BoxSet"; /** 业务层对象 */ -@ecs.register('SkillCom') -export class SkillComComp extends ecs.Comp { +@ecs.register('SDataCom') +export class SDataCom extends ecs.Comp { /** 业务层组件移除时,重置所有数据为默认值 */ + attrs:any=null + group:BoxSet=BoxSet.HERO + s_uuid:number=0 reset() { - + this.attrs=null + this.group=0 + this.s_uuid=0 } } /** 业务层业务逻辑处理对象 */ -export class SkillComSystem extends ecs.ComblockSystem implements ecs.IEntityEnterSystem { +export class SDataComSystem extends ecs.ComblockSystem implements ecs.IEntityEnterSystem { filter(): ecs.IMatcher { - return ecs.allOf(SkillComComp); + return ecs.allOf(SDataCom); } entityEnter(e: ecs.Entity): void { // 注:自定义业务逻辑 - e.remove(SkillComComp); + e.remove(SDataCom); } } \ No newline at end of file diff --git a/assets/script/game/skill/SDataCom.ts.meta b/assets/script/game/skill/SDataCom.ts.meta new file mode 100644 index 00000000..543cc1e9 --- /dev/null +++ b/assets/script/game/skill/SDataCom.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "6ded70d5-55f5-48b5-b48b-8883b26d1169", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/assets/script/game/skill/Skill.ts b/assets/script/game/skill/Skill.ts index 00288be7..94dff141 100644 --- a/assets/script/game/skill/Skill.ts +++ b/assets/script/game/skill/Skill.ts @@ -1,34 +1,95 @@ +import { instantiate, Node, Prefab, v3, Vec3 } from "cc"; import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS"; +import { Hero } from "../hero/Hero"; +import { Monster } from "../hero/Mon"; +import { ECSEntity } from "db://oops-framework/libs/ecs/ECSEntity"; +import { SkillSet } from "../common/config/SkillSet"; +import { oops } from "db://oops-framework/core/Oops"; +import { AtkConCom } from "./AtkConCom"; +import { HeroAttrsComp } from "../hero/HeroAttrsComp"; +import { BoxSet, FacSet } from "../common/config/BoxSet"; +import { HType } from "../common/config/heroSet"; +import { SkillView } from "./SkillView"; +import { SDataCom } from "./SDataCom"; +import { Attrs } from "../common/config/HeroAttrs"; +import { SMoveDataComp } from "../hero/SMoveComp"; /** Skill 模块 */ @ecs.register(`Skill`) export class Skill extends ecs.Entity { /** ---------- 数据层 ---------- */ - // SkillModel!: SkillModelComp; + SDataCom!: SDataCom; + SMoveCom!: SMoveDataComp /** ---------- 业务层 ---------- */ // SkillBll!: SkillBllComp; /** ---------- 视图层 ---------- */ - // SkillView!: SkillViewComp; + SView!: SkillView; /** 实始添加的数据层组件 */ protected init() { - // this.addComponents(); + this.addComponents(); + this.addComponents(); + } + load(startPos: Vec3, parent: Node, uuid: number, targetPos: Vec3,casterAttrs:Attrs[]=[],scale:number=1,fac:FacSet=FacSet.MON,type:HType=HType.warrior,box_group:BoxSet=BoxSet.HERO) { + const config = SkillSet[uuid]; + if (!config) { + console.error("[Skill] 技能配置不存在:", uuid); + return; + } + + // 加载预制体 + const path = `game/skill/atk/${config.sp_name}`; + const prefab:Prefab = oops.res.get(path, Prefab); + if (!prefab) { + console.error("[Skill] 预制体加载失败:", path); + return; + } + // console.log("load skill startPos",startPos) + const node: Node = instantiate(prefab); + console.log("load skill node",node) + node.parent = parent; + // 设置节点属性 + node.setPosition(startPos); + + if(fac==FacSet.MON){ + node.scale=v3(node.scale.x*-1,1,1) + }else{ + if(type==HType.warrior){ + if(scale<0){ + node.scale=v3(node.scale.x*-1,node.scale.y,1) + } + } + } + // 添加技能组件 + const SView = node.getComponent(SkillView); // 初始化技能参数 + // 只设置必要的运行时属性,配置信息通过 SkillSet[uuid] 访问 + // 核心标识 + SView.s_uuid= uuid + SView.group= box_group + this.add(SView); + + + const sDataCom = this.get(SDataCom); + const sMoveCom = this.get(SMoveDataComp); + sMoveCom.startPos=startPos + sMoveCom.targetPos=targetPos + sMoveCom.s_uuid=uuid + sDataCom.group=box_group + sDataCom.attrs=casterAttrs + sDataCom.s_uuid=uuid + } /** 模块资源释放 */ destroy() { // 注: 自定义释放逻辑,视图层实现 ecs.IComp 接口的 ecs 组件需要手动释放 + this.remove(SDataCom); + this.remove(SkillView) super.destroy(); + } } -/** Skill 模块业务逻辑系统组件,如无业务逻辑处理可删除此对象 */ -export class EcsSkillSystem extends ecs.System { - constructor() { - super(); - // this.add(new ecs.ComblockSystem()); - } -} diff --git a/assets/script/game/skill/SkillView.ts b/assets/script/game/skill/SkillView.ts index b3d9d168..ba497a96 100644 --- a/assets/script/game/skill/SkillView.ts +++ b/assets/script/game/skill/SkillView.ts @@ -1,27 +1,105 @@ -import { _decorator } from "cc"; -import { ecs } from "../../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS"; -import { CCComp } from "../../../../../extensions/oops-plugin-framework/assets/module/common/CCComp"; +import { _decorator, Animation, Collider2D, Contact2DType, Vec3 } from "cc"; +import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS"; +import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp"; +import { HeroViewComp } from "../hero/HeroViewComp"; +import { DTType, RType, SkillSet } from "../common/config/SkillSet"; +import { BezierMove } from "../BezierMove/BezierMove"; +import { BoxSet } from "../common/config/BoxSet"; +import { SDataCom } from "./SDataCom"; +import { SMoveDataComp } from "../hero/SMoveComp"; const { ccclass, property } = _decorator; /** 视图层对象 */ @ccclass('SkillViewComp') @ecs.register('SkillView', false) -export class SkillViewComp extends CCComp { +export class SkillView extends CCComp { /** 视图层逻辑代码分离演示 */ + anim:Animation=null; + group:number=0; + SConf:any=null; + s_uuid:number=1001 start() { - // var entity = this.ent as ecs.Entity; // ecs.Entity 可转为当前模块的具体实体对象 - // this.on(ModuleEvent.Cmd, this.onHandler, this); + this.SConf = SkillSet[this.s_uuid] + this.anim=this.node.getComponent(Animation) + this.node.active = true; + let collider = this.getComponent(Collider2D); + if(collider) { + collider.group = this.group; + collider.on(Contact2DType.BEGIN_CONTACT, this.onBeginContact, this); + } + const SMove=this.ent.get(SMoveDataComp) + switch(this.SConf.RType){ + case RType.linear: + this.do_linear(SMove.startPos,SMove.targetPos) + break + case RType.bezier: + this.do_bezier(SMove.startPos,SMove.targetPos) + break + case RType.fixed: + this.do_fixedStart(SMove.startPos,SMove.targetPos) + break + case RType.fixedEnd: + this.do_fixedEnd(SMove.startPos,SMove.targetPos) + break } - /** 全局消息逻辑处理 */ - // private onHandler(event: string, args: any) { - // switch (event) { - // case ModuleEvent.Cmd: - // break; - // } - // } - + } + onBeginContact (seCol: Collider2D, oCol: Collider2D) { + // console.log(this.scale+"碰撞开始 ",seCol,oCol); + if(seCol.node.position.x-oCol.node.position.x > 100 ) return + let target = oCol.getComponent(HeroViewComp) + if(oCol.group!=this.group){ + if(target == null) return; + if (!this.SConf) return; + } + } + do_bezier(startPos:Vec3,targetPos:Vec3){ + let bm=this.node.getComponent(BezierMove) + this.node.angle +=10 + // bm.speed=700 + if(this.group==BoxSet.MONSTER) {bm.controlPointSide=-1 } + bm.rotationSmoothness=0.6 + bm.moveTo(targetPos) + } + do_linear(startPos:Vec3,targetPos:Vec3){ + let bm=this.node.getComponent(BezierMove) + let s_x=startPos.x + let s_y=startPos.y + let t_x=targetPos.x + let t_y=targetPos.y + // 设定目标x + targetPos.x = 400; + if(this.group == BoxSet.MONSTER) { + bm.controlPointSide = -1; + targetPos.x = -400; + } + // 计算斜率 + const k = (t_y - s_y) / (t_x - s_x); + // 按直线公式计算新的y + targetPos.y = k * (targetPos.x - s_x) + s_y; + bm.controlPointOffset=0 + bm.rotationSmoothness=0.6 + bm.moveTo(targetPos); + } + do_fixedEnd(startPos:Vec3,targetPos:Vec3){ + this.node.setPosition(targetPos.x > 360?300:targetPos.x,this.node.position.y,0) + this.do_anim() + } + do_fixedStart(startPos:Vec3,targetPos:Vec3){ + this.node.setPosition(startPos.x > 360?300:startPos.x,this.node.position.y,0) + this.do_anim() + } + do_anim(){ + if(this.node.getComponent(Animation)){ + let anim = this.node.getComponent(Animation); + //console.log("[SkillCom]:has anim",anim) + anim.on(Animation.EventType.FINISHED, this.onAnimationFinished, this); + } + } + onAnimationFinished(){ + + } /** 视图对象通过 ecs.Entity.remove(ModuleViewComp) 删除组件是触发组件处理自定义释放逻辑 */ reset() { this.node.destroy(); diff --git a/assets/script/game/skill/components.meta b/assets/script/game/skill/components.meta new file mode 100644 index 00000000..3fb3f369 --- /dev/null +++ b/assets/script/game/skill/components.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.2.0", + "importer": "directory", + "imported": true, + "uuid": "e8a3bd61-1102-4fb8-8eca-c795cad7ef52", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/assets/script/game/skill/systems.meta b/assets/script/game/skill/systems.meta new file mode 100644 index 00000000..5a4e1271 --- /dev/null +++ b/assets/script/game/skill/systems.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.2.0", + "importer": "directory", + "imported": true, + "uuid": "d3d7bbfc-9c24-4551-8bb5-7a40d7c271cd", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/assets/script/game/skill/快速开始.md b/assets/script/game/skill/快速开始.md deleted file mode 100644 index 0439c6de..00000000 --- a/assets/script/game/skill/快速开始.md +++ /dev/null @@ -1,189 +0,0 @@ -# 新技能系统 - 快速开始 - -## 🚀 3步开始使用 - -### **步骤 1:注册系统到 Main.ts** - -```typescript -// Main.ts -import { SkillCastSystem, SkillCDSystem, SkillAutocastSystem } from './game/hero/HSkillSystem'; - -protected async initEcsSystem() { - // 技能系统(按顺序) - oops.ecs.add(new SkillCDSystem()); // CD更新 - oops.ecs.add(new SkillAutocastSystem()); // 自动施法 - oops.ecs.add(new SkillCastSystem()); // 施法执行 -} -``` - ---- - -### **步骤 2:角色已自动拥有技能** - -✅ `Hero.ts` 和 `Mon.ts` 已自动添加 `HeroSkillsComp` -✅ 加载角色时自动初始化技能 -✅ 无需手动配置 - -```typescript -// Hero.ts - load() 方法已自动处理 -skillsComp.initSkills(hero.skills); // ✅ 已完成 -``` - ---- - -### **步骤 3:技能自动施放!** - -**无需额外代码**,系统会自动处理: - -```typescript -// 每帧自动运行: -// 1. SkillCDSystem 更新CD -// 2. SkillAutocastSystem 检测可施放技能 -// ├─ CD好了? ✅ -// ├─ MP够? ✅ -// ├─ 正在攻击? ✅ -// └─ 添加 CastSkillRequestComp 标记 -// 3. SkillCastSystem 执行施法 -// ├─ 检查条件 -// ├─ 扣除MP -// ├─ 重置CD -// ├─ 播放动画 -// └─ 创建技能实体 -``` - ---- - -## 🎮 进阶使用 - -### **手动施法(玩家点击技能按钮)** - -```typescript -// UI 按钮点击事件 -onSkillButton1Clicked() { - const skillCon = this.heroNode.getComponent(SkillConComp); - skillCon.manualCastSkill(0); // 施放第0个技能 -} - -onSkillButton2Clicked() { - const skillCon = this.heroNode.getComponent(SkillConComp); - skillCon.manualCastSkill(1); // 施放第1个技能 -} -``` - ---- - -### **强制施法(天赋触发、事件触发)** - -```typescript -// 天赋系统 -doTalentEffect(heroEntity: ecs.Entity) { - const request = heroEntity.add(CastSkillRequestComp); - request.skillIndex = 2; // 施放第2个技能 - request.targetPositions = [v3(200, 0, 0)]; -} - -// 事件触发(如复仇:受伤时施放技能) -onDamaged(heroEntity: ecs.Entity) { - const request = heroEntity.add(CastSkillRequestComp); - request.skillIndex = 0; - request.targetPositions = this.selectEnemies(); -} -``` - ---- - -### **查询技能状态** - -```typescript -const hero = ecs.getEntity(Hero); -const skillsComp = hero.get(HeroSkillsComp); - -// 检查技能是否就绪 -if (skillsComp.canCast(0, heroModel.mp)) { - console.log("技能1可以施放!"); -} - -// 获取所有就绪技能 -const readySkills = skillsComp.getReadySkills(heroModel.mp); -console.log(`可施放技能数量: ${readySkills.length}`); - -// 获取技能CD -const skill0 = skillsComp.getSkill(0); -console.log(`技能1剩余CD: ${skill0.cd.toFixed(2)}秒`); -``` - ---- - -## 🔧 禁用自动施法 - -如果只想手动控制技能,注释掉自动施法系统: - -```typescript -protected async initEcsSystem() { - oops.ecs.add(new SkillCDSystem()); - // oops.ecs.add(new SkillAutocastSystem()); // ❌ 禁用自动施法 - oops.ecs.add(new SkillCastSystem()); -} -``` - ---- - -## 🎯 与原系统对比 - -| 指标 | 旧系统(SkillConComp.update) | 新系统(HSkillSystem) | -|------|------------------------------|----------------------| -| **职责** | CD更新 + 施法判定 + 执行 | 3个独立系统 | -| **扩展性** | 低(所有逻辑耦合) | 高(系统独立) | -| **代码位置** | 分散在 View 层 | 集中在数据/业务层 | -| **测试** | 难(依赖 View) | 易(独立系统) | -| **手动施法** | 需额外实现 | 标记组件即可 | -| **ECS 规范** | 不符合 | ✅ 完全符合 | - ---- - -## 📊 架构图 - -``` -玩家实体(Hero/Monster) -├── HeroAttrsComp(属性:hp, mp, 状态) -├── HeroSkillsComp(技能:skills[], CD管理)⭐ 新增 -├── HeroViewComp(视图:动画、UI) -└── BattleMoveComp(移动) - -技能系统(HSkillSystem) -├── SkillCDSystem ─────────→ HeroSkillsComp -│ └─ 每帧更新CD -├── SkillAutocastSystem ───→ HeroSkillsComp + HeroAttrsComp -│ └─ AI决策施法 -└── SkillCastSystem ───────→ 监听 CastSkillRequestComp - ├─ 检查条件 - ├─ 扣除MP - ├─ 重置CD - └─ 创建技能实体 -``` - ---- - -## ✅ 验证清单 - -运行游戏后检查: - -- [ ] 角色加载后拥有技能(查看 HeroSkillsComp.skills) -- [ ] 技能CD自动递减(观察 skill.cd 变化) -- [ ] 攻击时自动施放技能(观察技能特效) -- [ ] 施放后MP减少、CD重置 -- [ ] 控制状态(眩晕/冰冻)时不施放技能 - ---- - -## 🎉 完成! - -**新技能系统已完全集成到项目中!** - -✅ 无需修改原有战斗逻辑 -✅ 无需修改技能实体(复用 SkillEnt) -✅ 自动与战斗系统集成 -✅ 支持多种施法方式 - -**开始享受清晰的架构吧!** 🚀 - diff --git a/assets/script/game/skill/快速开始.md.meta b/assets/script/game/skill/快速开始.md.meta deleted file mode 100644 index 604fa4ac..00000000 --- a/assets/script/game/skill/快速开始.md.meta +++ /dev/null @@ -1,11 +0,0 @@ -{ - "ver": "1.0.1", - "importer": "text", - "imported": true, - "uuid": "a851deeb-51c4-4c8d-990f-0d460fe8848b", - "files": [ - ".json" - ], - "subMetas": {}, - "userData": {} -} diff --git a/assets/script/game/skill/新技能系统使用说明.md b/assets/script/game/skill/新技能系统使用说明.md deleted file mode 100644 index 684716ad..00000000 --- a/assets/script/game/skill/新技能系统使用说明.md +++ /dev/null @@ -1,296 +0,0 @@ -# 新技能系统使用说明 - -## 📊 架构概览 - -基于 **oops-framework ECS** 架构设计的完整施法系统。 - ---- - -## 🗂️ 文件结构 - -``` -技能系统文件: -├── HeroSkills.ts # 数据层:技能槽位数据 -├── HSkillSystem.ts # 业务层:3个系统 -│ ├── SkillCastSystem # 施法系统 -│ ├── SkillCDSystem # CD更新系统 -│ └── SkillAutocastSystem # 自动施法系统(AI) -└── SkillEnt.ts # 技能实体(复用现有) -``` - ---- - -## 🎯 设计理念 - -### **数据层(HeroSkillsComp)** - -**职责**:存储角色拥有的技能列表和CD状态 - -```typescript -@ecs.register('HeroSkills') -export class HeroSkillsComp extends ecs.Comp { - skills: SkillSlot[] = []; // 技能槽位数组 - - // 数据方法 - initSkills(skillIds: number[]) { } // 初始化技能 - canCast(index, mp): boolean { } // 检查可施放 - resetCD(index) { } // 重置CD - updateCDs(dt) { } // 更新CD - getReadySkills(mp): number[] { } // 获取就绪技能 -} -``` - -**技能槽位数据**: -```typescript -interface SkillSlot { - uuid: number; // 技能配置ID - cd: number; // 当前CD(递减) - cd_max: number; // 最大CD - cost: number; // MP消耗 - level: number; // 技能等级 -} -``` - ---- - -### **业务层(3个系统)** - -#### **1. SkillCastSystem(施法系统)⭐** - -**职责**:监听施法请求,执行施法 - -```typescript -export class SkillCastSystem extends ecs.ComblockSystem - implements ecs.IEntityEnterSystem { - - // 筛选:拥有技能 + 请求标记的实体 - filter(): ecs.IMatcher { - return ecs.allOf(HeroSkillsComp, HeroAttrsComp, CastSkillRequestComp); - } - - // 处理施法请求 - entityEnter(e: ecs.Entity): void { - // 1. 检查施法条件(CD、MP、状态) - // 2. 扣除MP - // 3. 重置CD - // 4. 播放动画 - // 5. 创建技能实体 - // 6. 移除请求标记 - } -} -``` - ---- - -#### **2. SkillCDSystem(CD更新系统)** - -**职责**:每帧自动更新所有技能CD - -```typescript -export class SkillCDSystem extends ecs.ComblockSystem - implements ecs.ISystemUpdate { - - filter(): ecs.IMatcher { - return ecs.allOf(HeroSkillsComp); - } - - update(e: ecs.Entity): void { - const skillsData = e.get(HeroSkillsComp); - skillsData.updateCDs(this.dt); // 自动递减CD - } -} -``` - ---- - -#### **3. SkillAutocastSystem(自动施法系统)** - -**职责**:AI自动选择和施放技能 - -```typescript -export class SkillAutocastSystem extends ecs.ComblockSystem - implements ecs.ISystemUpdate { - - filter(): ecs.IMatcher { - return ecs.allOf(HeroSkillsComp, HeroAttrsComp, HeroViewComp); - } - - update(e: ecs.Entity): void { - // 1. 检查角色状态 - // 2. 获取可施放技能 - // 3. 选择目标 - // 4. 添加施法请求标记 ← 触发 SkillCastSystem - } -} -``` - ---- - -## 🔄 数据流程 - -``` -方式1:自动施法(AI) - SkillAutocastSystem.update() - ├─ 检测可施放技能 - ├─ 选择目标 - └─ 添加 CastSkillRequestComp 标记 - ↓ - SkillCastSystem.entityEnter() ← 自动触发 - ├─ 检查施法条件 - ├─ 扣除MP - ├─ 重置CD - ├─ 播放施法动画 - ├─ 创建 SkillEnt - └─ 移除 CastSkillRequestComp - -方式2:手动施法(玩家点击) - UI.onClick() - └─ skillConComp.manualCastSkill(index) - └─ 添加 CastSkillRequestComp 标记 - ↓ - (后续流程同方式1) - -方式3:强制施法(天赋、事件触发) - TalentSystem - └─ heroEntity.add(CastSkillRequestComp) - ↓ - (后续流程同方式1) -``` - ---- - -## 🚀 使用示例 - -### **示例 1:初始化角色技能** - -```typescript -// Hero.ts - load() 方法中 -const skillsComp = this.get(HeroSkillsComp); -skillsComp.initSkills(hero.skills); // [6001, 6005, 6010] -``` - ---- - -### **示例 2:自动施法(默认)** - -**无需额外代码**,`SkillAutocastSystem` 会自动处理: - -```typescript -// 每帧自动检测: -// - 是否有可施放技能? -// - CD好了?MP够? -// - 正在攻击? -// ✅ 自动添加施法请求标记 → 触发施法 -``` - ---- - -### **示例 3:手动施法(玩家点击)** - -```typescript -// UI 按钮点击 -onSkillButton1Click() { - const skillCon = this.heroNode.getComponent(SkillConComp); - skillCon.manualCastSkill(0); // 施放第0个技能 -} -``` - ---- - -### **示例 4:强制施法(天赋触发)** - -```typescript -// 天赋系统 -doTalentEffect(heroEntity: ecs.Entity) { - // ✅ 添加施法请求标记 - const request = heroEntity.add(CastSkillRequestComp); - request.skillIndex = 1; // 施放第1个技能 - request.targetPositions = [v3(100, 0, 0)]; -} -``` - ---- - -## ⚙️ 系统注册(Main.ts) - -```typescript -protected async initEcsSystem() { - // ✅ 注册技能系统(按顺序) - oops.ecs.add(new SkillCDSystem()); // 1. CD更新 - oops.ecs.add(new SkillAutocastSystem()); // 2. 自动施法AI - oops.ecs.add(new SkillCastSystem()); // 3. 施法执行 - - // 战斗系统 - oops.ecs.add(new HeroAtkSystem()); - oops.ecs.add(new HeroAttrSystem()); -} -``` - ---- - -## 📋 迁移清单 - -### ✅ **已完成** - -| 任务 | 状态 | 说明 | -|------|------|------| -| 创建 HeroSkillsComp | ✅ | 技能数据组件 | -| 创建 SkillCastSystem | ✅ | 施法执行系统 | -| 创建 SkillCDSystem | ✅ | CD更新系统 | -| 创建 SkillAutocastSystem | ✅ | 自动施法系统 | -| 更新 Hero.ts | ✅ | 添加 HeroSkillsComp | -| 更新 Mon.ts | ✅ | 添加 HeroSkillsComp | -| 从 HeroAttrsComp 移除 skills | ✅ | 数据迁移完成 | -| 更新 SkillConComp | ✅ | 使用新系统 | - ---- - -## 🎯 与战斗系统集成 - -**技能系统只负责"施法",伤害结算由战斗系统处理** - -``` -SkillCastSystem(施法) - ↓ -创建 SkillEnt(技能实体) - ↓ -SkillEnt 碰撞检测 - ↓ -AtkConCom.single_damage() - ↓ -HeroAtkSystem.doAttack() ← 统一战斗系统 - ├─ 暴击判定 - ├─ 闪避判定 - ├─ 护盾吸收 - ├─ 修改数据 - └─ 触发视图 -``` - ---- - -## ✅ 优点总结 - -| 优点 | 说明 | -|------|------| -| **数据分离** | 技能数据独立组件,不污染 HeroAttrsComp | -| **标记驱动** | 使用 CastSkillRequestComp 标记组件,符合 ECS | -| **职责清晰** | CD更新、施法检查、执行分离成独立系统 | -| **易于扩展** | 添加新施法方式(手动/自动/强制)无需改动核心 | -| **易于测试** | 可单独测试每个系统 | -| **代码复用** | 手动/自动/强制施法共用同一套逻辑 | - ---- - -## 🎉 总结 - -**完整的、规范的、基于 oops-framework 的技能施法系统!** - -✅ 符合 ECS 架构(数据/业务/视图分离) -✅ 使用标记组件驱动,完全解耦 -✅ 复用现有 SkillEnt,无需重写 -✅ 与战斗系统完美集成 -✅ 支持自动/手动/强制多种施法方式 -✅ 代码清晰,注释详细 - -**可直接投入生产使用!** 🚀 - diff --git a/assets/script/game/skill/新技能系统使用说明.md.meta b/assets/script/game/skill/新技能系统使用说明.md.meta deleted file mode 100644 index a8e6de63..00000000 --- a/assets/script/game/skill/新技能系统使用说明.md.meta +++ /dev/null @@ -1,11 +0,0 @@ -{ - "ver": "1.0.1", - "importer": "text", - "imported": true, - "uuid": "2df12e82-84f3-40f1-a838-145f935d4cc1", - "files": [ - ".json" - ], - "subMetas": {}, - "userData": {} -}