- Checkpoint 1:清理 TickEnemies 侧 GC
- Checkpoint 2:解耦 Simulation 核心与 `Transform` 运行时依赖 - Checkpoint 3:收口 `EntitySync` 职责边界 - Checkpoint 4:拆分 Simulation Tick 阶段 - Checkpoint 5:补最小回归测试 - Checkpoint 6:补充 P1.5 结项文档
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
---
|
||||
name: simulation-development
|
||||
description: Maintain and extend the VampireLike Simulation layer. Use when modifying `Assets/GameMain/Scripts/Simulation` or related runtime paths (`GameStateBattle`, enemy movement gate, entity lifecycle sync, separation solver), including P1.5 cleanup and P2 Job/Burst preparation.
|
||||
---
|
||||
|
||||
# Simulation Development
|
||||
|
||||
## Quick Start
|
||||
|
||||
1. Read baseline design doc: `./references/SimulationDevelopmentSkill.md`.
|
||||
2. Read latest measured baseline: `../../docs/P1.5 Simulation-Supplement.md`.
|
||||
3. Confirm your change scope is one or more of:
|
||||
- `SimData` contracts (`EnemySimData`, `ProjectileSimData`, `PickupSimData`)
|
||||
- lifecycle sync (`SimulationWorld.EntitySync`)
|
||||
- per-frame simulation (`SimulationWorld.Tick`, `TickEnemies`)
|
||||
- presentation write-back (`SimulationWorld.Presentation`)
|
||||
- enemy separation solver integration (`EnemySeparationSolverProvider`)
|
||||
4. Keep rollback path available through `UseSimulationMovement`.
|
||||
|
||||
## Source Map
|
||||
|
||||
- Simulation core: `../../Assets/GameMain/Scripts/Simulation/SimulationWorld.cs`
|
||||
- Lifecycle sync: `../../Assets/GameMain/Scripts/Simulation/SimulationWorld.EntitySync.cs`
|
||||
- Presentation sync: `../../Assets/GameMain/Scripts/Simulation/SimulationWorld.Presentation.cs`
|
||||
- Tick context: `../../Assets/GameMain/Scripts/Simulation/SimulationTickContext.cs`
|
||||
- Index binding: `../../Assets/GameMain/Scripts/Simulation/EntityBinding.cs`
|
||||
- Battle entry: `../../Assets/GameMain/Scripts/Procedure/Game/GameStateBattle.cs`
|
||||
- Global component init: `../../Assets/GameMain/Scripts/Base/GameEntry.Custom.cs`
|
||||
- Enemy old path gate:
|
||||
- `../../Assets/GameMain/Scripts/Entity/EntityLogic/Enemy/MeleeEnemy.cs`
|
||||
- `../../Assets/GameMain/Scripts/Entity/EntityLogic/Enemy/RemoteEnemy.cs`
|
||||
- Separation solver:
|
||||
- `../../Assets/GameMain/Scripts/Utility/EnemySeperator/IEnemySeparationSolver.cs`
|
||||
- `../../Assets/GameMain/Scripts/Utility/EnemySeperator/EnemySeparationSolverProvider.cs`
|
||||
- `../../Assets/GameMain/Scripts/Utility/EnemySeperator/GridBucketEnemySeparationSolver.cs`
|
||||
- P1.5 baseline doc:
|
||||
- `../../docs/P1.5 Simulation-Supplement.md`
|
||||
|
||||
## Non-Negotiable Invariants
|
||||
|
||||
- Maintain `EntityId <-> SimulationIndex` consistency.
|
||||
- Use swap-back removal (`move last -> remove last -> remap index`).
|
||||
- Keep lifecycle registration/removal inside `EntitySync` event flow; do not double-write containers from gameplay code.
|
||||
- Keep logic/presentation boundary:
|
||||
- Simulation computes logical outputs.
|
||||
- Presentation writes back `Transform`.
|
||||
- Keep A/B rollback path:
|
||||
- `UseSimulationMovement == false` must preserve old behavior path.
|
||||
- Avoid new managed allocations in Tick hot paths.
|
||||
|
||||
## Change Recipes
|
||||
|
||||
### Add or Change SimData Fields
|
||||
|
||||
1. Update target struct in `Simulation/SimData/`.
|
||||
2. Populate default/initial values in `EntitySync` create methods.
|
||||
3. Apply runtime updates in `Tick` phase.
|
||||
4. Consume outputs in `Presentation` only if visual write-back is needed.
|
||||
5. Ensure backward compatibility when `UseSimulationMovement` is off.
|
||||
|
||||
### Extend Enemy Tick Behavior
|
||||
|
||||
1. Keep deterministic stage order (`BuildInput -> Move/Separation -> StateUpdate -> WriteBack`).
|
||||
2. Preserve state semantics and avoid direct UI/event side effects in Tick.
|
||||
3. Keep `ProfilerMarker` coverage for each stage.
|
||||
4. Keep Tick hot path data-driven (no direct `Transform` read/write).
|
||||
|
||||
### Implement Projectile/Pickup Tick (from placeholder to real behavior)
|
||||
|
||||
1. Keep lifecycle path unchanged (`EntitySync` handles add/remove).
|
||||
2. Add dedicated tick methods in `SimulationWorld` for each data type.
|
||||
3. Keep outputs in data containers; write visuals in presentation phase.
|
||||
4. Ensure removal path and binding remap rules are identical to enemy path.
|
||||
|
||||
### Refactor Toward Job/Burst
|
||||
|
||||
1. Prioritize `Move/Separation` stage parallelization.
|
||||
2. Keep `ProfilerMarker` and stage boundaries stable for P1.5/P2 comparison.
|
||||
3. Leave transform write-back to presentation-only stage.
|
||||
4. Keep managed allocations and virtual dispatch out of core loops.
|
||||
|
||||
## Validation Checklist
|
||||
|
||||
- `UseSimulationMovement = false` and `true` both run correctly.
|
||||
- No duplicate registration or stale index after entity hide/destroy.
|
||||
- Battle loop remains stable (`Battle -> LevelUp -> Shop -> Battle`).
|
||||
- No new per-frame GC spikes in `TickEnemies`.
|
||||
- Main flow has no new Error/Exception logs.
|
||||
- Update `./references/SimulationDevelopmentSkill.md` and `../../docs/P1.5 Simulation-Supplement.md` if contracts, boundaries, or baseline data changed.
|
||||
@@ -0,0 +1,4 @@
|
||||
interface:
|
||||
display_name: "Simulation Development"
|
||||
short_description: "Maintain and extend VampireLike Simulation architecture"
|
||||
default_prompt: "Use $simulation-development to implement and validate a Simulation layer change with rollback safety."
|
||||
@@ -0,0 +1,157 @@
|
||||
# Simulation Development Skill(VampireLike)
|
||||
|
||||
## 目标
|
||||
本文件是 `Simulation` 分层的开发规范与速查手册。
|
||||
后续调整敌人移动、补齐投射物/掉落物逻辑、推进 Job/Burst 改造时,优先按本文档执行,避免反复通读全部代码。
|
||||
|
||||
## 当前架构总览(P1.5 已落地)
|
||||
- Simulation 主目录:`Assets/GameMain/Scripts/Simulation/`
|
||||
- 核心组件:`SimulationWorld`(`GameFrameworkComponent`)
|
||||
- 数据容器:
|
||||
- `List<EnemySimData> _enemies`
|
||||
- `List<ProjectileSimData> _projectiles`
|
||||
- `List<PickupSimData> _pickups`
|
||||
- Tick 临时缓冲:
|
||||
- `List<EnemyTickWorkItem> _enemyTickWorkItems`
|
||||
- `List<EnemySeparationAgent> _enemySeparationAgents`
|
||||
- 索引绑定:`EntityBinding`(`EntityId <-> SimulationIndex` 双向映射)
|
||||
- 生命周期同步:`SimulationWorld.EntitySync`(监听实体 Show/Hide 事件)
|
||||
- 表现层回写:`SimulationWorld.Presentation`(`LateUpdate` 写回 `Transform`)
|
||||
- Tick 上下文:`SimulationTickContext`(`DeltaTime`、`RealDeltaTime`、`PlayerPosition`)
|
||||
|
||||
## 运行时主链路(按帧)
|
||||
1. `GameEntry.InitCustomComponents()` 获取或自动挂载 `SimulationWorld`
|
||||
文件:`Assets/GameMain/Scripts/Base/GameEntry.Custom.cs`
|
||||
2. `ProcedureGame.OnEnter()` 清理旧 Simulation 数据
|
||||
文件:`Assets/GameMain/Scripts/Procedure/Game/ProcedureGame.cs`
|
||||
3. `GameStateBattle.OnUpdate()` 中先执行刷怪,再执行 `SimulationWorld.Tick(...)`
|
||||
文件:`Assets/GameMain/Scripts/Procedure/Game/GameStateBattle.cs`
|
||||
4. `SimulationWorld.Tick()` 仅在 `UseSimulationMovement == true` 时执行敌人 Tick
|
||||
5. `SimulationWorld.LateUpdate()` 执行 `Presentation.OnLateUpdate()`,将仿真结果写回敌人 `Transform`
|
||||
|
||||
## 生命周期与数据同步设计
|
||||
`EntitySync` 通过事件驱动保持 Simulation 容器与实体生命周期一致:
|
||||
|
||||
| 事件 | 组名 | 行为 |
|
||||
|---|---|---|
|
||||
| `ShowEntitySuccessEventArgs` | `Enemy` | `RegisterEnemyLifecycle` + `UpsertEnemy` |
|
||||
| `HideEntityCompleteEventArgs` | `Enemy` | `UnregisterEnemyLifecycle` + `RemoveEnemyByEntityId` |
|
||||
| `ShowEntitySuccessEventArgs` | `Drop` | `RegisterPickupLifecycle` + `UpsertPickup` |
|
||||
| `HideEntityCompleteEventArgs` | `Drop` | `UnregisterPickupLifecycle` + `RemovePickupByEntityId` |
|
||||
| `ShowEntitySuccessEventArgs` | `Bullet` / `Projectile` | `RegisterProjectileLifecycle` + `UpsertProjectile` |
|
||||
| `HideEntityCompleteEventArgs` | `Bullet` / `Projectile` | `UnregisterProjectileLifecycle` + `RemoveProjectileByEntityId` |
|
||||
|
||||
关键规则:
|
||||
- 删除容器元素统一使用“末尾覆盖 + `RemoveAt(lastIndex)` + `EntityBinding.RemapIndex`”。
|
||||
- `Upsert` 语义:`EntityId` 已存在则覆盖,不存在则追加。
|
||||
|
||||
## EnemySimData 合约(当前实现)
|
||||
文件:`Assets/GameMain/Scripts/Simulation/SimData/EnemySimData.cs`
|
||||
|
||||
- `EntityId`:实体唯一标识
|
||||
- `Position / Forward / Rotation`:逻辑输出与表现层写回字段
|
||||
- `Speed`:来自 `EnemyData.SpeedBase`
|
||||
- `AttackRange`:当前固定初始化为 `1f`
|
||||
- `AvoidEnemyOverlap / EnemyBodyRadius / SeparationIterations`:从 `MovementComponent` 读取
|
||||
- `TargetType / State`:状态扩展预留
|
||||
|
||||
当前状态值(`SimulationWorld` 常量):
|
||||
- `0`:Idle
|
||||
- `1`:Chasing
|
||||
- `2`:InAttackRange
|
||||
|
||||
## TickEnemies 当前算法(P1.5 分阶段)
|
||||
文件:`Assets/GameMain/Scripts/Simulation/SimulationWorld.cs`
|
||||
|
||||
`TickEnemies` 入口保持纯数据热路径,不直接读写 `Transform`,按四阶段执行:
|
||||
1. `BuildInput`
|
||||
- 计算到玩家平面距离
|
||||
- 产出 `EnemyTickWorkItem`
|
||||
- 生成分离输入 `EnemySeparationAgent`
|
||||
2. `Move/Separation`
|
||||
- 计算追踪位移与朝向
|
||||
- 通过 `EnemySeparationSolverProvider.ResolveSimulation(...)` 做互斥求解
|
||||
3. `StateUpdate`
|
||||
- 按距离与可追逐状态更新 `Idle/Chasing/InAttackRange`
|
||||
4. `WriteBack`
|
||||
- 回写 `EnemySimData`(`Position/Forward/Rotation/State`)
|
||||
|
||||
## 互斥求解器双通道(Legacy + Simulation)
|
||||
文件:
|
||||
- `Assets/GameMain/Scripts/Utility/EnemySeperator/IEnemySeparationSolver.cs`
|
||||
- `Assets/GameMain/Scripts/Utility/EnemySeperator/EnemySeparationSolverProvider.cs`
|
||||
- `Assets/GameMain/Scripts/Utility/EnemySeperator/GridBucketEnemySeparationSolver.cs`
|
||||
|
||||
说明:
|
||||
- `SetSimulationAgents/ResolveSimulation`:供 Simulation 纯数据路径调用。
|
||||
- `Register/Unregister/Resolve(Transform, ...)`:保留旧路径兼容与回滚能力。
|
||||
- `GridBucketEnemySeparationSolver` 已加入桶列表复用池(`_bucketListPool`)以降低 GC。
|
||||
|
||||
## 表现层回写(Presentation)规则
|
||||
文件:`Assets/GameMain/Scripts/Simulation/SimulationWorld.Presentation.cs`
|
||||
|
||||
- 仅在 `UseSimulationMovement == true` 时执行
|
||||
- 遍历 `EnemyManager.Enemies`,按 `EntityId` 查找 `EnemySimData`
|
||||
- 写回顺序:
|
||||
- 始终写回 `position`
|
||||
- 优先使用 `rotation`
|
||||
- 若 `rotation` 无效,则回退到 `forward`
|
||||
|
||||
## 与旧移动系统的关系(重要)
|
||||
- `MeleeEnemy` / `RemoteEnemy` 在 `OnUpdate` 开头门控:
|
||||
- 开启 Simulation:直接 `return`
|
||||
- 关闭 Simulation:走旧 `MovementComponent`
|
||||
- 回滚能力来自同一构建内的 `UseSimulationMovement` A/B 开关。
|
||||
|
||||
## Projectile / Pickup 现状
|
||||
- `ProjectileSimData`、`PickupSimData` 已具备容器、绑定与生命周期同步通道
|
||||
- 当前仍未接入独立 Tick 行为,仅完成“创建/回收/索引同步”占位目标
|
||||
|
||||
## P1.5 实测基线(P2 输入)
|
||||
基线文档:`docs/P1.5 Simulation-Supplement.md`
|
||||
|
||||
关键结论:
|
||||
- `TickEnemies GC` 在 `500/1000/1500/2000` 敌人数下均为 `0 KB`
|
||||
- `GC Allocated In Frame` 从 P1 的 `29.5~109.7 KB` 降至 `2.1 KB`
|
||||
- `TickEnemies` 热路径耗时(四阶段合计)对比 P1 降幅约 `22.8%~26.8%`
|
||||
- Android 端评估以 CPU `ms` 为主,`fps` 受 60 上限影响
|
||||
|
||||
## 自动化回归(P1.5 已补)
|
||||
目录:`Assets/Tests/Simulation/EditMode/SimulationWorldTickTests.cs`
|
||||
|
||||
覆盖点:
|
||||
- 敌人追踪玩家
|
||||
- 进入攻击距离后停止移动
|
||||
- 实体移除后的索引 remap 稳定性
|
||||
|
||||
## 后续扩展规范(必须遵守)
|
||||
1. 先扩数据,再扩行为
|
||||
先在 `SimData` 增字段,再改 `EntitySync` 初始化与 `Tick` 逻辑,最后改表现层消费。
|
||||
|
||||
2. 保留 A/B 路径
|
||||
任何迁移都必须可在同一构建内通过开关回退到旧路径。
|
||||
|
||||
3. 生命周期只走 EntitySync
|
||||
禁止在敌人业务代码中手动改写 Simulation 容器,避免双写导致索引错乱。
|
||||
|
||||
4. 维持“逻辑输出 / 表现消费”边界
|
||||
Simulation 只产出逻辑结果,不直接触发 UI、特效、音频事件。
|
||||
|
||||
5. 删除策略统一用 swap-back
|
||||
所有 Simulation 容器删除都必须 remap 索引,严禁 `RemoveAt(i)` 直接删中间项。
|
||||
|
||||
6. 热路径禁用托管分配
|
||||
`TickEnemies`、互斥求解、阶段化循环里禁止 LINQ/临时集合扩张。
|
||||
|
||||
## P2 前的已知技术债
|
||||
- `AttackRange` 目前固定值 `1f`,尚未由配置化数值驱动
|
||||
- `EnemySimData.TargetType/State` 语义仍偏轻量,未形成完整状态机合约
|
||||
- Projectile/Pickup 尚未迁移真实 Tick 行为
|
||||
|
||||
## 提交前检查清单
|
||||
- 是否保持了 `UseSimulationMovement` 关闭时行为不变
|
||||
- 是否保持了 `EntityId <-> SimulationIndex` 一致性(含移除 remap)
|
||||
- 是否避免在 Tick 热路径引入新 GC
|
||||
- 是否将新字段接入了 `EntitySync -> Tick -> Presentation` 全链路
|
||||
- 是否补充了最小回归验证(至少 Battle 循环、敌人移除、索引稳定性)
|
||||
- 是否同步更新本 Skill 文档与 `docs/P1.5 Simulation-Supplement.md`
|
||||
@@ -0,0 +1,72 @@
|
||||
---
|
||||
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`)
|
||||
3. Keep behavior compatibility with current gameplay loop 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.cs`
|
||||
- Selectors:
|
||||
- `../../Assets/GameMain/Scripts/Entity/EntityLogic/Weapon/TargetSelector/`
|
||||
- Weapon data:
|
||||
- `../../Assets/GameMain/Scripts/Entity/EntityData/Weapon/`
|
||||
- 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.
|
||||
- Use safe parsing (`TryParse` + defaults) for runtime weapon parameters.
|
||||
- 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` for strong-typed fields.
|
||||
3. Add `WeaponXxx : WeaponBase` and implement only weapon-specific behavior.
|
||||
4. Build state files under `Weapon/WeaponXxx/` with partial class layout.
|
||||
5. Register display/data mapping path so `ShowWeapon` 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 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.
|
||||
|
||||
## 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.
|
||||
- UI/shop/inventory interactions stay stable after the change.
|
||||
- Update `./references/WeaponDevelopmentSkill.md` if contracts changed.
|
||||
@@ -0,0 +1,4 @@
|
||||
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."
|
||||
@@ -0,0 +1,134 @@
|
||||
# Weapon Development Skill(VampireLike)
|
||||
|
||||
## 目标
|
||||
本文件是 `Entity.Weapon` 体系的开发规范与速查手册。
|
||||
后续新增武器或扩展机制时,优先按本文档执行,避免重复通读历史上下文。
|
||||
|
||||
## 当前架构总览
|
||||
- 武器运行时入口:`WeaponBase`(`Assets/GameMain/Scripts/Entity/EntityLogic/Weapon/WeaponBase.cs`)
|
||||
- 武器具体实现:`WeaponKnife`、`WeaponHandgun`、`WeaponSlash`
|
||||
- 目标选择策略:`ITargetSelector` + `TargetSelectorType`
|
||||
- 攻击可视化:`IWeaponAttackEffect`
|
||||
- 数据入口:`DRWeapon` -> `WeaponData`(及其子类)
|
||||
- 实体生成:`EntityExtension.ShowWeapon`
|
||||
|
||||
## WeaponBase 统一职责(已上收)
|
||||
`WeaponBase` 负责以下通用逻辑,子类不要重复实现:
|
||||
- 生命周期模板:`OnShow/OnHide/OnAttachTo/OnDetachFrom`
|
||||
- 状态机管理:`RegisterState`、`EnsureStatesBuilt`、`TransitionTo`
|
||||
- 通用运行字段:`_target`、`_currAttackTimer`、`_sqrRange`
|
||||
- 启用门控:`OnUpdate` 中统一 `if (!_isEnabled) return`
|
||||
- 目标选择入口:`SelectTarget`、`SetTargetSelector`、`CreateSelector`
|
||||
- 距离判定:`IsInRange`(基于 XZ 平面距离)
|
||||
- 玩家攻击属性订阅:`BindAttackStatFromOwner` / `ReleaseAttackStatSubscription`
|
||||
|
||||
## 状态机约定
|
||||
统一状态枚举:
|
||||
- `Idle`
|
||||
- `Check_OutRange`
|
||||
- `Check_InRange`
|
||||
- `Attack`
|
||||
- `Disabled`(可选)
|
||||
|
||||
推荐流程:
|
||||
1. `Idle`:查找目标,计时器持续累积。
|
||||
2. `Check_OutRange`:有目标但不在攻击范围,继续转向并累积计时。
|
||||
3. `Check_InRange`:在攻击范围内,若 `currAttackTimer >= Cooldown` 切 `Attack`。
|
||||
4. `Attack`:执行攻击,重置计时器,结束后回 `Check_InRange`。
|
||||
|
||||
关键规则:
|
||||
- 即使没有目标,也允许蓄力(计时器持续走)。
|
||||
- 一旦进入 `Check_InRange` 且冷却已满,应立即触发攻击。
|
||||
|
||||
## 3D 场景下的距离/朝向原则
|
||||
- 射程与目标筛选:优先使用 XZ 平面距离(忽略 Y),调用 `AIUtility.GetSqrMagnitudeXZ`。
|
||||
- 视觉朝向与弹道:可按武器设计决定是否使用完整 3D 向量。
|
||||
- `WeaponHandgun`:允许俯仰瞄准,逻辑上用射线命中对象判定伤害。
|
||||
- 近战地面范围类(Knife/Slash):伤害检测建议投影到地面(XZ)再判定。
|
||||
|
||||
## 目标选择策略规范
|
||||
接口:`ITargetSelector.SelectTarget(WeaponBase weapon, IEnumerable<EntityBase> candidates, float maxSqrRange)`
|
||||
|
||||
现有策略:
|
||||
- `NearestTargetSelector`
|
||||
- `HighestHealthTargetSelector`
|
||||
- `LowestHealthTargetSelector`
|
||||
|
||||
语义约定:
|
||||
- `HighestHealth` / `LowestHealth` 必须按“当前血量”筛选。
|
||||
- 当前实现读取 `HealthComponent.CurrentHealth`。
|
||||
|
||||
扩展策略步骤:
|
||||
1. 新建 selector 类并实现 `ITargetSelector`。
|
||||
2. 更新 `TargetSelectorType` 枚举。
|
||||
3. 在 `WeaponBase.CreateSelector` 中注册。
|
||||
4. 武器在 `OnWeaponShow` 或构造阶段选择策略。
|
||||
|
||||
## 攻击可视化效果规范
|
||||
接口:`IWeaponAttackEffect.Play(WeaponBase weapon, Vector3 position, EntityBase target, float radius)`
|
||||
|
||||
约定:
|
||||
- 可视化逻辑与伤害逻辑解耦。
|
||||
- 武器类只负责触发 `Play`,不把可视化细节塞回武器核心逻辑。
|
||||
- 当前阶段允许临时对象创建;后续若有性能压力再统一对象池化。
|
||||
|
||||
## 数据层规范(DRWeapon / WeaponData)
|
||||
`DRWeapon` 提供通用字段:
|
||||
- `Attack`、`Cooldown`、`AttackRange`、`AttackSoundId`
|
||||
- `Pramas`(字典,Key 建议统一转小写)
|
||||
- `Modifiers`
|
||||
|
||||
约定:
|
||||
- 参数解析尽量在数据层/初始化阶段完成。
|
||||
- 武器逻辑层读取 `WeaponData` 的强类型结果,不要散落 `Parse`。
|
||||
- 解析时必须容错:优先 `TryParse` + 默认值,避免运行时异常。
|
||||
|
||||
## 新增武器标准流程
|
||||
1. 定义枚举
|
||||
- 更新 `WeaponType`(保持递增值,避免重排已有值)。
|
||||
|
||||
2. 建立数据类
|
||||
- 新建 `WeaponXxxData : WeaponData`。
|
||||
- 如果有独有参数,提供强类型字段或统一初始化逻辑。
|
||||
|
||||
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. 若为远程武器,建立子弹实体
|
||||
- `BulletXxx : Bullet`
|
||||
- `BulletXxxData : BulletData`
|
||||
- 通过武器赋予伤害/阵营参数。
|
||||
- 自动销毁应走对象池回收。
|
||||
|
||||
7. 接入实体展示与数据表
|
||||
- 确保 `DRWeapon` / `DREntity` 配置齐全。
|
||||
- `EntityExtension.ShowWeapon` 可正确映射到 `Entity.Weapon.WeaponXxx`。
|
||||
|
||||
8. 联动系统
|
||||
- 背包、商店购买/出售、UI 展示、事件流刷新。
|
||||
|
||||
## 代码检查清单(提交前)
|
||||
- 是否重复实现了 `WeaponBase` 已有通用逻辑。
|
||||
- 状态流转是否会卡死或漏转场。
|
||||
- 冷却计时是否在无目标时仍正常累积。
|
||||
- 目标失效(null/Unavailable/死亡)是否安全处理。
|
||||
- 命中检测是否符合武器设计(地面范围/射线/扇形)。
|
||||
- 数据参数是否全部容错解析。
|
||||
- 可视化是否与伤害逻辑解耦。
|
||||
- 是否正确订阅/解绑攻击属性(或复用基类方法)。
|
||||
|
||||
## 已知注意点
|
||||
- 目录中存在 `Pramas` 命名拼写历史包袱,保持兼容即可。
|
||||
- 文档中的“规范”优先级高于历史实现;历史实现若偏离,按本规范逐步收敛。
|
||||
Reference in New Issue
Block a user