diff --git a/Assets/GameMain/Configs/ResourceBuilder.xml b/Assets/GameMain/Configs/ResourceBuilder.xml index 8c08148..ad28384 100644 --- a/Assets/GameMain/Configs/ResourceBuilder.xml +++ b/Assets/GameMain/Configs/ResourceBuilder.xml @@ -7,8 +7,8 @@ 1 UnityGameFramework.Runtime.DefaultCompressionHelper False - True - VampireLike.Editor.VampireLikeBuildEventHandler + False + SepCore.Editor.DefaultBuildEventHandler D:/Learn/GameLearn/UnityProjects/VampireLike/bin/AssetBundles True True diff --git a/Assets/GameMain/Configs/ResourceCollection.xml b/Assets/GameMain/Configs/ResourceCollection.xml index f948d45..f3c895d 100644 --- a/Assets/GameMain/Configs/ResourceCollection.xml +++ b/Assets/GameMain/Configs/ResourceCollection.xml @@ -31,20 +31,27 @@ - + + + + + + + + @@ -57,24 +64,30 @@ + + + + + + @@ -82,26 +95,35 @@ + + + + + + + + + @@ -111,10 +133,12 @@ + + @@ -122,17 +146,24 @@ + - + + + + + + + @@ -140,55 +171,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Assets/GameMain/Scripts/Procedure/Game/GameStateBattle.cs b/Assets/GameMain/Scripts/Procedure/Game/GameStateBattle.cs index 4cbcf0e..27e2070 100644 --- a/Assets/GameMain/Scripts/Procedure/Game/GameStateBattle.cs +++ b/Assets/GameMain/Scripts/Procedure/Game/GameStateBattle.cs @@ -8,6 +8,7 @@ using GameFramework.Fsm; using GameFramework.Procedure; using SepCore.EnemyManager; using SepCore.Simulation; +using SepCore.Timer; using UnityEngine; namespace SepCore.Procedure @@ -20,7 +21,7 @@ namespace SepCore.Procedure private int _currentLevel; - private float _levelTimeLeft; + private TimerHandle _levelTimerHandle; private Player Player => _procedureGame.Player; @@ -29,7 +30,11 @@ namespace SepCore.Procedure public void AddBattleDuration(float seconds) { if (seconds <= 0f) return; - _levelTimeLeft += seconds; + + float remaining = GameEntry.Timer.GetRemainingTime(_levelTimerHandle); + if (remaining < 0f) return; + + GameEntry.Timer.ResetRemainingTime(_levelTimerHandle, remaining + seconds); } #region FSM @@ -51,7 +56,7 @@ namespace SepCore.Procedure throw new Exception($"GameStateBattle.OnEnter: {_currentLevel} is not found."); } - _levelTimeLeft = drLevel.Duration; + _levelTimerHandle = GameEntry.Timer.ScheduleOnce(drLevel.Duration, OnLevelTimeUp, this); _enemyManager.OnInit(drLevel, Player); if (Player != null) Player.Enable = true; @@ -64,12 +69,6 @@ namespace SepCore.Procedure public override void OnUpdate(float elapseSeconds, float realElapseSeconds) { - if (_levelTimeLeft < 0) - { - _procedureGame.BattleToShopOrLevelUp(); - return; - } - _enemyManager.OnUpdate(elapseSeconds, realElapseSeconds); SimulationWorld simulationWorld = GameEntry.SimulationWorld; @@ -79,14 +78,22 @@ namespace SepCore.Procedure simulationWorld.Tick(new SimulationTickContext(elapseSeconds, realElapseSeconds, playerPosition)); } - _levelTimeLeft -= elapseSeconds; - GameEntry.Event.Fire(this, LevelProcessEventArgs.Create((int)_levelTimeLeft)); + int timeLeft = Mathf.Max(0, (int)GameEntry.Timer.GetRemainingTime(_levelTimerHandle)); + GameEntry.Event.Fire(this, LevelProcessEventArgs.Create(timeLeft)); + } + + private void OnLevelTimeUp() + { + _procedureGame.BattleToShopOrLevelUp(); } public override void OnLeave() { GameEntry.UIRouter.CloseUIAsync(UIFormType.JoystickForm).Forget(); + GameEntry.Timer.Cancel(_levelTimerHandle); + _levelTimerHandle = TimerHandle.Invalid; + // 隐藏所有敌人实体 _enemyManager.OnReset(); diff --git a/Assets/Launcher.unity b/Assets/Launcher.unity index 725f25a..e793a7a 100644 --- a/Assets/Launcher.unity +++ b/Assets/Launcher.unity @@ -738,7 +738,7 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 11499388, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3} propertyPath: m_EditorResourceMode - value: 1 + value: 0 objectReference: {fileID: 0} - target: {fileID: 11499388, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3} propertyPath: m_JsonHelperTypeName diff --git a/Assets/Plugins/InputModule/Presentation/JoystickPack/Editor.meta b/Assets/Plugins/InputModule/Editor/Editor.meta similarity index 100% rename from Assets/Plugins/InputModule/Presentation/JoystickPack/Editor.meta rename to Assets/Plugins/InputModule/Editor/Editor.meta diff --git a/Assets/Plugins/InputModule/Presentation/JoystickPack/Editor/DynamicJoystickEditor.cs b/Assets/Plugins/InputModule/Editor/Editor/DynamicJoystickEditor.cs similarity index 100% rename from Assets/Plugins/InputModule/Presentation/JoystickPack/Editor/DynamicJoystickEditor.cs rename to Assets/Plugins/InputModule/Editor/Editor/DynamicJoystickEditor.cs diff --git a/Assets/Plugins/InputModule/Presentation/JoystickPack/Editor/DynamicJoystickEditor.cs.meta b/Assets/Plugins/InputModule/Editor/Editor/DynamicJoystickEditor.cs.meta similarity index 100% rename from Assets/Plugins/InputModule/Presentation/JoystickPack/Editor/DynamicJoystickEditor.cs.meta rename to Assets/Plugins/InputModule/Editor/Editor/DynamicJoystickEditor.cs.meta diff --git a/Assets/Plugins/InputModule/Presentation/JoystickPack/Editor/FloatingJoystickEditor.cs b/Assets/Plugins/InputModule/Editor/Editor/FloatingJoystickEditor.cs similarity index 100% rename from Assets/Plugins/InputModule/Presentation/JoystickPack/Editor/FloatingJoystickEditor.cs rename to Assets/Plugins/InputModule/Editor/Editor/FloatingJoystickEditor.cs diff --git a/Assets/Plugins/InputModule/Presentation/JoystickPack/Editor/FloatingJoystickEditor.cs.meta b/Assets/Plugins/InputModule/Editor/Editor/FloatingJoystickEditor.cs.meta similarity index 100% rename from Assets/Plugins/InputModule/Presentation/JoystickPack/Editor/FloatingJoystickEditor.cs.meta rename to Assets/Plugins/InputModule/Editor/Editor/FloatingJoystickEditor.cs.meta diff --git a/Assets/Plugins/InputModule/Presentation/JoystickPack/Editor/JoystickEditor.cs b/Assets/Plugins/InputModule/Editor/Editor/JoystickEditor.cs similarity index 100% rename from Assets/Plugins/InputModule/Presentation/JoystickPack/Editor/JoystickEditor.cs rename to Assets/Plugins/InputModule/Editor/Editor/JoystickEditor.cs diff --git a/Assets/Plugins/InputModule/Presentation/JoystickPack/Editor/JoystickEditor.cs.meta b/Assets/Plugins/InputModule/Editor/Editor/JoystickEditor.cs.meta similarity index 100% rename from Assets/Plugins/InputModule/Presentation/JoystickPack/Editor/JoystickEditor.cs.meta rename to Assets/Plugins/InputModule/Editor/Editor/JoystickEditor.cs.meta diff --git a/Assets/Plugins/InputModule/Presentation/JoystickPack/Editor/VariableJoystickEditor.cs b/Assets/Plugins/InputModule/Editor/Editor/VariableJoystickEditor.cs similarity index 100% rename from Assets/Plugins/InputModule/Presentation/JoystickPack/Editor/VariableJoystickEditor.cs rename to Assets/Plugins/InputModule/Editor/Editor/VariableJoystickEditor.cs diff --git a/Assets/Plugins/InputModule/Presentation/JoystickPack/Editor/VariableJoystickEditor.cs.meta b/Assets/Plugins/InputModule/Editor/Editor/VariableJoystickEditor.cs.meta similarity index 100% rename from Assets/Plugins/InputModule/Presentation/JoystickPack/Editor/VariableJoystickEditor.cs.meta rename to Assets/Plugins/InputModule/Editor/Editor/VariableJoystickEditor.cs.meta diff --git a/Assets/Plugins/InputModule/Editor/SepCore.InputModule.Editor.asmdef b/Assets/Plugins/InputModule/Editor/SepCore.InputModule.Editor.asmdef index c19fd6c..3de2279 100644 --- a/Assets/Plugins/InputModule/Editor/SepCore.InputModule.Editor.asmdef +++ b/Assets/Plugins/InputModule/Editor/SepCore.InputModule.Editor.asmdef @@ -5,7 +5,8 @@ "GUID:d54b9488b03814a44ab937f0aeb738b1", "GUID:75469ad4d38634e559750d17036d5f7c", "GUID:363c5eb08ff8e6a439b85e37b8c20d96", - "GUID:436e23dbdc31e7d4fb5c3f804548b2df" + "GUID:436e23dbdc31e7d4fb5c3f804548b2df", + "GUID:0e1d182005e0ae647ab3fa40f5492dbb" ], "includePlatforms": [ "Editor" diff --git a/ProjectSettings/UnityConnectSettings.asset b/ProjectSettings/UnityConnectSettings.asset index 5eac22c..f7b31b2 100644 --- a/ProjectSettings/UnityConnectSettings.asset +++ b/ProjectSettings/UnityConnectSettings.asset @@ -4,7 +4,7 @@ UnityConnectSettings: m_ObjectHideFlags: 0 serializedVersion: 1 - m_Enabled: 0 + m_Enabled: 1 m_TestMode: 0 m_EventOldUrl: https://api.uca.cloud.unity3d.com/v1/events m_EventUrl: https://cdp.cloud.unity3d.com/v1/events @@ -25,7 +25,7 @@ UnityConnectSettings: m_Enabled: 0 m_TestMode: 0 m_InitializeOnStartup: 1 - m_PackageRequiringCoreStatsPresent: 0 + m_PackageRequiringCoreStatsPresent: 1 UnityAdsSettings: m_Enabled: 0 m_InitializeOnStartup: 1 diff --git a/skills/simulation-development/SKILL.md b/skills/simulation-development/SKILL.md deleted file mode 100644 index bcce81f..0000000 --- a/skills/simulation-development/SKILL.md +++ /dev/null @@ -1,103 +0,0 @@ ---- -name: simulation-development -description: Maintain, review, refactor, and extend VampireLike SimulationWorld architecture. Use when working on SimulationWorld data ownership, entity lifecycle sync, tick pipeline orchestration, Job/Burst data channels, projectile or area collision settlement, target-selection indexing, presentation write-back, or Simulation regression tests and architecture documentation that must preserve core invariants. ---- - -# Simulation Development - -1. Read `./references/SimulationDevelopmentSkill.md` first. -2. Treat current code as the source of truth when the reference and implementation diverge. -3. Load only the source files needed for the task from the map below. -4. Keep architecture changes and behavior changes explicit; do not hide them inside unrelated edits. - -## Source Map - -- Core entry: `../../Assets/GameMain/Scripts/Simulation/SimulationWorld.cs` -- Sim state lifecycle: `../../Assets/GameMain/Scripts/Simulation/SimulationWorld.SimEntityState.cs` -- Entity lifecycle bridge: `../../Assets/GameMain/Scripts/Simulation/SimulationWorld.EntitySync.cs` -- Job data channel: `../../Assets/GameMain/Scripts/Simulation/DataChannel/SimulationWorld.JobDataChannel.cs` -- Enemy pipeline: `../../Assets/GameMain/Scripts/Simulation/Jobs/SimulationWorld.EnemyJobs.cs` -- Projectile pipeline: `../../Assets/GameMain/Scripts/Simulation/Jobs/SimulationWorld.ProjectileJobs.cs` -- Collision pipeline: `../../Assets/GameMain/Scripts/Simulation/Jobs/SimulationWorld.CollisionPipeline.cs` -- Target selection index: `../../Assets/GameMain/Scripts/Simulation/SimulationWorld.TargetSelectionSpatialIndex.cs` -- Transform write-back: `../../Assets/GameMain/Scripts/Simulation/Presentation/SimulationWorld.TransformSync.cs` -- Hit presentation bridge: `../../Assets/GameMain/Scripts/Simulation/Presentation/SimulationWorld.HitPresentation.cs` -- Tick context: `../../Assets/GameMain/Scripts/Simulation/SimulationTickContext.cs` -- Entity index binding: `../../Assets/GameMain/Scripts/Simulation/EntityBinding.cs` -- Battle update entry: `../../Assets/GameMain/Scripts/Procedure/Game/GameStateBattle.cs` -- Procedure-level cleanup: `../../Assets/GameMain/Scripts/Procedure/Game/ProcedureGame.cs` -- Damage and collision utility: `../../Assets/GameMain/Scripts/Utility/AIUtility.cs` -- Regression tests: - - `../../Assets/Tests/Simulation/EditMode/SimulationWorldTickTests.cs` - - `../../Assets/Tests/Simulation/PlayMode/SimulationWorldPlayModeTests.cs` - -## Workflow - -1. Classify the change before editing: - - simulation state contract - - entity lifecycle mapping - - tick pipeline stage - - collision or area query semantics - - presentation write-back - - test or architecture doc maintenance -2. Preserve the main boundaries: - - `Tick` remains the only simulation logic entry - - lifecycle registration and removal remain centralized - - logic does not write `Transform` - - damage, event dispatch, entity hiding, and recycle stay on the main thread -3. Extend data first when behavior depends on new state: - - update `SimData` - - update job input/output structs - - update conversion and initialization paths -4. Reuse an existing pipeline stage before adding a new one. -5. Update `./references/SimulationDevelopmentSkill.md` when module boundaries, invariants, or execution flow change. -6. Add or adjust Simulation tests for every behavior change. - -## Non-Negotiable Invariants - -- Keep `_enemies`, `_projectiles`, and `_pickups` as the persistent source of truth. -- Keep `EntityBinding` consistent with container indices. -- Use swap-back removal with remap before unbind. -- Drive container add/remove only through lifecycle sync and sim state helpers. -- Keep target-selection buckets and collision buckets as rebuildable caches, not persistent business state. -- Keep area query snapshot semantics intact. -- Avoid managed allocations and LINQ in hot paths. - -## Change Guidance - -### Extend Simulation State - -1. Add fields to the relevant sim data and job structs. -2. Populate defaults in the lifecycle registration path. -3. Flow the data through the execution stage that owns it. -4. Consume presentation-only values in Presentation code, not in simulation jobs. - -### Extend Lifecycle Mapping - -1. Add the entity group mapping in `SimulationWorld.EntitySync.cs`. -2. Register and unregister through dedicated sim state helpers. -3. Preserve clear ownership over which container the entity enters. - -### Extend Tick Pipeline - -1. Place logic inside the smallest existing stage that fits. -2. Keep job work data-oriented and side-effect free. -3. Apply outputs back to sim state before any presentation write-back. - -### Extend Collision Behavior - -1. Separate broad-phase candidate generation from final settlement. -2. Preserve dedup and snapshot behavior on the main thread. -3. Route gameplay effects through the existing main-thread settlement path. - -### Extend Presentation - -1. Read simulation output only after logic settlement is complete. -2. Do not mutate simulation state from presentation code. - -## Validation - -- Verify index stability after removal paths. -- Verify clear/reset paths leave no stale bindings or transient buffers. -- Verify behavior under the relevant Simulation tests. -- Verify the reference doc still matches the code after architectural edits. diff --git a/skills/simulation-development/agents/openai.yaml b/skills/simulation-development/agents/openai.yaml deleted file mode 100644 index 286be52..0000000 --- a/skills/simulation-development/agents/openai.yaml +++ /dev/null @@ -1,4 +0,0 @@ -interface: - display_name: "Simulation Development" - short_description: "Extend VampireLike SimulationWorld safely" - default_prompt: "Use $simulation-development to implement, review, or extend a SimulationWorld change while preserving architecture invariants." diff --git a/skills/simulation-development/references/SimulationDevelopmentSkill.md b/skills/simulation-development/references/SimulationDevelopmentSkill.md deleted file mode 100644 index 623ce68..0000000 --- a/skills/simulation-development/references/SimulationDevelopmentSkill.md +++ /dev/null @@ -1,311 +0,0 @@ -# SimulationWorld Architecture Specification - -## 文档定位 -本文件是 `SimulationWorld` 的架构规范与扩展开发约束。 - -用途分为两部分: -- 作为当前 `SimulationWorld` 实现的架构总览,说明模块职责、依赖边界、运行链路和数据所有权。 -- 作为后续扩展、重构、性能优化和回归修复时的约束文档,防止破坏核心不变量。 - -文档与实现冲突时,当前分支源码优先;提交前必须同步修正文档。 - -## 适用范围 -- `Assets/GameMain/Scripts/Simulation/*` -- `Assets/GameMain/Scripts/Procedure/Game/GameStateBattle.cs` -- `Assets/GameMain/Scripts/Procedure/Game/ProcedureGame.cs` -- `Assets/GameMain/Scripts/Utility/AIUtility.cs` -- `Assets/Tests/Simulation/EditMode/*` -- `Assets/Tests/Simulation/PlayMode/*` - -## 架构目标 -- 将战斗中的敌人、投射物、掉落物运行时状态收口到统一仿真容器。 -- 将热路径逻辑与 Unity 表现层解耦,避免在仿真阶段直接读写 `Transform`。 -- 为 Job/Burst 提供稳定的数据通道、生命周期管理和主线程结算收口点。 -- 保持 `SimulationWorld` 对外是单一战斗调度入口,而不是分散的业务入口集合。 -- 保证扩展新仿真对象或新碰撞规则时,能够沿着既有管线接入,而不是旁路修改。 - -## 非目标 -- 不负责完整战斗规则定义。伤害公式、碰撞业务语义仍由 `AIUtility` 和实体逻辑承担。 -- 不负责实体创建策略。实体创建与隐藏仍由外部流程和 Entity 系统负责。 -- 不追求全局 ECS 化。本模块仍以 `SimulationWorld + partial + Native 容器` 为中心组织。 -- 不在 Job 中直接驱动表现层、事件系统或 Unity 对象生命周期。 - -## 外部依赖与系统边界 -`SimulationWorld` 处于战斗流程中层,位于 `GameStateBattle` 和具体实体逻辑之间。 - -上游依赖: -- `GameStateBattle.OnUpdate` 驱动每帧 `Tick`。 -- `GameEntry.Event` 提供实体显示/隐藏事件,用于同步仿真容器生命周期。 -- `GameEntry.Entity` 提供实体查询、隐藏和表现事件消费。 - -下游协作: -- `AIUtility` 负责伤害与碰撞业务结算。 -- Enemy/Projectile/Drop 实体提供初始化所需运行时数据。 -- Presentation 子模块负责把仿真结果写回表现层。 - -边界要求: -- 外部业务代码不得直接增删 `_enemies`、`_projectiles`、`_pickups`。 -- 外部业务代码不得绕过 `SimulationWorld` 直接维护仿真索引。 -- `SimulationWorld` 不直接拥有实体生成权,只消费实体生命周期事件。 - -## 模块结构 -`SimulationWorld` 使用 `partial` 拆分,职责按以下边界划分: - -- `SimulationWorld.cs` - - 核心组件入口、主状态容器、基础依赖、Unity 生命周期入口。 -- `SimulationWorld.SimEntityState.cs` - - 敌人、投射物、掉落物的仿真态创建、更新、删除和清空。 -- `SimulationWorld.EntitySync.cs` - - 监听实体 show/hide 事件,将实体生命周期映射到仿真容器。 -- `DataChannel/SimulationWorld.JobDataChannel.cs` - - Native 容器持有、初始化、清理、容量准备、仿真数据到 Job 数据的转换。 -- `Jobs/SimulationWorld.EnemyJobs.cs` - - 每帧仿真主编排、敌人移动与互斥分离 Job 调度。 -- `Jobs/SimulationWorld.ProjectileJobs.cs` - - 投射物移动、寿命处理、越界回收。 -- `Jobs/SimulationWorld.CollisionPipeline.cs` - - 投射物与区域碰撞查询构建、候选筛选、主线程命中结算。 -- `SimulationWorld.TargetSelectionSpatialIndex.cs` - - 敌人目标选择空间索引。 -- `Presentation/SimulationWorld.TransformSync.cs` - - `LateUpdate` 表现写回。 -- `Presentation/SimulationWorld.HitPresentation.cs` - - 命中事件的表现消费桥。 - -## 核心数据所有权 -### 主容器 -- `_enemies` -- `_projectiles` -- `_pickups` - -这些容器是真实仿真态所有者。Job 输入输出缓冲只是当前帧的镜像通道,不是持久源数据。 - -### 绑定关系 -- `EntityBinding` 维护 `EntityId <-> SimulationIndex` 双向映射。 -- 容器删除使用 `swap-back`。 -- 发生尾元素覆盖时,必须同步 `RemapIndex`。 -- 删除完成后再 `Unbind`,避免索引悬挂。 - -### Native 容器 -- Job 通道一律使用 `Allocator.Persistent`。 -- 生命周期由 `InitializeJobDataChannels` / `DisposeJobDataChannels` 集中管理。 -- 帧间复用时使用 `Clear`,不允许用临时重建替代正常复用。 -- Job 数据与主容器数据之间的转换必须集中在 `JobDataChannel` 侧完成。 - -## 生命周期模型 -### 实体进入仿真 -统一由 `EntitySync` 监听实体显示事件后触发: -- Enemy group -> `RegisterEnemyLifecycle` -- Drop group -> `RegisterPickupLifecycle` -- Bullet / Projectile / EnemyProjectile group -> `RegisterProjectileLifecycle` - -### 实体退出仿真 -统一由 `EntitySync` 监听实体隐藏事件后触发: -- Enemy -> `UnregisterEnemyLifecycle` -- Drop -> `UnregisterPickupLifecycle` -- Projectile 相关 group -> `UnregisterProjectileLifecycle` - -### 清场 -`ClearSimulationState` 负责: -- 清空主容器 -- 清空投射物回收与结算缓存 -- 清空区域碰撞请求与命中缓存 -- 清空 Job 通道 -- 清空全部 `EntityBinding` - -## 运行时执行链路 -### 帧级入口 -1. `GameStateBattle.OnUpdate` -2. `_enemyManager.OnUpdate(...)` -3. `SimulationWorld.Tick(...)` -4. `SimulationWorld.LateUpdate()` - -### Tick 总流程 -`SimulationWorld.Tick` 是战斗仿真的唯一主入口。 - -约束: -- 当 `UseSimulationMovement == false` 时,直接返回。 -- `Tick` 只负责逻辑仿真与结算,不直接写 `Transform`。 - -### 每帧仿真管线 -当前实现的标准顺序为: -1. Early Return - - `DeltaTime <= 0` 时只清理碰撞临时通道和统计。 -2. BuildInput - - 将 `_enemies` / `_projectiles` 同步为 Job 输入。 - - 准备敌人输出、投射物输出、碰撞查询缓冲。 -3. StateUpdate - - 调度敌人移动 Job。 - - 调度投射物移动 Job。 -4. Schedule - - 按需调度敌人互斥分离 Job。 - - 合并敌人与投射物 Job 依赖。 -5. Complete - - 等待本帧仿真 Job 完成。 -6. Collision - - 构建碰撞查询。 - - 构建敌人碰撞桶。 - - 生成候选并统计。 -7. WriteBack - - 把输出写回主容器。 - - 在主线程结算碰撞与伤害。 - - 回收失效投射物。 - -### LateUpdate -`LateUpdate` 只做表现写回,不做逻辑判定: -- 敌人位置/朝向写回 -- 投射物位置/朝向写回 - -## 线程模型与边界 -### Job/Burst 允许做的事 -- 读取 Job 输入缓冲 -- 写入 Job 输出缓冲 -- 写入 NativeHashMap / NativeList 等碰撞与分桶数据 -- 执行纯数据计算 - -### Job/Burst 禁止做的事 -- 读写 `Transform` -- 操作 GameObject / Entity 生命周期 -- 调用事件系统 -- 直接调用 `AIUtility.PerformCollision` -- 进行托管分配、LINQ、装箱 - -### 主线程必须做的事 -- 应用输出到仿真主容器 -- 命中结算与伤害计算 -- 投射物失效回收 -- 命中表现事件派发 -- `LateUpdate` 表现写回 - -## 子系统约束 -### 敌人仿真 -固定接入点: -- BuildInput -- Movement -- Separation -- WriteBack - -约束: -- 敌人状态必须以 `EnemySimData` 为中心流动。 -- 互斥与移动结果必须先写入输出缓冲,再统一提交。 -- 与目标选择相关的空间索引脏标记必须在主容器变更时维护。 - -### 投射物仿真 -固定接入点: -- BuildInput -- Movement -- Collision Query -- Resolve -- Recycle - -约束: -- 投射物生命周期状态必须由 `ProjectileSimData.Active` 和 `State` 共同表达。 -- 投射物实际隐藏与移除只能在主线程回收阶段完成。 - -### 碰撞管线 -职责: -- 构建投射物查询和区域查询 -- 生成 broad-phase 候选 -- 在主线程做最终业务结算 - -约束: -- Broad-phase 只能筛候选,不能替代最终命中判定。 -- Area Query 必须保留 `SourceWasActiveAtQueryTime` 快照语义。 -- 候选去重与区域命中去重只能在主线程收口。 - -### 目标选择空间索引 -职责: -- 提供按位置查询最近敌人的能力。 - -约束: -- 仅在仿真启用时对外提供结果。 -- 敌人主容器变更后必须标记脏。 -- 索引是缓存,不是源数据;源数据仍是 `_enemies`。 - -### Presentation -职责: -- 将仿真层结果写回表现层。 -- 消费命中表现事件。 - -约束: -- Presentation 只消费仿真结果,不反向修改仿真逻辑状态。 -- 任何新增表现都应接在 `Presentation` 子模块,而不是接回 Job 或业务结算热路径。 - -## 不可破坏的不变量 -### 生命周期单入口 -仿真容器的增删必须只经过 `EntitySync` 和 `SimEntityState`。 - -### 数据单一事实来源 -主容器是持续态事实来源,Job 缓冲只是帧级副本。 - -### 逻辑与表现分离 -逻辑阶段不写 `Transform`,表现阶段不做业务结算。 - -### 索引一致性 -任何 `swap-back` 删除都必须同步 remap,否则视为架构级错误。 - -### 主线程结算收口 -伤害、事件派发、实体隐藏和回收必须回到主线程。 - -### 空间索引与碰撞桶是缓存 -它们可以重建,不可被外部业务当作持久数据依赖。 - -## 扩展开发流程 -### Step 0:判定接入位置 -先判断新需求属于: -- 新仿真态字段 -- 新执行阶段逻辑 -- 新碰撞查询类型 -- 新表现桥接 - -不要一开始就直接改 `Tick` 主流程。 - -### Step 1:扩状态 -先补 `SimData`、必要的 Job 输入输出结构和转换逻辑。 - -### Step 2:接生命周期 -如果是新实体类型,先定义 show/hide 到仿真态的映射,再进入执行阶段。 - -### Step 3:接执行管线 -优先复用已有阶段;只有确实无法收纳时才新增阶段,并补可观测的 profiler 标记。 - -### Step 4:接主线程结算 -需要业务判定、伤害、事件派发、实体隐藏时,一律回主线程收口。 - -### Step 5:接表现 -视觉写回、命中反馈、临时特效都放在 `Presentation` 侧。 - -### Step 6:补测试 -至少覆盖: -- 正常行为 -- 空容器和边界条件 -- 删除后的索引稳定性 -- 碰撞去重或快照语义 -- 与旧路径一致的关键行为 - -### Step 7:更新文档 -修改模块边界、数据契约、不变量或执行阶段时,必须同步更新本文件。 - -## 回归关注点 -- `ClearSimulationState` 是否把主容器、缓存和 binding 一并清干净。 -- 删除路径是否保持 `swap-back + remap` 一致。 -- Job Native 容器是否有泄漏或容量管理回退。 -- 是否在热路径引入托管分配。 -- 是否让表现逻辑重新侵入仿真逻辑。 -- 是否破坏 Area Query 快照语义。 -- 是否破坏碰撞候选与命中去重。 - -## 测试建议 -至少保留并持续扩展以下类型的测试: -- Tick 行为正确性 -- 主线程与 Job 管线一致性 -- 最近敌查询正确性 -- 投射物候选上限与玩家候选覆盖 -- Area Query 快照语义 -- 清场和 Battle 循环稳定性 - -## 维护原则 -如果未来需要继续扩展为多模式仿真开关、更多 Job 管线层级或新的仿真对象类型,应优先维护以下三点: -- `Tick` 仍然只有一个主入口 -- 仿真态生命周期仍然只有一个注册/反注册入口 -- 主线程结算与表现写回边界不被打穿 diff --git a/skills/ui-five-layer-architecture/SKILL.md b/skills/ui-five-layer-architecture/SKILL.md deleted file mode 100644 index 4b898d0..0000000 --- a/skills/ui-five-layer-architecture/SKILL.md +++ /dev/null @@ -1,81 +0,0 @@ ---- -name: ui-five-layer-architecture -description: Define, review, and refactor UI modules using a strict five-layer architecture (UseCase, RawData, Controller, Context, View). Use when creating cross-project UI architecture standards, designing new UI modules, reviewing layer boundaries and event flow, converting ad-hoc UI code into layered structure, or validating UI test strategy for business-driven interfaces. ---- - -# UI Five-Layer Architecture - -## Quick Start - -1. Read `./references/ui-five-layer-standard.md`. -2. Classify the UI as `standard-five-layer` or `lightweight`. -3. Apply boundary rules before editing code or writing design output. -4. Use the checklists in this file to drive design, review, or refactor tasks. - -## Workflow - -### 1. Scope the task - -Decide which mode the user needs: - -- architecture-spec mode: create or revise a UI architecture standard -- design mode: design one or more UI modules before coding -- implementation mode: implement or refactor code to match the standard -- review mode: audit existing code for boundary or dependency violations - -### 2. Choose module level - -Use `standard-five-layer` when UI owns business state transitions, validations, or branching behavior. - -Use `lightweight` when UI only handles display/navigation and has no independent business rules. - -### 3. Enforce non-negotiable boundaries - -Apply these constraints in every mode: - -- keep `UseCase` business-only and return only `RawData/Result` -- build `Context` only inside `Controller` -- keep `View` presentation-only and event-emitting only -- keep `View` out of global business event subscriptions -- route external UI open/close/refresh through `Controller` - -### 4. Apply communication and dependency checks - -Validate: - -- dependency direction is valid for all touched files -- UI-specific events stay UI-local in meaning -- `Controller` filters sender and instance scope when needed -- no `RawData` field uses `*Context` types - -### 5. Produce mode-specific output - -- architecture-spec mode: - - output the final standard text - - include strict rules, permitted exceptions, and checklists -- design mode: - - output layer map and flow diagram for each UI module - - include type list and event list -- implementation mode: - - implement code changes matching the standard - - update tests if `UseCase` behavior changes -- review mode: - - report findings first, ordered by severity - - include concrete file and line references - -## Architecture Checklist - -Use this list before closing any task: - -1. Classify each UI module correctly: `standard-five-layer` or `lightweight`. -2. Ensure `UseCase` does not construct `Context` or touch view concerns. -3. Ensure `RawData` does not include `*Context` or presentation types. -4. Ensure `Controller` owns all `RawData/Result -> Context` transformation. -5. Ensure `View` only consumes `Context` and emits UI-local events. -6. Ensure external open/close/update operations enter through `Controller`. -7. Ensure event ownership and sender filtering are explicit. -8. Ensure test approach matches policy in the reference file. - -## References - -Read `./references/ui-five-layer-standard.md` for the full specification. diff --git a/skills/ui-five-layer-architecture/agents/openai.yaml b/skills/ui-five-layer-architecture/agents/openai.yaml deleted file mode 100644 index d92a807..0000000 --- a/skills/ui-five-layer-architecture/agents/openai.yaml +++ /dev/null @@ -1,4 +0,0 @@ -interface: - display_name: "UI Five-Layer Architecture" - short_description: "Cross-project UI five-layer architecture standards" - default_prompt: "Use $ui-five-layer-architecture to define, review, or refactor a UI module with clear five-layer boundaries." diff --git a/skills/ui-five-layer-architecture/references/ui-five-layer-standard.md b/skills/ui-five-layer-architecture/references/ui-five-layer-standard.md deleted file mode 100644 index 6a60f41..0000000 --- a/skills/ui-five-layer-architecture/references/ui-five-layer-standard.md +++ /dev/null @@ -1,274 +0,0 @@ -# UI Five-Layer Architecture Standard - -## Table of Contents - -1. [Scope and Intent](#scope-and-intent) -2. [Core Model](#core-model) -3. [Layer Definitions](#layer-definitions) -4. [UI Module Levels](#ui-module-levels) -5. [Dependency Rules](#dependency-rules) -6. [Event Communication Rules](#event-communication-rules) -7. [Interaction Flow](#interaction-flow) -8. [Naming and Folder Conventions](#naming-and-folder-conventions) -9. [Testing Policy](#testing-policy) -10. [Anti-Patterns](#anti-patterns) -11. [Delivery Checklist](#delivery-checklist) - -## Scope and Intent - -Use this standard to define and enforce a stable UI architecture that can be reused across projects. - -Primary goals: - -- keep business logic independent from UI rendering -- keep UI rendering deterministic and testable -- make reviews and refactors consistent -- reduce coupling and regression risk - -## Core Model - -Base chain: - -```text -External Flow - -> Controller - -> UseCase - -> RawData / Result - -> BuildContext - -> View - -View - --(UI-local event)--> Controller -``` - -Rules: - -- `UseCase` produces business outputs only. -- `Controller` is the only layer that creates `Context`. -- `View` only renders and emits interaction events. - -## Layer Definitions - -### UseCase - -Responsibilities: - -- own business rules and state transitions -- validate business actions -- return `RawData` or result objects - -Constraints: - -- do not depend on `Context`, `View`, or rendering types -- do not format display strings or map visual assets -- do not publish UI-local events - -### RawData - -Responsibilities: - -- carry business data from `UseCase` to `Controller` - -Constraints: - -- keep data business-oriented -- do not reference any `*Context` type -- do not contain rendering objects (for example UI components) - -### Controller - -Responsibilities: - -- be the external entry point for open/close/refresh -- bind and call `UseCase` -- transform `RawData/Result` into `Context` -- subscribe/unsubscribe UI-local events -- coordinate full or partial refresh - -Constraints: - -- do not let `View` bypass controller orchestration -- do not hide heavy business logic that belongs in `UseCase` - -### Context - -Responsibilities: - -- carry display-ready data for rendering - -Constraints: - -- construct/update only in `Controller` -- do not enter `UseCase` -- allow composition (`FormContext`, `ItemContext`, `AreaContext`) - -### View - -Responsibilities: - -- bind controls and render `Context` -- emit UI-local interaction events - -Constraints: - -- do not call `UseCase` -- do not mutate domain state -- do not subscribe to global business events -- do not become external entry points - -## UI Module Levels - -### Standard Five-Layer Module - -Structure: - -- `UseCase + RawData + Controller + Context + View` - -Use when: - -- module owns business rules, validations, or branching state transitions -- user actions mutate domain state -- behavior requires automated verification - -### Lightweight Module - -Structure: - -- `Controller + Context + View` - -Use when: - -- module is display/navigation/confirmation only -- no independent business rules are present - -Rule: - -- upgrade to standard five-layer as soon as business rules appear - -## Dependency Rules - -Allowed: - -- `UseCase -> domain/services/RawData/Result` -- `Controller -> UseCase/RawData/Result/Context/View/UI-local events` -- `Context -> child context/value objects` -- `View -> Context/UI-local events` - -Forbidden: - -- `UseCase -> Context/View/rendering types` -- `RawData/Result -> Context/View` -- `Context -> UseCase/View` -- `View -> UseCase` -- `View -> global business events` -- `View -> domain state mutation` - -## Event Communication Rules - -### Event Ownership - -- `View -> Controller` uses UI-local events only -- UI-local events are not global business contracts -- business/domain modules must not consume UI-local event semantics - -### Safety Requirements - -- validate sender ownership in `Controller` -- scope handling to the active UI instance -- keep subscribe/unsubscribe symmetric - -## Interaction Flow - -### Standard Module - -```text -External Flow - -> create/bind UseCase - -> open UI via Controller - -Controller - -> UseCase action - -> RawData/Result - -> BuildContext - -> View refresh - -View - --(UI-local event)--> Controller -``` - -### Lightweight Module - -```text -External Flow - -> open UI via Controller(userData) - -Controller - -> BuildContext(userData) - -> View refresh - -View - --(UI-local event)--> Controller -``` - -## Naming and Folder Conventions - -Recommended folders: - -- `UI//UseCase` -- `UI//RawData` -- `UI//Controller` -- `UI//Context` -- `UI//View` - -Recommended naming: - -- `XXXFormUseCase` -- `XXXFormRawData` -- `XXXFormController` -- `XXXFormContext`, `XXXItemContext`, `XXXAreaContext` -- `XXXResult`, `XXXActionResult` - -## Testing Policy - -Policy: - -- if a UI has a `UseCase` and automated tests are added, use EditMode tests for the `UseCase` - -Priority coverage: - -- initial model generation -- business branch and validation behavior -- action result correctness -- boundary and invalid input handling - -Manual verification focus: - -- first open -- interaction refresh -- partial refresh -- close and reopen -- null/invalid userData behavior - -## Anti-Patterns - -Do not allow: - -- `UseCase` returning `Context` -- `RawData` carrying `*Context` -- `View` subscribing global business events -- direct domain mutation inside `View` -- skipping `Controller` as module entry -- module marked lightweight while carrying business state transitions - -## Delivery Checklist - -Use this checklist before marking work complete: - -1. classify each module as standard or lightweight -2. verify business rules sit in `UseCase` only -3. verify `Context` is built only in `Controller` -4. verify `RawData` has no presentation model leakage -5. verify `View` is render-and-emit only -6. verify UI-local events are scoped and sender-checked -7. verify dependency direction constraints pass -8. verify tests/manual checks match the testing policy diff --git a/skills/weapon-development/SKILL.md b/skills/weapon-development/SKILL.md deleted file mode 100644 index d0bdc7b..0000000 --- a/skills/weapon-development/SKILL.md +++ /dev/null @@ -1,92 +0,0 @@ ---- -name: weapon-development -description: Develop and extend the VampireLike weapon system. Use when creating new weapons, updating weapon state machines, changing target selection/effects/data parsing, or integrating weapon behavior with shop, inventory, and entity flow. ---- - -# Weapon Development - -## Quick Start - -1. Read full baseline spec: `./references/WeaponDevelopmentSkill.md`. -2. Confirm change scope: - - weapon runtime (`WeaponBase`, concrete weapons) - - state flow (`Idle`, `Check_OutRange`, `Check_InRange`, `Attack`) - - target selector (`ITargetSelector`, `TargetSelectorType`) - - effect layer (`IWeaponAttackEffect`) - - data contract (`DRWeapon`, `WeaponData`, `ParamsData`) -3. Keep behavior compatibility with current gameplay loop, shop flow, inventory flow, and UI/event chain. - -## Source Map - -- Weapon base: - - `../../Assets/GameMain/Scripts/Entity/EntityLogic/Weapon/WeaponBase.cs` -- Existing weapons: - - `../../Assets/GameMain/Scripts/Entity/EntityLogic/Weapon/WeaponKnife/` - - `../../Assets/GameMain/Scripts/Entity/EntityLogic/Weapon/WeaponHandgun/` - - `../../Assets/GameMain/Scripts/Entity/EntityLogic/Weapon/WeaponSlash/` - - `../../Assets/GameMain/Scripts/Entity/EntityLogic/Weapon/WeaponLightning/` -- Selectors: - - `../../Assets/GameMain/Scripts/Entity/EntityLogic/Weapon/TargetSelector/` -- Attack effects: - - `../../Assets/GameMain/Scripts/Entity/EntityLogic/Weapon/AttackEffects/` -- Weapon data: - - `../../Assets/GameMain/Scripts/Entity/EntityData/Weapon/` -- Weapon table: - - `../../Assets/GameMain/DataTables/Weapon.txt` -- Data row: - - `../../Assets/GameMain/Scripts/DataTable/DRWeapon.cs` -- Shop integration: - - `../../Assets/GameMain/Scripts/UI/GameScene/UseCase/ShopFormUseCase.cs` -- Entity show flow: - - `../../Assets/GameMain/Scripts/Entity/EntityExtension.cs` - -## Non-Negotiable Invariants - -- Do not duplicate logic already owned by `WeaponBase`. -- Keep state transitions explicit and non-blocking. -- Keep cooldown accumulation valid even when no target is found. -- Keep attack logic and visual effect logic decoupled. -- Parse weapon-specific parameters into strong-typed `ParamsData` at data initialization time. -- Treat `Weapon.txt` `Params` as a JSON object column; empty params must use `{}`. -- Preserve compatibility with shop/inventory/UI refresh flow. - -## Change Recipes - -### Add a New Weapon - -1. Extend `WeaponType` without reordering existing enum values. -2. Add `WeaponXxxData : WeaponData` and `WeaponXxxParamsData` for weapon-specific parameters. -3. Parse `ParamsJson` through `ParseParams()` inside the weapon data constructor. -4. Add `WeaponXxx : WeaponBase` and implement only weapon-specific behavior. -5. Build state files under `Weapon/WeaponXxx/` with partial class layout. -6. Register display/data mapping path so `ShowWeapon` and shop purchase can instantiate correctly. - -### Add or Update a Target Selector - -1. Implement `ITargetSelector`. -2. Update `TargetSelectorType`. -3. Register creation in `WeaponBase.CreateSelector`. -4. Validate target semantics with current-health/runtime-health rules where applicable. - -### Add or Update Attack Effect - -1. Implement `IWeaponAttackEffect`. -2. Trigger effect from weapon attack state only. -3. Keep damage resolution outside effect code. - -### Add or Update Weapon Parameters - -1. Update `Weapon.txt` `Params` JSON object. -2. Add or update the corresponding `WeaponXxxParamsData` fields. -3. Keep key names aligned with `ParamsData` property names. -4. Read values from `ParamsData` in weapon initialization, not by manual string parsing in runtime logic. - -## Validation Checklist - -- Weapon can be shown, attached, and updated without exceptions. -- State machine does not stall across target loss/reacquire. -- Cooldown and range checks match design expectation. -- Damage path and effect path remain decoupled. -- `ParamsData` matches table content and default fallback behavior. -- UI/shop/inventory interactions stay stable after the change. -- Update `./references/WeaponDevelopmentSkill.md` if contracts or recommended patterns changed. diff --git a/skills/weapon-development/agents/openai.yaml b/skills/weapon-development/agents/openai.yaml deleted file mode 100644 index d8a820e..0000000 --- a/skills/weapon-development/agents/openai.yaml +++ /dev/null @@ -1,4 +0,0 @@ -interface: - display_name: "Weapon Development" - short_description: "Build and extend VampireLike weapon architecture" - default_prompt: "Use $weapon-development to implement a weapon system change with stable state flow and data contracts." diff --git a/skills/weapon-development/references/WeaponDevelopmentSkill.md b/skills/weapon-development/references/WeaponDevelopmentSkill.md deleted file mode 100644 index b8f75c1..0000000 --- a/skills/weapon-development/references/WeaponDevelopmentSkill.md +++ /dev/null @@ -1,262 +0,0 @@ -# Weapon Development Skill(VampireLike) - -## 目标 -本文件是 `Entity.Weapon` 体系的开发规范与速查手册。 -后续新增武器、扩展参数、调整状态机或联动商店/背包时,优先按本文档执行,避免重复通读历史上下文。 - -## 当前架构总览 -- 武器运行时入口:`WeaponBase`(`Assets/GameMain/Scripts/Entity/EntityLogic/Weapon/WeaponBase.cs`) -- 武器具体实现:`WeaponKnife`、`WeaponHandgun`、`WeaponSlash`、`WeaponLightning` -- 目标选择策略:`ITargetSelector` + `TargetSelectorType` -- 攻击可视化:`IWeaponAttackEffect` -- 数据入口:`DRWeapon` -> `WeaponData`(及其子类) -- 实体生成:`EntityExtension.ShowWeapon` -- 商店接入:`ShopFormUseCase.CreateWeaponData` - -## WeaponBase 统一职责(已上收) -`WeaponBase` 负责以下通用逻辑,子类不要重复实现: -- 生命周期模板:`OnShow/OnHide/OnAttachTo/OnDetachFrom` -- 状态机管理:`RegisterState`、`EnsureStatesBuilt`、`TransitionTo` -- 通用运行字段:`_target`、`_currAttackTimer`、`_sqrRange` -- 启用门控:`OnUpdate` 中统一 `if (!_isEnabled) return` -- 目标选择入口:`SelectTarget`、`SetTargetSelector`、`CreateSelector` -- 距离判定:`IsInRange`(基于 XZ 平面距离) -- Simulation 命中请求:`TryQueueAreaCollisionQuery`、`TryQueueSectorCollisionQuery` -- 玩家攻击属性订阅:`BindAttackStatFromOwner` / `ReleaseAttackStatSubscription` - -约束: -- 不要在子类里重复实现 `WeaponBase` 已有能力。 -- 行为差异优先收敛在:`BuildStates`、`Check`、`Attack`、少量专属辅助方法。 - -## 当前武器模板分类 -### 1. `WeaponKnife` -- 模式:近身前刺 + 圆形范围命中 -- 典型参数:`HitRadius` -- 适合派生:长枪、刺剑、短矛、震地锤近身版 - -### 2. `WeaponHandgun` -- 模式:单次 `Raycast` 瞬发命中 -- 当前参数化程度较低,仍适合作为远程枪械母版继续扩展 -- 适合派生:手枪、霰弹枪、狙击枪、三连发枪 - -### 3. `WeaponSlash` -- 模式:扇形范围命中 -- 典型参数:`SectorAngle` -- 适合派生:大剑、斧头、半月斩、横扫类武器 - -### 4. `WeaponLightning` -- 模式:锁定目标点 + 落点范围打击 -- 典型参数:`HitRadius`、`HoverHeight` -- 适合派生:闪电、陨石杖、圣光柱、空袭类武器 - -## 状态机约定 -统一状态枚举: -- `Idle` -- `Check_OutRange` -- `Check_InRange` -- `Attack` -- `Disabled`(可选) - -推荐流程: -1. `Idle`:查找目标,计时器持续累积。 -2. `Check_OutRange`:有目标但不在攻击范围,继续转向并累积计时。 -3. `Check_InRange`:在攻击范围内,若 `currAttackTimer >= Cooldown` 切 `Attack`。 -4. `Attack`:执行攻击,重置计时器,结束后回 `Check_InRange`。 - -关键规则: -- 即使没有目标,也允许蓄力(计时器持续走)。 -- 一旦进入 `Check_InRange` 且冷却已满,应立即触发攻击。 -- 攻击过程中若需要多帧动画,使用 `_isAttacking` 控制状态退出时机。 - -## 3D 场景下的距离/朝向原则 -- 射程与目标筛选:优先使用 XZ 平面距离(忽略 Y),调用 `AIUtility.GetSqrMagnitudeXZ`。 -- 视觉朝向与弹道:可按武器设计决定是否使用完整 3D 向量。 - - `WeaponHandgun`:允许俯仰瞄准,逻辑上用射线命中对象判定伤害。 - - 近战地面范围类(Knife/Slash):伤害检测建议投影到地面(XZ)再判定。 - - 落点类(Lightning):锁点可取目标当前位置,但范围判定仍建议按地面距离收口。 - -## 目标选择策略规范 -接口:`ITargetSelector.SelectTarget(WeaponBase weapon, IEnumerable candidates, float maxSqrRange)` - -现有策略: -- `NearestTargetSelector` -- `HighestHealthTargetSelector` -- `LowestHealthTargetSelector` - -语义约定: -- `NearestTargetSelector` 在 Simulation 启用时优先走空间索引。 -- `HighestHealth` / `LowestHealth` 当前基于运行时生命值选择目标。 -- 若新增新策略,必须明确“按当前值”还是“按最大值”筛选,避免语义漂移。 - -扩展策略步骤: -1. 新建 selector 类并实现 `ITargetSelector`。 -2. 更新 `TargetSelectorType` 枚举。 -3. 在 `WeaponBase.CreateSelector` 中注册。 -4. 武器在 `OnWeaponShow` 或初始化阶段选择策略。 - -## 攻击可视化效果规范 -接口:`IWeaponAttackEffect.Play(WeaponBase weapon, Vector3 position, EntityBase target, float radius)` - -约定: -- 可视化逻辑与伤害逻辑解耦。 -- 武器类只负责触发 `Play`,不把可视化细节塞回武器核心逻辑。 -- 当前阶段允许临时对象创建;后续若有性能压力再统一对象池化。 -- 命中特效、范围预警、扇形描边都属于 effect 层,不属于伤害层。 - -## 数据层规范(DRWeapon / WeaponData) -### `DRWeapon` -提供通用字段: -- `Attack` -- `Cooldown` -- `AttackRange` -- `AttackSoundId` -- `ParamsJson` -- `Pramas` -- `Modifiers` - -约定: -- `Params` 列现在使用标准 JSON 对象。 -- 空参数统一写 `{}`。 -- 不再兼容 `[]`。 -- `Pramas` 保留为描述/UI 的兼容字典视图,不再作为武器逻辑主读取入口。 - -### `WeaponData` -提供公共武器字段与通用解析入口: -- 公共战斗字段:`Attack`、`Cooldown`、`AttackRange` -- 公共展示字段:`Title`、`IconAssetName`、`Rarity`、`Price` -- 参数入口:`ParamsJson`、`Params` -- 通用强类型解析:`ParseParams()` - -约定: -- 参数解析优先在数据层完成。 -- 武器逻辑层读取强类型 `ParamsData`,不要散落字符串 `Parse`。 -- 只有描述/UI 等弱类型场景才继续读取 `Params` 字典。 - -### 具体武器数据子类 -每种武器数据子类都应持有自己的 `ParamsData`: -- `WeaponKnifeData -> WeaponKnifeParamsData` -- `WeaponHandgunData -> WeaponHandgunParamsData` -- `WeaponSlashData -> WeaponSlashParamsData` -- `WeaponLightningData -> WeaponLightningParamsData` - -当前已接通字段: -- `WeaponKnifeParamsData` - - `HitRadius` -- `WeaponHandgunParamsData` - - 暂无字段 -- `WeaponSlashParamsData` - - `SectorAngle` -- `WeaponLightningParamsData` - - `HitRadius` - - `HoverHeight` - -JSON 约束: -- key 名应与 `ParamsData` 属性名一致。 -- 统一使用 JSON 对象,不要再使用自定义 KV 串。 -- 不建议在 `ParamsJson` 中使用制表符和跨行内容,避免 txt 表分列出错。 - -## 新增武器标准流程 -1. 定义枚举 - - 更新 `WeaponType`,保持递增值,避免重排已有值。 - -2. 建立数据类 - - 新建 `WeaponXxxData : WeaponData`。 - - 新建 `WeaponXxxParamsData`。 - - 在构造阶段调用 `ParseParams()`,把参数初始化为强类型字段。 - -3. 建立行为类 - - 新建 `WeaponXxx : WeaponBase`。 - - 只实现差异化逻辑:`BuildStates`、`Check`、`Attack`、特有检测/动画。 - -4. 建立状态类 - - 推荐放在 `Weapon/WeaponXxx/` 目录,采用 partial 组织: - - `WeaponXxx.cs` - - `WeaponXxx.IdleState.cs` - - `WeaponXxx.CheckOutRangeState.cs` - - `WeaponXxx.CheckInRangeState.cs` - - `WeaponXxx.AttackState.cs` - -5. 建立可视化(可选) - - 新建 `WeaponXxxAttackEffect : IWeaponAttackEffect`,在武器中组合调用。 - -6. 接入实体展示与数据表 - - 确保 `DRWeapon` / `DREntity` 配置齐全。 - - `EntityExtension.ShowWeapon` 可正确映射到 `Entity.Weapon.WeaponXxx`。 - - 商店/背包创建入口能正确构造 `WeaponXxxData`。 - -7. 联动系统 - - 背包、商店购买/出售、UI 展示、事件流刷新。 - -8. 验证点 - - 武器能正确生成、附着、更新。 - - `ParamsData` 与数据表一致。 - - 描述文本仍正确展示。 - - Simulation 模式和非 Simulation 模式都能命中。 - -## 扩展优先级建议 -### 第一批:低成本高收益 -- 长枪 / 刺剑 - - 基于 `WeaponKnife` -- 大剑 / 半月斩 - - 基于 `WeaponSlash` -- 战锤 / 震地锤 - - 基于 `WeaponLightning` 或 `WeaponKnife` -- 霰弹枪 - - 基于参数化后的 `WeaponHandgun` -- 狙击枪 - - 基于参数化后的 `WeaponHandgun` -- 陨石杖 / 圣光柱 - - 基于 `WeaponLightning` - -### 第二批:中成本扩展 -- 链式闪电 -- 穿透弹 / 火球 -- 地雷 / 陷阱 -- 回旋镖 - -### 暂缓项 -- 持续激光 -- 喷火器 -- 环绕飞剑 -- 常驻法球 -- 状态异常驱动的复杂武器流派 - -原因: -- 当前武器主流程仍是单次攻击结算。 -- 持续伤害、异常状态和常驻 orbit 行为尚未形成统一挂点。 - -## `WeaponHandgun` 后续建议 -当前 `WeaponHandgun` 参数化仍偏弱,建议作为下一阶段母版优先扩展。 - -建议新增字段: -- `PelletCount` -- `SpreadAngle` -- `PenetrationCount` -- `FireOriginOffsetX` -- `FireOriginOffsetY` -- `FireOriginOffsetZ` -- `HitMarkerSize` -- `HitMarkerYOffset` -- `HitMarkerDuration` - -完成后可快速派生: -- 手枪 -- 霰弹枪 -- 狙击枪 -- 三连发枪 - -## 代码检查清单(提交前) -- 是否重复实现了 `WeaponBase` 已有通用逻辑。 -- 状态流转是否会卡死或漏转场。 -- 冷却计时是否在无目标时仍正常累积。 -- 目标失效(null/Unavailable/死亡)是否安全处理。 -- 命中检测是否符合武器设计(地面范围/射线/扇形/落点)。 -- 数据参数是否全部收敛到 `ParamsData`。 -- 可视化是否与伤害逻辑解耦。 -- 是否正确订阅/解绑攻击属性(或复用基类方法)。 -- 商店、背包、武器描述是否仍保持兼容。 - -## 已知注意点 -- 目录中存在 `Pramas` 命名拼写历史包袱,当前保持兼容即可。 -- 文档中的“规范”优先级高于历史实现;历史实现若偏离,按本规范逐步收敛。 -- 当前更推荐“公共 `WeaponData` + 专属 `ParamsData`”结构,不建议把每种武器做成一套完全独立的数据总线。