Compare commits

..

2 Commits

Author SHA1 Message Date
SepComet c111170bb0 清理项目目录并进行一次打包验证 2026-06-25 20:47:15 +08:00
SepComet cc8982b131 推进计时器逻辑的集中管理
- 将敌人的攻击计时任务放到基类中进行创建
- 将 EnemySpawnScheduler 敌人生成调度器的刷怪计时任务迁移到 TimerComponent
2026-06-23 20:42:24 +08:00
30 changed files with 154 additions and 1247 deletions

View File

@ -7,8 +7,8 @@
<AssetBundleCompression>1</AssetBundleCompression>
<CompressionHelperTypeName>UnityGameFramework.Runtime.DefaultCompressionHelper</CompressionHelperTypeName>
<AdditionalCompressionSelected>False</AdditionalCompressionSelected>
<ForceRebuildAssetBundleSelected>True</ForceRebuildAssetBundleSelected>
<BuildEventHandlerTypeName>VampireLike.Editor.VampireLikeBuildEventHandler</BuildEventHandlerTypeName>
<ForceRebuildAssetBundleSelected>False</ForceRebuildAssetBundleSelected>
<BuildEventHandlerTypeName>SepCore.Editor.DefaultBuildEventHandler</BuildEventHandlerTypeName>
<OutputDirectory>D:/Learn/GameLearn/UnityProjects/VampireLike/bin/AssetBundles</OutputDirectory>
<OutputPackageSelected>True</OutputPackageSelected>
<OutputFullSelected>True</OutputFullSelected>

View File

@ -31,20 +31,27 @@
<Assets>
<Asset Guid="0080b46050fc460cb6d6c7fc8d4e8c27" ResourceName="DataTables" />
<Asset Guid="0179316b5fc7c2946a67c5877c02fc30" ResourceName="UI/UISprites/Common" />
<Asset Guid="04d7dde7615d71b4db1a0c8d67a62e95" ResourceName="UI/UIForms" />
<Asset Guid="037baa35a19f9e64ea778ab74b0d48eb" ResourceName="UI/UISprites/Icons" />
<Asset Guid="03a4e96650b651d49b7cb120872f7d61" ResourceName="UI/UISprites/Icons" />
<Asset Guid="04dbc0581071c254ea6564b2ff06ff9b" ResourceName="Textures" />
<Asset Guid="04e1c2b9cc1f8484cbf5533473f55a2c" ResourceName="UI/UISprites/Icons" />
<Asset Guid="0722cd253d6bf014eb4134a2151ec7e3" ResourceName="UI/UIItems" />
<Asset Guid="08c97a5a0cf01ff469525a635a4a1eb1" ResourceName="UI/UISprites/Icons" />
<Asset Guid="093f8873cfe371d41b854ed9fb6bff69" ResourceName="Music/About" />
<Asset Guid="0963e6c65b2b1f74d9f455e21901e2dc" ResourceName="Textures" />
<Asset Guid="0a65a68c01a76ea4b8b574827a6467aa" ResourceName="UI/UISprites/Common" />
<Asset Guid="0b6c5dfe0bd1cc34497e98040ca981e1" ResourceName="UI/UISprites/Icons" />
<Asset Guid="0e45b8557b1831845a6e03fce7b5aa2b" ResourceName="SceneSettings" />
<Asset Guid="0ed73dc47f4cb38489020d05e9f02c99" ResourceName="Materials" />
<Asset Guid="0f995b3145e0e7247a42da6cef1dbf23" ResourceName="Materials" />
<Asset Guid="1046dcb12e547564d8b54bd15419a787" ResourceName="Entities" />
<Asset Guid="1053b0070685be347ab58587156842dc" ResourceName="Localization/Dictionaries" ResourceVariant="zh-tw" />
<Asset Guid="11143001bcbdc864b8d8fe2083142e5a" ResourceName="Entities" />
<Asset Guid="1125080c72a9ace40b258e8df7695362" ResourceName="UI/UISprites/Icons" />
<Asset Guid="121c37ab385fb0248bdf75e663ac8e8e" ResourceName="UI/UISprites/Icons" />
<Asset Guid="1478894bc9a1ed241b05b0862a7b8bce" ResourceName="Textures" />
<Asset Guid="14869ac0d4433f04db1704e39d03412e" ResourceName="Localization/Dictionaries" ResourceVariant="en-us" />
<Asset Guid="14fe2dc326b7c78458bc404b6a9c791c" ResourceName="UI/UIForms" />
<Asset Guid="156d241f796508c4da4fc354a7fbf5a8" ResourceName="UI/UISprites/Common" />
<Asset Guid="185f97f18bd603a478461ce9c08bd039" ResourceName="Materials" />
<Asset Guid="18dc0cd2c080841dea60987a38ce93fa" ResourceName="URPAssets" />
@ -57,24 +64,30 @@
<Asset Guid="1d46a17a95a444c08830612bc1146d1d" ResourceName="Materials" />
<Asset Guid="1e0350b97c61bfb4a91c62d756d01727" ResourceName="Materials" />
<Asset Guid="1eb120084e9602c4da979658e08c63e4" ResourceName="UI/UISprites/Icons" />
<Asset Guid="1f84d60b84fd40b49bc5033530a7e24a" ResourceName="UI/UISprites/Icons" />
<Asset Guid="20e5dc75d1c3fe74bb841127a036d69d" ResourceName="UI/UIForms" />
<Asset Guid="20ebdccacd18e7c45a8c10c46cddb3d7" ResourceName="UI/UISprites/Icons" />
<Asset Guid="2257393dd6ed51a49a6c3fd178382b59" ResourceName="Materials" />
<Asset Guid="236433a27ce40a7429b87d14d8fa3035" ResourceName="UI/UISprites/Common" />
<Asset Guid="24ca310f92a6796408f1db7647ec4e55" ResourceName="UI/UISprites/Common" />
<Asset Guid="25229adaac96caa42a737b60465f3229" ResourceName="Fonts" />
<Asset Guid="261cde3315e1f0e41a28d6477fa3d434" ResourceName="UI/UISprites/Icons" />
<Asset Guid="2768a9a4c8d289840918dcb879705893" ResourceName="Meshes" />
<Asset Guid="28988b1366cba3e4b947bb60d6b118f9" ResourceName="DataTables" />
<Asset Guid="28c42f88cfe56e84e95c0d7545db8c1b" ResourceName="Materials" />
<Asset Guid="2cb5eef4d7d7bf6459dd13a3f8d90246" ResourceName="Textures" />
<Asset Guid="2d1b2cc11a8e99f42b34265765fc6d86" ResourceName="Entities" />
<Asset Guid="2ed59bf0745586248aaa89cf7d3305a7" ResourceName="Entities" />
<Asset Guid="2fbbf1c667136af4b9dd4f823f897378" ResourceName="UI/UISprites/Icons" />
<Asset Guid="308577c72eb4cd14ca676aeee62b0ea5" ResourceName="UI/UIItems" />
<Asset Guid="312264fc6c05ca04499685cc989e533d" ResourceName="UI/UISprites/Icons" />
<Asset Guid="315e8ed2db27c254cb3366ff0793cd90" ResourceName="DataTables" />
<Asset Guid="352da872791696c48af3b21132e3e3c3" ResourceName="DataTables" />
<Asset Guid="372a8b1e52bedc64b9207b12d167afaa" ResourceName="DataTables" />
<Asset Guid="39e7c07f139dbb3438fc8b55662778d5" ResourceName="UI/UISprites/Icons" />
<Asset Guid="3aa539b1e46111d4299a83c73ebe762c" ResourceName="UI/UIForms" />
<Asset Guid="3be3fac3611f4584695662c4889f722d" ResourceName="UI/UISprites/Common" />
<Asset Guid="3cda1857d670c6340adb393eda40cd3e" ResourceName="UI/UISprites/Icons" />
<Asset Guid="3dc7455402dfa462b89a4bbd513955e9" ResourceName="Sounds" />
<Asset Guid="3e504a46a8fcec34db3c4776530c6eb2" ResourceName="Textures" />
<Asset Guid="3f54fcfdac53aec42ae18a6a1c6d8cb1" ResourceName="UI/UISprites/Common" />
@ -82,26 +95,35 @@
<Asset Guid="4209bea5e51753141ae060017f42afdc" ResourceName="UI/UIForms" />
<Asset Guid="429ed03405bf8854eab46552b7470ac0" ResourceName="Materials" />
<Asset Guid="4473d81b14ddb0143addf0e6050d8491" ResourceName="Scenes" />
<Asset Guid="44b2eb1016babff49866138a705af9f6" ResourceName="UI/UISprites/Icons" />
<Asset Guid="44c8db52241385c45bbb14a1718f17bf" ResourceName="Configs" />
<Asset Guid="44cfa1c448225554c961ad6eb667d80b" ResourceName="DataTables" />
<Asset Guid="4b1e2f50f0632d9478c096a9ad7b5509" ResourceName="UI/UISprites/Icons" />
<Asset Guid="4c3865b2ac420cd46a9cde6ab468d016" ResourceName="Materials" />
<Asset Guid="4ca22ae3bc068c84eb7858d5b9bdf3e2" ResourceName="Fonts" />
<Asset Guid="4f688097e85071841a2c3ba165000c20" ResourceName="Textures" />
<Asset Guid="5282f20eba4d44213820e21af8481932" ResourceName="Meshes" />
<Asset Guid="5461b0fc87a2ab04fbcfd898d18f6107" ResourceName="Configs" />
<Asset Guid="56142b46ec15fe74b85ca4fdd3324751" ResourceName="UI/UIForms" />
<Asset Guid="56b8df63bbad60749a69e38bc687fadf" ResourceName="UI/UISprites/Common" />
<Asset Guid="578af1667322bab45b652b79a40bb4fb" ResourceName="Materials" />
<Asset Guid="583ff7026dac91849b7c7b2468ba456b" ResourceName="Materials" />
<Asset Guid="598cee40dd61c3349acd508f5c2c92c1" ResourceName="UI/UISprites/Icons" />
<Asset Guid="5b5a6a737c460eb4abc105d6583d405e" ResourceName="Fonts" />
<Asset Guid="5dcd89912e222bf4c87f76db4044bc5e" ResourceName="Localization/Dictionaries" ResourceVariant="ko-kr" />
<Asset Guid="5ebb46af6f16ae94e87f64a7dc0a49cb" ResourceName="Entities" />
<Asset Guid="62242e716ff2d274da787fe558f06b06" ResourceName="UI/UISprites/Icons" />
<Asset Guid="62af9e5c8f39cfa49af9e10ccf42f1da" ResourceName="UI/UISprites/Common" />
<Asset Guid="632ea7a45da70a34984e1280b03fc579" ResourceName="UI/UISprites/Icons" />
<Asset Guid="638ff8ae4a0d15047839cd265d3bc296" ResourceName="Music/Background" />
<Asset Guid="63fe6ff9ab9e1433f8db4ebd940f2442" ResourceName="Materials" />
<Asset Guid="64c7bcc7087b03d4b9bc0ac32f9b2b48" ResourceName="UI/UISprites/Icons" />
<Asset Guid="65a506fc3ca171347ac74203416a3d7f" ResourceName="UI/UISprites/Icons" />
<Asset Guid="65ffa78c2e8cbb346826cc2f3a7b6ea4" ResourceName="DataTables" />
<Asset Guid="66261723fe421be498ce5957449fc972" ResourceName="Scenes" />
<Asset Guid="68729f225481f3a49b1daacf924dd109" ResourceName="UI/UISprites/Icons" />
<Asset Guid="6a58ea03d3bd54e428d969b85afa5680" ResourceName="Fonts" />
<Asset Guid="6c80f46064f8e74429d9fdc5190abfb0" ResourceName="UI/UISprites/Icons" />
<Asset Guid="6cbc2c323b77f804fb958fa4eca33998" ResourceName="UI/UISprites/Common" />
<Asset Guid="6d9b42ac01f24bf4d98923573f103575" ResourceName="Textures" />
<Asset Guid="6db0c8354d868834abf29840037591b1" ResourceName="Textures" />
@ -111,10 +133,12 @@
<Asset Guid="6e5d026bf0652ed4380f6a66f4aa26c5" ResourceName="Textures" />
<Asset Guid="707360a9c581a4bd7aa53bfeb1429f71" ResourceName="URPAssets" />
<Asset Guid="71646ebaae78d43aeb8b53cacdb69671" ResourceName="Textures" />
<Asset Guid="717aeb9eec580f74b8ba8c4e2b657efb" ResourceName="UI/UISprites/Icons" />
<Asset Guid="72e76810224064300b7d32e8322a5d12" ResourceName="Sounds" />
<Asset Guid="74de26a1b47fa13468ea72894a720176" ResourceName="UI/UIForms" />
<Asset Guid="77cbb7c5404c10242ab59953e0746314" ResourceName="Fonts" />
<Asset Guid="78595a129d0438c44b2d52720e9c0edd" ResourceName="UI/UIItems" />
<Asset Guid="7aac48e4d5b44884b8cafa4de4e489ef" ResourceName="UI/UISprites/Icons" />
<Asset Guid="7b7fd9122c28c4d15b667c7040e3b3fd" ResourceName="URPAssets" />
<Asset Guid="7e6ecd46ba625fb43ab2b4bc52dcb3c1" ResourceName="DataTables" />
<Asset Guid="7e91cd9bad7babf4b975882a4b7453cb" ResourceName="Textures" />
@ -122,17 +146,24 @@
<Asset Guid="7f1361f834115be4ea4ec2387c67334e" ResourceName="Fonts" />
<Asset Guid="7f5aee8da226edf4991598327cb32ce0" ResourceName="UI/UISprites/Logos" />
<Asset Guid="7fd11dc5d29076d469d414dec2818f11" ResourceName="Configs" />
<Asset Guid="800f7cb88c9f452479b6ad9576c7b25d" ResourceName="UI/UISprites/Icons" />
<Asset Guid="836be25be3e1e8c41ae5545bc8a9a4d7" ResourceName="Textures" />
<Asset Guid="83e5fce2d1e5b3e4ab6551de03cc9c22" ResourceName="Materials" />
<Asset Guid="85c9ccd84e0717f4dba80fd64bc9fb53" ResourceName="UI/UISprites/Icons" />
<Asset Guid="8940b037a9b441c4cbd3d2b446838424" ResourceName="Materials" />
<Asset Guid="8e27380ee68aa4a219b4db9018e7da31" ResourceName="Materials" />
<Asset Guid="903fe490f7ddf7b489e01f02c7983c79" ResourceName="UI/UISprites/Icons" />
<Asset Guid="91b565625868b59428114c4a5b945e80" ResourceName="UI/UISprites/SelectRoleFormRT" />
<Asset Guid="92e207ca452785d478c7a2994d0c8351" ResourceName="UI/UISprites/Icons" />
<Asset Guid="92e9102fc83c8e949971efe6f1fa6501" ResourceName="UI/UIForms" />
<Asset Guid="9400f2349c8b48a4b9fa86bd63f169cb" ResourceName="UI/UISprites/Icons" />
<Asset Guid="9538765dceb3e9248aec104646e501ad" ResourceName="UI/UISprites/Icons" />
<Asset Guid="955000b4d1441470e8cbf94f483228b5" ResourceName="Materials" />
<Asset Guid="97b1f8b25cca2bc458cb9d6127c8d186" ResourceName="Materials" />
<Asset Guid="99d811b0183246646a2ce8df996f4bca" ResourceName="Fonts" />
<Asset Guid="9ab9adabcf369c44e89076c7e5b454ba" ResourceName="UI/UISprites/Icons" />
<Asset Guid="9ad79a3e50af5244eb233da7e4f21233" ResourceName="UI/UISprites/Icons" />
<Asset Guid="9afa958d6d8235941b9badb42aae4370" ResourceName="Meshes" />
<Asset Guid="9bc1d62ccbd7a754f9612f310b40be8c" ResourceName="UI/UISprites/Icons" />
<Asset Guid="9be2e1e45f4edd74c8764538ad306b78" ResourceName="Localization/Dictionaries" ResourceVariant="zh-cn" />
<Asset Guid="9d193ac5b4294e0e9ba6e867320944b7" ResourceName="Entities" />
<Asset Guid="9ddab293e2a8af3499dac05f5fd6169c" ResourceName="Meshes" />
@ -140,55 +171,82 @@
<Asset Guid="9f847ec5e66e03e4ead1d3c5f7b510e8" ResourceName="UI/UISprites/Common" />
<Asset Guid="a019ae3af8e864616b85773c509f5285" ResourceName="Sounds" />
<Asset Guid="a23eef5e20ff8cb46adf33491fc443fb" ResourceName="Materials" />
<Asset Guid="a266178dac3c9e448ae7bf85617cfd6a" ResourceName="UI/UISprites/Icons" />
<Asset Guid="a4b74a0ecf03ffb47b0b0e5dfa8ca35f" ResourceName="UI/UISprites/Icons" />
<Asset Guid="a6560a915ef98420e9faacc1c7438823" ResourceName="URPAssets" />
<Asset Guid="a6ac41115088b5946bc544429260c997" ResourceName="Entities" />
<Asset Guid="a71f8bb1b1b2c51438e2bafc884cb02c" ResourceName="DataTables" />
<Asset Guid="a76ff56683a2bc2479ebd63dcdd658b0" ResourceName="UI/UIItems" />
<Asset Guid="a77acddad3f780148b867a6ccdfe95b8" ResourceName="UI/UISprites/Icons" />
<Asset Guid="a7b030cffa2dc44478c14e49a22771c2" ResourceName="Materials" />
<Asset Guid="a8c07bbe04fdaf04b80e27f651a8edd6" ResourceName="UI/UISprites/Common" />
<Asset Guid="a9be0531a19af934e97a1f3b47b78872" ResourceName="UI/UISprites/Common" />
<Asset Guid="aa90f2691281f1e429893fabd7656264" ResourceName="UI/UISprites/Icons" />
<Asset Guid="ab01163442b94ad4794a345dd5a49ec9" ResourceName="UI/UISprites/Icons" />
<Asset Guid="ab1c66f8a301fe043930c9f0c23716cf" ResourceName="Fonts" />
<Asset Guid="ab45c3f613f388d43bbf43ec05eb92e2" ResourceName="UI/UISprites/Common" />
<Asset Guid="ab4774faf71b57a40a29a9e902403fe3" ResourceName="DataTables" />
<Asset Guid="abb5b3b95f1a42c46a251da378cbeace" ResourceName="Entities" />
<Asset Guid="adad702a675d15945aa01e882420d653" ResourceName="UI/UISprites/Icons" />
<Asset Guid="ae6a7f967521769458b0913fa39caaf4" ResourceName="Textures" />
<Asset Guid="b031b4f2980561542a7f7ba41391edc3" ResourceName="Scenes" />
<Asset Guid="b0c7cf51d3fecb446ab93bf854419715" ResourceName="Materials" />
<Asset Guid="b6539f2b0fa989d4fb7e057799b9260d" ResourceName="Fonts" />
<Asset Guid="b6f06390b8e233345b9a089681f14b81" ResourceName="Entities" />
<Asset Guid="b8848e672f7437f42a6285a4fe499afb" ResourceName="UI/UIForms" />
<Asset Guid="b9611b00d258c1c40adf809817828ec1" ResourceName="UI/UISprites/Icons" />
<Asset Guid="ba157ba55f72c424a9e88f3c029997c4" ResourceName="Textures" />
<Asset Guid="babce70282f41c6419b2e1807532b375" ResourceName="UI/UISprites/Icons" />
<Asset Guid="baedbbad82997f445a8cb4da210404e0" ResourceName="Meshes" />
<Asset Guid="bbfd75fe6fe00e1448fe988173ede7f9" ResourceName="UI/UIForms" />
<Asset Guid="bc065bcf1474d7d4387fafd202678c37" ResourceName="Fonts" />
<Asset Guid="bf75b984df8a84987bcf3a8bf6e2862d" ResourceName="Sounds" />
<Asset Guid="c27d1aa86b4c7054a82432a9d71e5f85" ResourceName="UI/UISprites/Icons" />
<Asset Guid="c3a86ff7445c9c94cbf24ef544cecf4c" ResourceName="UI/UISprites/Icons" />
<Asset Guid="c3f12cb3b19f78748bc2a90894790dd4" ResourceName="UI/UISprites/Icons" />
<Asset Guid="c40be3174f62c4acf8c1216858c64956" ResourceName="URPAssets" />
<Asset Guid="c49cffd4fc1dfb549b2b30448a0becda" ResourceName="UI/UISprites/Icons" />
<Asset Guid="c4f37184fcb9306428d7d002f7dca96d" ResourceName="Materials" />
<Asset Guid="c547624e174de984882f0a14b4bb32e1" ResourceName="Materials" />
<Asset Guid="c58c9afddbd36d14d837fa218d772996" ResourceName="Materials" />
<Asset Guid="c7d1e11dd37634b48a9dd4012b8e4306" ResourceName="UI/UISounds" />
<Asset Guid="c972fe1ec8a1e0b4292f01575b0b3898" ResourceName="UI/UISprites/Icons" />
<Asset Guid="caa829ab2ffc71340a69253afdf58365" ResourceName="Music/Menu" />
<Asset Guid="cadd0764941c8b646ae79b51d0ea8285" ResourceName="UI/UISprites/Common" />
<Asset Guid="cd819288654317a49853341f3f17ee50" ResourceName="UI/UISprites/Icons" />
<Asset Guid="ce3f3526692a08a489042130fa47039e" ResourceName="UI/UISprites/Icons" />
<Asset Guid="ce8bbe245fa50b74bac0eea7bff0dc3a" ResourceName="UI/UIItems" />
<Asset Guid="cf05d7e458e9c4c4b8e78442e9277815" ResourceName="UI/UISprites/Icons" />
<Asset Guid="cfe53cf384344bd47a8680f8c5f97a7b" ResourceName="DataTables" />
<Asset Guid="d0e2fc18fe036412f8223b3b3d9ad574" ResourceName="URPAssets" />
<Asset Guid="d39d823848837064f84424b6621a3e6b" ResourceName="UI/UISprites/Icons" />
<Asset Guid="d3bdbf0dec5b2864f902c43899017cbc" ResourceName="UI/UISprites/Icons" />
<Asset Guid="d5396f61766946c4db970694836a49fc" ResourceName="UI/UIItems" />
<Asset Guid="d607728ea8898a048bd3beba30fe5853" ResourceName="UI/UISprites/Icons" />
<Asset Guid="d9808401c68af274a8edfbed3d1b53c7" ResourceName="Meshes" />
<Asset Guid="db58965402f12ed47b4dad61a9e48c9d" ResourceName="UI/UISprites/Icons" />
<Asset Guid="dd972bed1926aa4489426be921651ec5" ResourceName="UI/UISprites/Icons" />
<Asset Guid="e03343286e97c824c8e1595b0ccb1000" ResourceName="UI/UISprites/Icons" />
<Asset Guid="e1260c1148f6143b28bae5ace5e9c5d1" ResourceName="URPAssets" />
<Asset Guid="e198b017eb054ef41879410763964076" ResourceName="UI/UISprites/Icons" />
<Asset Guid="e3547eea3b25dbc4bb1170421633a004" ResourceName="UI/UISprites/Icons" />
<Asset Guid="e3616cef1ab22d642903eb59f34feea1" ResourceName="Entities" />
<Asset Guid="e5ca26c53b6ab8a46b52817008d7c7fa" ResourceName="UI/UISprites/Icons" />
<Asset Guid="e634585d5c4544dd297acaee93dc2beb" ResourceName="URPAssets" />
<Asset Guid="e82837c9099f69a48b48fc44eb8d119d" ResourceName="UI/UISprites/Common" />
<Asset Guid="e830964cdb85ff3429bca484c16bab44" ResourceName="UI/UISounds" />
<Asset Guid="e85864330b68dde498dcb6e8711815d3" ResourceName="UI/UISprites/Common" />
<Asset Guid="e8904198a64b17443b96e2bf83d7055b" ResourceName="UI/UISprites/Icons" />
<Asset Guid="e8def20f1612e8844b8f0dda497fa5ba" ResourceName="DataTables" />
<Asset Guid="eabb37cb6d738b443b398b701a64cd88" ResourceName="Textures" />
<Asset Guid="edfd1e0fa2ccc4b4e94879d87eada2f7" ResourceName="UI/UISprites/Icons" />
<Asset Guid="f06c2627a191cc1468383483fbafca04" ResourceName="Fonts" />
<Asset Guid="f17bc7e2694233349a8b22e96c5c43de" ResourceName="Materials" />
<Asset Guid="f2f4df0b7211e4c42a5638273525d8ee" ResourceName="UI/UISprites/Common" />
<Asset Guid="f2f97a713beae744181ba934f9c4113a" ResourceName="DataTables" />
<Asset Guid="f319082377c69d74484c7fa612e50cf8" ResourceName="UI/UISprites/Icons" />
<Asset Guid="f36c72c738c55f741afcd674a0b1245f" ResourceName="Materials" />
<Asset Guid="f3e6e933f81f117478eed5a4baccbc46" ResourceName="UI/UISprites/Icons" />
<Asset Guid="f438a72a91e1c3c4a9ced40888ffec96" ResourceName="UI/UISprites/Icons" />
<Asset Guid="f5b2cc6c7ce8cc5408ad507a4f40af49" ResourceName="Fonts" />
<Asset Guid="f6bd6a95990ae92469c5c9ab4293064f" ResourceName="UI/UIForms" />

View File

@ -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();

View File

@ -58,6 +58,7 @@ namespace SepCore.EnemyManager
_spawnCts?.Dispose();
_spawnCts = null;
_enemyRegistry = null;
_spawnScheduler?.Reset();
_spawnScheduler = null;
_entity = null;
}
@ -71,7 +72,7 @@ namespace SepCore.EnemyManager
_duration = _baseDuration;
_currentLevel = level.Id;
_spawnScheduler.Init(level);
_spawnScheduler.Init(level, OnWaveSpawn);
_enemyRegistry.Clear();
_currentSpawnEnemyId = 0;
}
@ -79,13 +80,14 @@ namespace SepCore.EnemyManager
public void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
_enemyRegistry.PruneInvalidEntries();
var spawnRequests = _spawnScheduler.Tick(elapseSeconds);
foreach (var request in spawnRequests)
{
for (int j = 0; j < request.Count; j++)
{
SpawnEnemyAsync(request.EnemyType).Forget();
_spawnScheduler.Tick(elapseSeconds);
}
private void OnWaveSpawn(EnemyType enemyType, int count)
{
for (int j = 0; j < count; j++)
{
SpawnEnemyAsync(enemyType).Forget();
}
}

View File

@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
using SepCore.DataTable;
using SepCore.Definition;
using SepCore.Timer;
using UnityEngine;
namespace SepCore.EnemyManager
@ -12,98 +12,67 @@ namespace SepCore.EnemyManager
private float _elapsedTime;
private float[] _baseIntervals;
private float[] _currentIntervals;
private EnemyType[] _enemyTypes;
private int[] _spawnCounts;
private float[] _nextSpawnTimes;
private TimerHandle[] _waveHandles;
private float _spawnRateScale = 1f;
private Action<EnemyType, int> _onSpawn;
public float SpawnRateScale => _spawnRateScale;
public float ElapsedTime => _elapsedTime;
public int WaveCount => _enemyTypes?.Length ?? 0;
public void Init(DRLevel level)
public void Init(DRLevel level, Action<EnemyType, int> onSpawn)
{
GameEntry.Timer.CancelByOwner(this);
_onSpawn = onSpawn;
_baseIntervals = (float[])level.Intervals.Clone();
_currentIntervals = (float[])_baseIntervals.Clone();
_enemyTypes = level.EntityTypes;
_spawnCounts = level.EntityCounts;
_elapsedTime = 0f;
SetSpawnRateScale(_spawnRateScale);
_waveHandles = new TimerHandle[_baseIntervals.Length];
for (int i = 0; i < _baseIntervals.Length; i++)
{
int waveIndex = i;
float interval = GetScaledInterval(_baseIntervals[i], _spawnRateScale);
_waveHandles[i] = GameEntry.Timer.ScheduleRepeat(interval, () => OnWaveTick(waveIndex), -1, this);
}
}
public List<SpawnRequest> Tick(float deltaTime)
public void Tick(float deltaTime)
{
_elapsedTime += deltaTime;
var requests = new List<SpawnRequest>();
if (_nextSpawnTimes == null) return requests;
for (int i = 0; i < _nextSpawnTimes.Length; i++)
{
if (_elapsedTime < _nextSpawnTimes[i]) continue;
requests.Add(new SpawnRequest
{
EnemyType = _enemyTypes[i],
Count = _spawnCounts[i],
WaveIndex = i
});
_nextSpawnTimes[i] += _currentIntervals[i];
}
return requests;
}
public void SetSpawnRateScale(float scale)
{
float newScale = Mathf.Max(MinSpawnRateScale, scale);
if (_baseIntervals == null || _baseIntervals.Length == 0)
{
_spawnRateScale = newScale;
return;
}
_spawnRateScale = Mathf.Max(MinSpawnRateScale, scale);
if (_waveHandles == null) return;
bool hasRuntimeState = _nextSpawnTimes != null && _currentIntervals != null &&
_nextSpawnTimes.Length == _baseIntervals.Length &&
_currentIntervals.Length == _baseIntervals.Length;
float oldScale = _spawnRateScale;
_spawnRateScale = newScale;
if (!hasRuntimeState)
for (int i = 0; i < _waveHandles.Length; i++)
{
for (int i = 0; i < _baseIntervals.Length; i++)
{
_currentIntervals[i] = GetScaledInterval(_baseIntervals[i], _spawnRateScale);
}
_nextSpawnTimes = (float[])_currentIntervals.Clone();
return;
}
for (int i = 0; i < _baseIntervals.Length; i++)
{
float oldInterval = GetScaledInterval(_baseIntervals[i], oldScale);
float newInterval = GetScaledInterval(_baseIntervals[i], _spawnRateScale);
float remainTime = Mathf.Max(0f, _nextSpawnTimes[i] - _elapsedTime);
float remainRatio = oldInterval > Mathf.Epsilon ? Mathf.Clamp01(remainTime / oldInterval) : 0f;
_currentIntervals[i] = newInterval;
_nextSpawnTimes[i] = _elapsedTime + newInterval * remainRatio;
GameEntry.Timer.SetInterval(_waveHandles[i], newInterval, adjustRemainingTime: true);
}
}
public void Reset()
{
GameEntry.Timer.CancelByOwner(this);
_elapsedTime = 0;
_baseIntervals = null;
_currentIntervals = null;
_enemyTypes = null;
_spawnCounts = null;
_nextSpawnTimes = null;
_waveHandles = null;
_spawnRateScale = 1f;
_onSpawn = null;
}
private void OnWaveTick(int waveIndex)
{
_onSpawn?.Invoke(_enemyTypes[waveIndex], _spawnCounts[waveIndex]);
}
private static float GetScaledInterval(float baseInterval, float scale)
@ -112,11 +81,4 @@ namespace SepCore.EnemyManager
return baseInterval / safeScale;
}
}
public struct SpawnRequest
{
public EnemyType EnemyType;
public int Count;
public int WaveIndex;
}
}

View File

@ -2,6 +2,7 @@
using SepCore.AsyncTask;
using SepCore.Definition;
using SepCore.Entity;
using SepCore.Timer;
using UnityEngine;
public abstract class EnemyBase : TargetableObject
@ -15,6 +16,29 @@ public abstract class EnemyBase : TargetableObject
protected EnemyData _enemyData;
protected bool _canAttack;
protected TimerHandle _attackTimerHandle;
protected void StartAttackCooldown(float cooldown)
{
_canAttack = false;
_attackTimerHandle = GameEntry.Timer.ScheduleOnce(cooldown, () => _canAttack = true, this);
}
protected void ResetAttackCooldown(float cooldown)
{
GameEntry.Timer.Cancel(_attackTimerHandle);
_canAttack = false;
_attackTimerHandle = GameEntry.Timer.ScheduleOnce(cooldown, () => _canAttack = true, this);
}
protected void CancelAttackCooldown()
{
GameEntry.Timer.Cancel(_attackTimerHandle);
_attackTimerHandle = TimerHandle.Invalid;
_canAttack = false;
}
protected override void OnShow(object userData)
{
base.OnShow(userData);

View File

@ -1,7 +1,6 @@
using SepCore.Components;
using SepCore.CustomUtility;
using SepCore.Definition;
using SepCore.Timer;
using UnityEngine;
using UnityGameFramework.Runtime;
@ -24,8 +23,6 @@ namespace SepCore.Entity
private int _attackDamage = 1;
private float _sqrAttackRange;
private TimerHandle _attackTimerHandle;
private bool _canAttack;
private AttackStateType _attackState = AttackStateType.Idle;
@ -71,8 +68,7 @@ namespace SepCore.Entity
_attackDamage = Mathf.Max(1, _meleeEnemyData.AttackDamage);
_sqrAttackRange = _attackRange * _attackRange;
_canAttack = false;
_attackTimerHandle = GameEntry.Timer.ScheduleOnce(_attackCooldown, () => _canAttack = true, this);
StartAttackCooldown(_attackCooldown);
_attackState = AttackStateType.Idle;
_targetableTarget = null;
@ -96,8 +92,7 @@ namespace SepCore.Entity
_movementComponent.OnReset();
_healthComponent.OnReset();
_targetableTarget = null;
GameEntry.Timer.Cancel(_attackTimerHandle);
_attackTimerHandle = TimerHandle.Invalid;
CancelAttackCooldown();
_attackState = AttackStateType.Idle;
base.OnHide(isShutdown, userData);
@ -155,8 +150,7 @@ namespace SepCore.Entity
if (_canAttack)
{
_canAttack = false;
_attackTimerHandle = GameEntry.Timer.ScheduleOnce(_attackCooldown, () => _canAttack = true, this);
ResetAttackCooldown(_attackCooldown);
TransitionTo(AttackStateType.Attack);
}

View File

@ -3,7 +3,6 @@ using SepCore.AsyncTask;
using SepCore.CustomUtility;
using SepCore.Definition;
using Cysharp.Threading.Tasks;
using SepCore.Timer;
using UnityEngine;
using UnityGameFramework.Runtime;
@ -28,8 +27,6 @@ namespace SepCore.Entity
private float _attackRangeSquared;
private float _attackCooldown = 1f;
private int _attackDamage = 1;
private TimerHandle _attackTimerHandle;
private bool _canAttack;
[SerializeField] private float _projectileSpeed = 12f;
[SerializeField] private float _projectileLifeTime = 3f;
@ -83,8 +80,7 @@ namespace SepCore.Entity
_projectileSpawnHeightOffset);
_projectileAssetName = ReadStringParam(ProjectileAssetNameParamKey, _projectileAssetName);
_canAttack = false;
_attackTimerHandle = GameEntry.Timer.ScheduleOnce(_attackCooldown, () => _canAttack = true, this);
StartAttackCooldown(_attackCooldown);
this.CachedTransform.position = enemyData.Position;
}
else
@ -112,8 +108,7 @@ namespace SepCore.Entity
if (_canAttack)
{
TryFireProjectile();
_canAttack = false;
_attackTimerHandle = GameEntry.Timer.ScheduleOnce(_attackCooldown, () => _canAttack = true, this);
ResetAttackCooldown(_attackCooldown);
}
}
else
@ -127,8 +122,7 @@ namespace SepCore.Entity
{
_movementComponent.OnReset();
_healthComponent.OnReset();
GameEntry.Timer.Cancel(_attackTimerHandle);
_attackTimerHandle = TimerHandle.Invalid;
CancelAttackCooldown();
base.OnHide(isShutdown, userData);
}

View File

@ -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

View File

@ -5,7 +5,8 @@
"GUID:d54b9488b03814a44ab937f0aeb738b1",
"GUID:75469ad4d38634e559750d17036d5f7c",
"GUID:363c5eb08ff8e6a439b85e37b8c20d96",
"GUID:436e23dbdc31e7d4fb5c3f804548b2df"
"GUID:436e23dbdc31e7d4fb5c3f804548b2df",
"GUID:0e1d182005e0ae647ab3fa40f5492dbb"
],
"includePlatforms": [
"Editor"

View File

@ -492,7 +492,7 @@ namespace SepCore.Timer
return;
}
taskInfo.RemainingTime = taskInfo.Interval;
taskInfo.RemainingTime += taskInfo.Interval;
}
private void InvokeCallback(TimerTaskInfo taskInfo)

View File

@ -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

View File

@ -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.

View File

@ -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."

View File

@ -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` 仍然只有一个主入口
- 仿真态生命周期仍然只有一个注册/反注册入口
- 主线程结算与表现写回边界不被打穿

View File

@ -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.

View File

@ -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."

View File

@ -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/<Domain>/UseCase`
- `UI/<Domain>/RawData`
- `UI/<Domain>/Controller`
- `UI/<Domain>/Context`
- `UI/<Domain>/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

View File

@ -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<TParams>()` 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.

View File

@ -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."

View File

@ -1,262 +0,0 @@
# Weapon Development SkillVampireLike
## 目标
本文件是 `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<EntityBase> 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<TParams>()`
约定:
- 参数解析优先在数据层完成。
- 武器逻辑层读取强类型 `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<TParams>()`,把参数初始化为强类型字段。
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`”结构,不建议把每种武器做成一套完全独立的数据总线。