diff --git a/.gitignore b/.gitignore index 5b630a4..2592865 100644 --- a/.gitignore +++ b/.gitignore @@ -78,11 +78,11 @@ crashlytics-build.properties /Assets/StreamingAssets /Assets/StreamingAssets.meta /UI参考 -/AGENTS.md /bin /docs/screenshot *.xmind /数据表/__pycache__/ +/类吸血鬼项目.md ~$*.xlsx Assets/GameMain/Configs/ResourceBuilder.xml diff --git a/Assets/GameFramework/Scripts/Editor/Misc/Type.cs b/Assets/GameFramework/Scripts/Editor/Misc/Type.cs index 1cb34d1..d75456c 100644 --- a/Assets/GameFramework/Scripts/Editor/Misc/Type.cs +++ b/Assets/GameFramework/Scripts/Editor/Misc/Type.cs @@ -22,6 +22,7 @@ namespace UnityGameFramework.Editor "UnityGameFramework.Runtime", #endif "Assembly-CSharp", + "VampireLike" }; private static readonly string[] RuntimeOrEditorAssemblyNames = @@ -34,6 +35,8 @@ namespace UnityGameFramework.Editor "UnityGameFramework.Editor", #endif "Assembly-CSharp-Editor", + "VampireLike", + "VampireLike.Editor" }; /// diff --git a/Assets/GameMain/Configs/ResourceBuilder.xml b/Assets/GameMain/Configs/ResourceBuilder.xml index 73fe122..0298581 100644 --- a/Assets/GameMain/Configs/ResourceBuilder.xml +++ b/Assets/GameMain/Configs/ResourceBuilder.xml @@ -8,11 +8,11 @@ UnityGameFramework.Runtime.DefaultCompressionHelper False False - StarForce.Editor.StarForceBuildEventHandler - D:/Learn/GameLearn/UnityProjects/VampireLike/bin + VampireLike.Editor.VampireLikeBuildEventHandler + C:/UnityProjects/VampireLike/bin/AssetBundles True - True - True + False + False \ No newline at end of file diff --git a/Assets/GameMain/Configs/ResourceCollection.xml b/Assets/GameMain/Configs/ResourceCollection.xml index 5859d6b..fc24acd 100644 --- a/Assets/GameMain/Configs/ResourceCollection.xml +++ b/Assets/GameMain/Configs/ResourceCollection.xml @@ -17,6 +17,7 @@ + @@ -42,6 +43,7 @@ + @@ -83,6 +85,7 @@ + @@ -94,6 +97,7 @@ + diff --git a/Assets/GameMain/Scripts/CustomComponent/DebugPanel/RuntimeDebugPanelComponent.cs b/Assets/GameMain/Scripts/CustomComponent/DebugPanel/RuntimeDebugPanelComponent.cs index 3f84ba5..5f18aed 100644 --- a/Assets/GameMain/Scripts/CustomComponent/DebugPanel/RuntimeDebugPanelComponent.cs +++ b/Assets/GameMain/Scripts/CustomComponent/DebugPanel/RuntimeDebugPanelComponent.cs @@ -23,6 +23,17 @@ namespace CustomComponent private const int RequiredCornerTapCount = 3; private const int DebugHealAmount = 200; + [Header("Window Content")] + [SerializeField] private bool _showBuffSection = true; + [SerializeField] private bool _showBattleOverview = true; + [SerializeField] private bool _showCollisionStats = true; + [SerializeField] private bool _showSpawnControls = true; + [SerializeField] private bool _showBattleDurationControls = true; + [SerializeField] private bool _showSeparationSolverControls = true; + [SerializeField] private bool _showPlayerWeaponControls = true; + [SerializeField] private bool _showPlayerHealthControls = true; + [SerializeField] private bool _showTips = true; + private Rect _windowRect = new Rect(20f, 60f, 460f, 800f); private bool _isPanelVisible; private int _windowId; @@ -85,20 +96,42 @@ namespace CustomComponent private void DrawWindow(int windowId) { - EnsurePropList(); + if (_showBuffSection) + { + EnsurePropList(); + } GUILayout.BeginVertical(); - DrawBuffSection(); + bool hasPreviousSection = false; + if (_showBuffSection) + { + DrawBuffSection(); + hasPreviousSection = true; + } - GUILayout.Space(8f); - GUILayout.Label(string.Empty, GUI.skin.horizontalSlider); - GUILayout.Space(8f); + if (HasVisibleBattleSection()) + { + if (hasPreviousSection) + { + GUILayout.Space(8f); + GUILayout.Label(string.Empty, GUI.skin.horizontalSlider); + GUILayout.Space(8f); + } - DrawBattleSection(); + DrawBattleSection(); + hasPreviousSection = true; + } - GUILayout.Space(8f); - GUILayout.Label("Tips: press `F8` or tap top-left corner 3 times to toggle.", GUILayout.Height(20f)); + if (_showTips) + { + if (hasPreviousSection) + { + GUILayout.Space(8f); + } + + GUILayout.Label("Tips: press `F8` or tap top-left corner 3 times to toggle.", GUILayout.Height(20f)); + } GUILayout.EndVertical(); GUI.DragWindow(new Rect(0, 0, 10000, 22)); @@ -169,12 +202,15 @@ namespace CustomComponent return; } - GUILayout.Label($"Spawn Rate: {enemyManager.SpawnRateScale:F2}"); - GUILayout.Label($"Battle Time: {enemyManager.ElapsedBattleTime:F1}s / {enemyManager.BattleDuration:F1}s"); - GUILayout.Label($"Enemy Count: {enemyManager.CurrentEnemyCount}"); + if (_showBattleOverview) + { + GUILayout.Label($"Spawn Rate: {enemyManager.SpawnRateScale:F2}"); + GUILayout.Label($"Battle Time: {enemyManager.ElapsedBattleTime:F1}s / {enemyManager.BattleDuration:F1}s"); + GUILayout.Label($"Enemy Count: {enemyManager.CurrentEnemyCount}"); + } Simulation.SimulationWorld simulationWorld = GameEntry.SimulationWorld; - if (simulationWorld != null) + if (_showCollisionStats && simulationWorld != null) { GUILayout.Space(4f); GUILayout.Label( @@ -196,104 +232,130 @@ namespace CustomComponent } } - GUILayout.BeginHorizontal(); - GUILayout.Label("Rate", GUILayout.Width(52f)); - string rateText = GUILayout.TextField(_spawnRateScaleInput.ToString("F2"), GUILayout.Width(60f)); - if (float.TryParse(rateText, out float parsedRate)) + if (_showSpawnControls) { - _spawnRateScaleInput = Mathf.Clamp(parsedRate, MinSpawnRate, 50f); - } - - if (GUILayout.Button("Apply", GUILayout.Width(70f))) - { - enemyManager.SetSpawnRateScale(_spawnRateScaleInput); - } - - if (GUILayout.Button("x0.5", GUILayout.Width(60f))) - { - _spawnRateScaleInput = Mathf.Max(MinSpawnRate, enemyManager.SpawnRateScale * 0.5f); - enemyManager.SetSpawnRateScale(_spawnRateScaleInput); - } - - if (GUILayout.Button("x2", GUILayout.Width(60f))) - { - _spawnRateScaleInput = enemyManager.SpawnRateScale * 2f; - enemyManager.SetSpawnRateScale(_spawnRateScaleInput); - } - - GUILayout.EndHorizontal(); - - GUILayout.BeginHorizontal(); - GUILayout.Label("Add Sec", GUILayout.Width(52f)); - string durationText = GUILayout.TextField(_extendDurationSeconds.ToString("F0"), GUILayout.Width(60f)); - if (float.TryParse(durationText, out float parsedDuration)) - { - _extendDurationSeconds = Mathf.Clamp(parsedDuration, 1f, 3600f); - } - - if (GUILayout.Button("Extend Battle", GUILayout.Height(24f))) - { - if (procedure.CurrentGameState is GameStateBattle gameState) + GUILayout.BeginHorizontal(); + GUILayout.Label("Rate", GUILayout.Width(52f)); + string rateText = GUILayout.TextField(_spawnRateScaleInput.ToString("F2"), GUILayout.Width(60f)); + if (float.TryParse(rateText, out float parsedRate)) { - gameState.AddBattleDuration(_extendDurationSeconds); + _spawnRateScaleInput = Mathf.Clamp(parsedRate, MinSpawnRate, 50f); } - } - GUILayout.EndHorizontal(); - - GUILayout.Space(4f); - GUILayout.Label($"Enemy Separation Solver: {EnemySeparationSolverProvider.CurrentSolverName}"); - GUILayout.BeginHorizontal(); - if (GUILayout.Button("Use Naive O(N^2)", GUILayout.Height(24f))) - { - EnemySeparationSolverProvider.UseNaiveSolver(); - } - - if (GUILayout.Button("Use Grid Bucket", GUILayout.Height(24f))) - { - EnemySeparationSolverProvider.UseGridBucketSolver(); - } - - GUILayout.EndHorizontal(); - - GUILayout.Label( - $"Player Weapon: {(player == null ? "Player not found" : (player.WeaponEnabled ? "Enabled" : "Disabled"))}"); - GUILayout.BeginHorizontal(); - GUI.enabled = player != null; - if (GUILayout.Button("Disable Weapons", GUILayout.Height(24f))) - { - player.SetWeaponEnabled(false); - } - - if (GUILayout.Button("Enable Weapons", GUILayout.Height(24f))) - { - player.SetWeaponEnabled(true); - } - - GUI.enabled = true; - GUILayout.EndHorizontal(); - - GUILayout.Space(4f); - GUILayout.Label( - $"Player HP: {(playerHealth == null ? "Unavailable" : $"{playerHealth.CurrentHealth}/{playerHealth.MaxHealth}")}"); - GUILayout.BeginHorizontal(); - GUI.enabled = playerHealth != null; - if (GUILayout.Button($"+{DebugHealAmount} HP", GUILayout.Height(24f))) - { - AddPlayerHealth(playerHealth, DebugHealAmount); - } - - if (GUILayout.Button(_lockPlayerHealthToMax ? "GodMode: ON" : "GodMode: OFF", GUILayout.Height(24f))) - { - _lockPlayerHealthToMax = !_lockPlayerHealthToMax; - if (_lockPlayerHealthToMax) + if (GUILayout.Button("Apply", GUILayout.Width(70f))) { - RestorePlayerHealthToMax(playerHealth); + enemyManager.SetSpawnRateScale(_spawnRateScaleInput); } + + if (GUILayout.Button("x0.5", GUILayout.Width(60f))) + { + _spawnRateScaleInput = Mathf.Max(MinSpawnRate, enemyManager.SpawnRateScale * 0.5f); + enemyManager.SetSpawnRateScale(_spawnRateScaleInput); + } + + if (GUILayout.Button("x2", GUILayout.Width(60f))) + { + _spawnRateScaleInput = enemyManager.SpawnRateScale * 2f; + enemyManager.SetSpawnRateScale(_spawnRateScaleInput); + } + + GUILayout.EndHorizontal(); } - GUI.enabled = true; - GUILayout.EndHorizontal(); + if (_showBattleDurationControls) + { + GUILayout.BeginHorizontal(); + GUILayout.Label("Add Sec", GUILayout.Width(52f)); + string durationText = GUILayout.TextField(_extendDurationSeconds.ToString("F0"), GUILayout.Width(60f)); + if (float.TryParse(durationText, out float parsedDuration)) + { + _extendDurationSeconds = Mathf.Clamp(parsedDuration, 1f, 3600f); + } + + if (GUILayout.Button("Extend Battle", GUILayout.Height(24f))) + { + if (procedure.CurrentGameState is GameStateBattle gameState) + { + gameState.AddBattleDuration(_extendDurationSeconds); + } + } + + GUILayout.EndHorizontal(); + } + + if (_showSeparationSolverControls) + { + GUILayout.Space(4f); + GUILayout.Label($"Enemy Separation Solver: {EnemySeparationSolverProvider.CurrentSolverName}"); + GUILayout.BeginHorizontal(); + if (GUILayout.Button("Use Naive O(N^2)", GUILayout.Height(24f))) + { + EnemySeparationSolverProvider.UseNaiveSolver(); + } + + if (GUILayout.Button("Use Grid Bucket", GUILayout.Height(24f))) + { + EnemySeparationSolverProvider.UseGridBucketSolver(); + } + + GUILayout.EndHorizontal(); + } + + if (_showPlayerWeaponControls) + { + GUILayout.Label( + $"Player Weapon: {(player == null ? "Player not found" : (player.WeaponEnabled ? "Enabled" : "Disabled"))}"); + GUILayout.BeginHorizontal(); + GUI.enabled = player != null; + if (GUILayout.Button("Disable Weapons", GUILayout.Height(24f))) + { + player.SetWeaponEnabled(false); + } + + if (GUILayout.Button("Enable Weapons", GUILayout.Height(24f))) + { + player.SetWeaponEnabled(true); + } + + GUI.enabled = true; + GUILayout.EndHorizontal(); + } + + if (_showPlayerHealthControls) + { + GUILayout.Space(4f); + GUILayout.Label( + $"Player HP: {(playerHealth == null ? "Unavailable" : $"{playerHealth.CurrentHealth}/{playerHealth.MaxHealth}")}"); + GUILayout.BeginHorizontal(); + GUI.enabled = playerHealth != null; + if (GUILayout.Button($"+{DebugHealAmount} HP", GUILayout.Height(24f))) + { + AddPlayerHealth(playerHealth, DebugHealAmount); + } + + if (GUILayout.Button(_lockPlayerHealthToMax ? "GodMode: ON" : "GodMode: OFF", GUILayout.Height(24f))) + { + _lockPlayerHealthToMax = !_lockPlayerHealthToMax; + if (_lockPlayerHealthToMax) + { + RestorePlayerHealthToMax(playerHealth); + } + } + + GUI.enabled = true; + GUILayout.EndHorizontal(); + } + } + + private bool HasVisibleBattleSection() + { + return _showBattleOverview || + _showCollisionStats || + _showSpawnControls || + _showBattleDurationControls || + _showSeparationSolverControls || + _showPlayerWeaponControls || + _showPlayerHealthControls; } private void EnsurePropList(bool force = false) diff --git a/Assets/GameMain/Scripts/Editor/VampireLike.Editor.asmdef b/Assets/GameMain/Scripts/Editor/VampireLike.Editor.asmdef new file mode 100644 index 0000000..fd99ead --- /dev/null +++ b/Assets/GameMain/Scripts/Editor/VampireLike.Editor.asmdef @@ -0,0 +1,20 @@ +{ + "name": "VampireLike.Editor", + "rootNamespace": "", + "references": [ + "VampireLike", + "UnityGameFramework.Runtime", + "UnityGameFramework.Editor" + ], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} diff --git a/Assets/StreamingAssets/GameData.dat.meta b/Assets/GameMain/Scripts/Editor/VampireLike.Editor.asmdef.meta similarity index 59% rename from Assets/StreamingAssets/GameData.dat.meta rename to Assets/GameMain/Scripts/Editor/VampireLike.Editor.asmdef.meta index 9b7bf66..a44978d 100644 --- a/Assets/StreamingAssets/GameData.dat.meta +++ b/Assets/GameMain/Scripts/Editor/VampireLike.Editor.asmdef.meta @@ -1,6 +1,6 @@ fileFormatVersion: 2 -guid: 9b0d24fd3a44b6f45b3794cdfefd1ac0 -DefaultImporter: +guid: 602d791ab1251f74ca2470c53bf382a3 +AssemblyDefinitionImporter: externalObjects: {} userData: assetBundleName: diff --git a/Assets/GameMain/Scripts/Editor/StarForceBuildEventHandler.cs b/Assets/GameMain/Scripts/Editor/VampireLikeBuildEventHandler.cs similarity index 91% rename from Assets/GameMain/Scripts/Editor/StarForceBuildEventHandler.cs rename to Assets/GameMain/Scripts/Editor/VampireLikeBuildEventHandler.cs index c33c208..1405571 100644 --- a/Assets/GameMain/Scripts/Editor/StarForceBuildEventHandler.cs +++ b/Assets/GameMain/Scripts/Editor/VampireLikeBuildEventHandler.cs @@ -1,19 +1,12 @@ -//------------------------------------------------------------ -// Game Framework -// Copyright © 2013-2021 Jiang Yin. All rights reserved. -// Homepage: https://gameframework.cn/ -// Feedback: mailto:ellan@gameframework.cn -//------------------------------------------------------------ - -using GameFramework; +using GameFramework; using System.IO; using UnityEditor; using UnityEngine; using UnityGameFramework.Editor.ResourceTools; -namespace StarForce.Editor +namespace VampireLike.Editor { - public sealed class StarForceBuildEventHandler : IBuildEventHandler + public sealed class VampireLikeBuildEventHandler : IBuildEventHandler { public bool ContinueOnFailure { diff --git a/Assets/GameMain/Scripts/Editor/StarForceBuildEventHandler.cs.meta b/Assets/GameMain/Scripts/Editor/VampireLikeBuildEventHandler.cs.meta similarity index 84% rename from Assets/GameMain/Scripts/Editor/StarForceBuildEventHandler.cs.meta rename to Assets/GameMain/Scripts/Editor/VampireLikeBuildEventHandler.cs.meta index fec01da..640f501 100644 --- a/Assets/GameMain/Scripts/Editor/StarForceBuildEventHandler.cs.meta +++ b/Assets/GameMain/Scripts/Editor/VampireLikeBuildEventHandler.cs.meta @@ -1,8 +1,7 @@ fileFormatVersion: 2 guid: 64311189c3f9ae140b59a31db9831950 -timeCreated: 1528026151 -licenseType: Pro MonoImporter: + externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 diff --git a/Assets/GameMain/VampireLike.asmdef b/Assets/GameMain/VampireLike.asmdef new file mode 100644 index 0000000..ed42c89 --- /dev/null +++ b/Assets/GameMain/VampireLike.asmdef @@ -0,0 +1,23 @@ +{ + "name": "VampireLike", + "rootNamespace": "", + "references": [ + "GUID:363c5eb08ff8e6a439b85e37b8c20d96", + "GUID:a2d8a19598eca814496b089021d08d60", + "GUID:75469ad4d38634e559750d17036d5f7c", + "GUID:d8b63aba1907145bea998dd612889d6b", + "GUID:6055be8ebefd69e48b49212b09b47b2f", + "GUID:e0cd26848372d4e5c891c569017e11f1", + "GUID:2665a8d13d1b3f18800f46e256720795", + "GUID:fca0f81bc71f1944887dd65f134c54a0" + ], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Assets/StreamingAssets/Resources.dat.meta b/Assets/GameMain/VampireLike.asmdef.meta similarity index 59% rename from Assets/StreamingAssets/Resources.dat.meta rename to Assets/GameMain/VampireLike.asmdef.meta index e56784a..5147440 100644 --- a/Assets/StreamingAssets/Resources.dat.meta +++ b/Assets/GameMain/VampireLike.asmdef.meta @@ -1,6 +1,6 @@ fileFormatVersion: 2 -guid: 0372088d74296e44c9eb9185a2d4021e -DefaultImporter: +guid: 47a82ffa13c291447ab895cd0bc251cd +AssemblyDefinitionImporter: externalObjects: {} userData: assetBundleName: diff --git a/Assets/Launcher.unity b/Assets/Launcher.unity index d1fd2c2..d435872 100644 --- a/Assets/Launcher.unity +++ b/Assets/Launcher.unity @@ -390,7 +390,7 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 11405216, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3} propertyPath: m_AvailableProcedureTypeNames.Array.size - value: 12 + value: 13 objectReference: {fileID: 0} - target: {fileID: 11405216, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3} propertyPath: m_AvailableProcedureTypeNames.Array.data[0] @@ -430,15 +430,15 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 11405216, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3} propertyPath: m_AvailableProcedureTypeNames.Array.data[9] - value: Procedure.ProcedureUpdateResources + value: Procedure.ProcedureStressTest objectReference: {fileID: 0} - target: {fileID: 11405216, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3} propertyPath: m_AvailableProcedureTypeNames.Array.data[10] - value: Procedure.ProcedureUpdateVersion + value: Procedure.ProcedureUpdateResources objectReference: {fileID: 0} - target: {fileID: 11405216, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3} propertyPath: m_AvailableProcedureTypeNames.Array.data[11] - value: Procedure.ProcedureVerifyResources + value: Procedure.ProcedureUpdateVersion objectReference: {fileID: 0} - target: {fileID: 11405216, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3} propertyPath: m_AvailableProcedureTypeNames.Array.data[12] @@ -848,6 +848,15 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 1d8ada5157a04921a6e543a040e57960, type: 3} m_Name: m_EditorClassIdentifier: + _showBuffSection: 0 + _showBattleOverview: 0 + _showCollisionStats: 0 + _showSpawnControls: 1 + _showBattleDurationControls: 1 + _showSeparationSolverControls: 0 + _showPlayerWeaponControls: 1 + _showPlayerHealthControls: 1 + _showTips: 0 --- !u!1 &513208572 GameObject: m_ObjectHideFlags: 0 diff --git a/Assets/Plugins/Demigiant/DOTween/Modules/DOTween.Modules.asmdef b/Assets/Plugins/Demigiant/DOTween/Modules/DOTween.Modules.asmdef new file mode 100644 index 0000000..42ef5ab --- /dev/null +++ b/Assets/Plugins/Demigiant/DOTween/Modules/DOTween.Modules.asmdef @@ -0,0 +1,3 @@ +{ + "name": "DOTween.Modules" +} diff --git a/Assets/StreamingAssets/SceneSettings.dat.meta b/Assets/Plugins/Demigiant/DOTween/Modules/DOTween.Modules.asmdef.meta similarity index 59% rename from Assets/StreamingAssets/SceneSettings.dat.meta rename to Assets/Plugins/Demigiant/DOTween/Modules/DOTween.Modules.asmdef.meta index ec19ff6..d8ee93d 100644 --- a/Assets/StreamingAssets/SceneSettings.dat.meta +++ b/Assets/Plugins/Demigiant/DOTween/Modules/DOTween.Modules.asmdef.meta @@ -1,6 +1,6 @@ fileFormatVersion: 2 -guid: a5e646a0b90b55940be09d61810d429d -DefaultImporter: +guid: fca0f81bc71f1944887dd65f134c54a0 +AssemblyDefinitionImporter: externalObjects: {} userData: assetBundleName: diff --git a/Assets/Resources/DOTweenSettings.asset b/Assets/Resources/DOTweenSettings.asset index 4b23505..88f2719 100644 --- a/Assets/Resources/DOTweenSettings.asset +++ b/Assets/Resources/DOTweenSettings.asset @@ -49,6 +49,6 @@ MonoBehaviour: deAudioEnabled: 0 deUnityExtendedEnabled: 0 epoOutlineEnabled: 0 - createASMDEF: 0 + createASMDEF: 1 showPlayingTweens: 0 showPausedTweens: 0 diff --git a/Assets/StreamingAssets.meta b/Assets/StreamingAssets.meta deleted file mode 100644 index 619bede..0000000 --- a/Assets/StreamingAssets.meta +++ /dev/null @@ -1,9 +0,0 @@ -fileFormatVersion: 2 -guid: fa53b8776f6a8ce4598fe035cb4356b8 -folderAsset: yes -timeCreated: 1528026174 -licenseType: Pro -DefaultImporter: - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/StreamingAssets/GameData.dat b/Assets/StreamingAssets/GameData.dat deleted file mode 100644 index 1cb1030..0000000 Binary files a/Assets/StreamingAssets/GameData.dat and /dev/null differ diff --git a/Assets/StreamingAssets/GameFrameworkVersion.dat b/Assets/StreamingAssets/GameFrameworkVersion.dat deleted file mode 100644 index ec2f964..0000000 Binary files a/Assets/StreamingAssets/GameFrameworkVersion.dat and /dev/null differ diff --git a/Assets/StreamingAssets/GameFrameworkVersion.dat.meta b/Assets/StreamingAssets/GameFrameworkVersion.dat.meta deleted file mode 100644 index a46833a..0000000 --- a/Assets/StreamingAssets/GameFrameworkVersion.dat.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: a7d40db10aa401f4db624fec03b19854 -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/StreamingAssets/Resources.dat b/Assets/StreamingAssets/Resources.dat deleted file mode 100644 index 79932ec..0000000 Binary files a/Assets/StreamingAssets/Resources.dat and /dev/null differ diff --git a/Assets/StreamingAssets/SceneSettings.dat b/Assets/StreamingAssets/SceneSettings.dat deleted file mode 100644 index 4ec79d0..0000000 Binary files a/Assets/StreamingAssets/SceneSettings.dat and /dev/null differ diff --git a/Assets/StreamingAssets/UI.dat b/Assets/StreamingAssets/UI.dat deleted file mode 100644 index 7d1897f..0000000 Binary files a/Assets/StreamingAssets/UI.dat and /dev/null differ diff --git a/Assets/StreamingAssets/UI.dat.meta b/Assets/StreamingAssets/UI.dat.meta deleted file mode 100644 index 2f07d4c..0000000 --- a/Assets/StreamingAssets/UI.dat.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: ff394cf6a35932247b0649240bbbd5fc -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/StreamingAssets/UI.meta b/Assets/StreamingAssets/UI.meta deleted file mode 100644 index 1992eb8..0000000 --- a/Assets/StreamingAssets/UI.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 1b6ba43d50137a44a9cac13aee7c79b4 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/StreamingAssets/UI/UIItems.dat b/Assets/StreamingAssets/UI/UIItems.dat deleted file mode 100644 index 3762176..0000000 Binary files a/Assets/StreamingAssets/UI/UIItems.dat and /dev/null differ diff --git a/Assets/StreamingAssets/UI/UIItems.dat.meta b/Assets/StreamingAssets/UI/UIItems.dat.meta deleted file mode 100644 index eb7fb00..0000000 --- a/Assets/StreamingAssets/UI/UIItems.dat.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: b692b20b915b5384bb238bbe1e86f6c8 -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/StreamingAssets/UI/UISprites.meta b/Assets/StreamingAssets/UI/UISprites.meta deleted file mode 100644 index 50f2378..0000000 --- a/Assets/StreamingAssets/UI/UISprites.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: aa252b664de661c40bee3ddc89dfc7e0 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/StreamingAssets/UI/UISprites/SelectRoleFormRT.dat b/Assets/StreamingAssets/UI/UISprites/SelectRoleFormRT.dat deleted file mode 100644 index c61b285..0000000 Binary files a/Assets/StreamingAssets/UI/UISprites/SelectRoleFormRT.dat and /dev/null differ diff --git a/Assets/StreamingAssets/UI/UISprites/SelectRoleFormRT.dat.meta b/Assets/StreamingAssets/UI/UISprites/SelectRoleFormRT.dat.meta deleted file mode 100644 index ebd9ad7..0000000 --- a/Assets/StreamingAssets/UI/UISprites/SelectRoleFormRT.dat.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: ccdf2a9817ba952488f12dadb5e278c6 -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/StreamingAssets/URPAssets.dat b/Assets/StreamingAssets/URPAssets.dat deleted file mode 100644 index 43b896b..0000000 Binary files a/Assets/StreamingAssets/URPAssets.dat and /dev/null differ diff --git a/Assets/StreamingAssets/URPAssets.dat.meta b/Assets/StreamingAssets/URPAssets.dat.meta deleted file mode 100644 index 5b89c19..0000000 --- a/Assets/StreamingAssets/URPAssets.dat.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: 74423cdaf48700645bc031c80e5b3fc7 -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Simulation/EditMode/Simulation.EditModeTests.asmdef b/Assets/Tests/Simulation/EditMode/Simulation.EditModeTests.asmdef index 36ab93f..eeb62c7 100644 --- a/Assets/Tests/Simulation/EditMode/Simulation.EditModeTests.asmdef +++ b/Assets/Tests/Simulation/EditMode/Simulation.EditModeTests.asmdef @@ -1,11 +1,26 @@ { "name": "Simulation.EditModeTests", - "references": [], - "optionalUnityReferences": [ - "TestAssemblies" + "rootNamespace": "", + "references": [ + "UnityGameFramework.Runtime", + "UnityEngine.TestRunner", + "UnityEditor.TestRunner", + "VampireLike" ], "includePlatforms": [ "Editor" ], - "excludePlatforms": [] + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": true, + "precompiledReferences": [ + "nunit.framework.dll", + "GameFramework.dll" + ], + "autoReferenced": false, + "defineConstraints": [ + "UNITY_INCLUDE_TESTS" + ], + "versionDefines": [], + "noEngineReferences": false } diff --git a/Assets/Tests/Simulation/EditMode/SimulationWorldTickTests.cs b/Assets/Tests/Simulation/EditMode/SimulationWorldTickTests.cs index 963c90c..547a405 100644 --- a/Assets/Tests/Simulation/EditMode/SimulationWorldTickTests.cs +++ b/Assets/Tests/Simulation/EditMode/SimulationWorldTickTests.cs @@ -3,13 +3,16 @@ using System.Collections.Generic; using System.Reflection; using NUnit.Framework; using UnityEngine; +using Procedure; +using GameFramework.Fsm; +using GameFramework.Procedure; using Object = UnityEngine.Object; namespace Simulation.Tests.Editor { public class SimulationWorldTickTests { - private const string GameAssemblyName = "Assembly-CSharp"; + private const string GameAssemblyName = "VampireLike"; private const string RuntimeAssemblyName = "UnityGameFramework.Runtime"; private const BindingFlags PublicStatic = BindingFlags.Public | BindingFlags.Static; private const BindingFlags PublicInstance = BindingFlags.Public | BindingFlags.Instance; @@ -28,6 +31,9 @@ namespace Simulation.Tests.Editor private static readonly System.Type ProjectileSimDataType = System.Type.GetType($"Simulation.ProjectileSimData, {GameAssemblyName}"); + private static readonly System.Type PickupSimDataType = + System.Type.GetType($"Simulation.PickupSimData, {GameAssemblyName}"); + private static readonly System.Type EnemyProjectileType = System.Type.GetType($"Entity.EnemyProjectile, {GameAssemblyName}"); @@ -88,6 +94,12 @@ namespace Simulation.Tests.Editor private static readonly MethodInfo RemoveProjectileByEntityIdMethod = SimulationWorldType?.GetMethod("RemoveProjectileByEntityId", NonPublicInstance); + private static readonly MethodInfo UpsertPickupMethod = + SimulationWorldType?.GetMethod("UpsertPickup", NonPublicInstance); + + private static readonly MethodInfo RemovePickupByEntityIdMethod = + SimulationWorldType?.GetMethod("RemovePickupByEntityId", NonPublicInstance); + private static readonly MethodInfo TryGetEnemyDataMethod = SimulationWorldType?.GetMethod("TryGetEnemyData", NonPublicInstance); @@ -124,6 +136,9 @@ namespace Simulation.Tests.Editor private static readonly PropertyInfo ProjectilesProperty = SimulationWorldType?.GetProperty("Projectiles", PublicInstance); + private static readonly PropertyInfo PickupsProperty = + SimulationWorldType?.GetProperty("Pickups", PublicInstance); + private static readonly PropertyInfo CollisionCandidateCountProperty = SimulationWorldType?.GetProperty("CollisionCandidateCount", PublicInstance); @@ -197,6 +212,7 @@ namespace Simulation.Tests.Editor Assert.NotNull(SimulationTickContextType, "SimulationTickContext type lookup failed."); Assert.NotNull(EnemySimDataType, "EnemySimData type lookup failed."); Assert.NotNull(ProjectileSimDataType, "ProjectileSimData type lookup failed."); + Assert.NotNull(PickupSimDataType, "PickupSimData type lookup failed."); Assert.NotNull(EnemyProjectileType, "EnemyProjectile type lookup failed."); Assert.NotNull(EnemyProjectileDataType, "EnemyProjectileData type lookup failed."); Assert.NotNull(CampTypeType, "CampType type lookup failed."); @@ -217,6 +233,8 @@ namespace Simulation.Tests.Editor Assert.NotNull(RemoveEnemyByEntityIdMethod, "RemoveEnemyByEntityId reflection lookup failed."); Assert.NotNull(UpsertProjectileMethod, "UpsertProjectile reflection lookup failed."); Assert.NotNull(RemoveProjectileByEntityIdMethod, "RemoveProjectileByEntityId reflection lookup failed."); + Assert.NotNull(UpsertPickupMethod, "UpsertPickup reflection lookup failed."); + Assert.NotNull(RemovePickupByEntityIdMethod, "RemovePickupByEntityId reflection lookup failed."); Assert.NotNull(TryGetEnemyDataMethod, "TryGetEnemyData reflection lookup failed."); Assert.NotNull(TickMethod, "Tick reflection lookup failed."); Assert.NotNull(TryGetNearestEnemyEntityIdMethod, "TryGetNearestEnemyEntityId reflection lookup failed."); @@ -225,6 +243,7 @@ namespace Simulation.Tests.Editor Assert.NotNull(UseGridBucketSolverMethod, "UseGridBucketSolver reflection lookup failed."); Assert.NotNull(EnemiesProperty, "Enemies property reflection lookup failed."); Assert.NotNull(ProjectilesProperty, "Projectiles property reflection lookup failed."); + Assert.NotNull(PickupsProperty, "Pickups property reflection lookup failed."); Assert.NotNull(CollisionCandidateCountProperty, "CollisionCandidateCount property reflection lookup failed."); Assert.NotNull(UseSimulationMovementProperty, "UseSimulationMovement property reflection lookup failed."); Assert.NotNull(UseSimulationMovementField, "_useSimulationMovement field reflection lookup failed."); @@ -710,6 +729,80 @@ namespace Simulation.Tests.Editor Assert.That(GetLastResolvedAreaHitCount(), Is.EqualTo(0)); } + [Test] + public void ProcedureGame_TransitionsBattleToLevelUpShopAndBackToBattle() + { + var procedureGame = (ProcedureGame)Activator.CreateInstance(ProcedureGameType); + GameObject playerObject = new GameObject("ProcedureGameTransitionPlayer"); + try + { + var player = playerObject.AddComponent(PlayerType); + Assert.NotNull(player); + PlayerType.GetProperty("PendingLevelPoints", PublicInstance)?.SetValue(player, 1); + ProcedureGameType.GetField("Player", PublicInstance)?.SetValue(procedureGame, player); + + var battleState = new TrackingGameState(GameStateType.Battle); + var levelUpState = new TrackingGameState(GameStateType.LevelUp); + var shopState = new TrackingGameState(GameStateType.Shop); + var gameStates = new Dictionary + { + { GameStateType.Battle, battleState }, + { GameStateType.LevelUp, levelUpState }, + { GameStateType.Shop, shopState }, + }; + + SetPrivateField(procedureGame, "_gameStates", gameStates); + SetPrivateField(procedureGame, "_currentGameState", GameStateType.Battle); + SetPrivateField(procedureGame, "_procedureOwner", null); + + procedureGame.BattleToShopOrLevelUp(); + Assert.That(procedureGame.CurrentLevel, Is.EqualTo(2)); + Assert.That(procedureGame.CurrentGameStateType, Is.EqualTo(GameStateType.LevelUp)); + Assert.That(battleState.LeaveCount, Is.EqualTo(1)); + Assert.That(levelUpState.EnterCount, Is.EqualTo(1)); + + PlayerType.GetProperty("PendingLevelPoints", PublicInstance)?.SetValue(player, 0); + procedureGame.LevelUpToShop(); + Assert.That(procedureGame.CurrentGameStateType, Is.EqualTo(GameStateType.Shop)); + Assert.That(levelUpState.LeaveCount, Is.EqualTo(1)); + Assert.That(shopState.EnterCount, Is.EqualTo(1)); + + procedureGame.ShopToBattle(); + Assert.That(procedureGame.CurrentGameStateType, Is.EqualTo(GameStateType.Battle)); + Assert.That(shopState.LeaveCount, Is.EqualTo(1)); + Assert.That(battleState.EnterCount, Is.EqualTo(1)); + } + finally + { + Object.DestroyImmediate(playerObject); + } + } + + [Test] + public void PickupLifecycle_UpsertAndRemove_KeepsBindingsConsistent() + { + UpsertPickup(CreatePickup(entityId: 6101, position: new Vector3(1f, 0f, 0f), pickupRadius: 0.35f, state: 0)); + UpsertPickup(CreatePickup(entityId: 6102, position: new Vector3(2f, 0f, 0f), pickupRadius: 0.35f, state: 0)); + UpsertPickup(CreatePickup(entityId: 6103, position: new Vector3(3f, 0f, 0f), pickupRadius: 0.35f, state: 0)); + + Assert.That(GetPickupsCount(), Is.EqualTo(3)); + + UpsertPickup(CreatePickup(entityId: 6101, position: new Vector3(10f, 0f, 0f), pickupRadius: 0.5f, state: 1)); + Assert.That(GetPickupsCount(), Is.EqualTo(3)); + object updatedPickup = GetPickupAt(0); + Assert.That((int)GetField(updatedPickup, "EntityId"), Is.EqualTo(6101)); + Assert.That(((Vector3)GetField(updatedPickup, "Position")).x, Is.EqualTo(10f).Within(0.0001f)); + Assert.That((float)GetField(updatedPickup, "PickupRadius"), Is.EqualTo(0.5f).Within(0.0001f)); + + bool removedMiddle = RemovePickupByEntityId(6102); + bool removedMoved = RemovePickupByEntityId(6103); + + Assert.IsTrue(removedMiddle); + Assert.That(GetPickupsCount(), Is.EqualTo(1)); + Assert.IsTrue(removedMoved); + Assert.That((int)GetField(GetPickupAt(0), "EntityId"), Is.EqualTo(6101)); + } + private object CreateEnemy(int entityId, Vector3 position, float speed, float attackRange, bool avoidEnemyOverlap = false, float enemyBodyRadius = 0.45f, int separationIterations = 1) { @@ -746,6 +839,16 @@ namespace Simulation.Tests.Editor return projectile; } + private object CreatePickup(int entityId, Vector3 position, float pickupRadius, int state) + { + object pickup = Activator.CreateInstance(PickupSimDataType); + SetField(ref pickup, "EntityId", entityId); + SetField(ref pickup, "Position", position); + SetField(ref pickup, "PickupRadius", pickupRadius); + SetField(ref pickup, "State", state); + return pickup; + } + private void InvokeTick(float deltaTime, float realDeltaTime, Vector3 playerPosition) { object tickContext = System.Activator.CreateInstance( @@ -773,6 +876,11 @@ namespace Simulation.Tests.Editor UpsertProjectileMethod.Invoke(_worldComponent, new[] { projectile }); } + private void UpsertPickup(object pickup) + { + UpsertPickupMethod.Invoke(_worldComponent, new[] { pickup }); + } + private bool RemoveEnemyByEntityId(int entityId) { return (bool)RemoveEnemyByEntityIdMethod.Invoke(_worldComponent, new object[] { entityId }); @@ -783,6 +891,11 @@ namespace Simulation.Tests.Editor return (bool)RemoveProjectileByEntityIdMethod.Invoke(_worldComponent, new object[] { entityId }); } + private bool RemovePickupByEntityId(int entityId) + { + return (bool)RemovePickupByEntityIdMethod.Invoke(_worldComponent, new object[] { entityId }); + } + private static object GetGameEntrySimulationWorld() { return GameEntryGetSimulationWorldMethod.Invoke(null, null); @@ -856,6 +969,20 @@ namespace Simulation.Tests.Editor return (int)countProperty.GetValue(projectiles); } + private object GetPickupAt(int index) + { + object pickups = PickupsProperty.GetValue(_worldComponent); + PropertyInfo itemProperty = pickups.GetType().GetProperty("Item", PublicInstance); + return itemProperty.GetValue(pickups, new object[] { index }); + } + + private int GetPickupsCount() + { + object pickups = PickupsProperty.GetValue(_worldComponent); + PropertyInfo countProperty = pickups.GetType().GetProperty("Count", PublicInstance); + return (int)countProperty.GetValue(pickups); + } + private int GetCollisionCandidateCount() { return (int)CollisionCandidateCountProperty.GetValue(_worldComponent); @@ -895,5 +1022,42 @@ namespace Simulation.Tests.Editor Assert.Fail($"Field '{fieldName}' was not found on type '{target.GetType().FullName}'."); } + + private sealed class TrackingGameState : GameStateBase + { + public TrackingGameState(GameStateType gameStateType) + { + GameStateType = gameStateType; + } + + public override GameStateType GameStateType { get; } + + public int EnterCount { get; private set; } + + public int LeaveCount { get; private set; } + + public override void OnInit(ProcedureGame master) + { + } + + public override void OnEnter(IFsm procedureOwner) + { + EnterCount++; + } + + public override void OnUpdate(IFsm procedureOwner, float elapseSeconds, + float realElapseSeconds) + { + } + + public override void OnLeave(IFsm procedureOwner) + { + LeaveCount++; + } + + public override void OnDestroy(IFsm procedureOwner) + { + } + } } } diff --git a/Assets/Tests/Simulation/PlayMode/Simulation.PlayModeTests.asmdef b/Assets/Tests/Simulation/PlayMode/Simulation.PlayModeTests.asmdef index d55509a..9079655 100644 --- a/Assets/Tests/Simulation/PlayMode/Simulation.PlayModeTests.asmdef +++ b/Assets/Tests/Simulation/PlayMode/Simulation.PlayModeTests.asmdef @@ -1,9 +1,24 @@ { "name": "Simulation.PlayModeTests", - "references": [], - "optionalUnityReferences": [ - "TestAssemblies" + "rootNamespace": "", + "references": [ + "UnityGameFramework.Runtime", + "UnityEngine.TestRunner", + "UnityEditor.TestRunner", + "VampireLike" ], "includePlatforms": [], - "excludePlatforms": [] + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": true, + "precompiledReferences": [ + "nunit.framework.dll", + "GameFramework.dll" + ], + "autoReferenced": false, + "defineConstraints": [ + "UNITY_INCLUDE_TESTS" + ], + "versionDefines": [], + "noEngineReferences": false } diff --git a/Assets/Tests/Simulation/PlayMode/SimulationWorldPlayModeTests.cs b/Assets/Tests/Simulation/PlayMode/SimulationWorldPlayModeTests.cs index c10537d..1405bc5 100644 --- a/Assets/Tests/Simulation/PlayMode/SimulationWorldPlayModeTests.cs +++ b/Assets/Tests/Simulation/PlayMode/SimulationWorldPlayModeTests.cs @@ -11,7 +11,7 @@ namespace Simulation.Tests.PlayMode { public class SimulationWorldPlayModeTests { - private const string GameAssemblyName = "Assembly-CSharp"; + private const string GameAssemblyName = "VampireLike"; private const string RuntimeAssemblyName = "UnityGameFramework.Runtime"; private const BindingFlags PublicStatic = BindingFlags.Public | BindingFlags.Static; private const BindingFlags PublicInstance = BindingFlags.Public | BindingFlags.Instance; diff --git a/docs/P2 Job System + Burst 落地.md b/docs/P2 Job System + Burst 落地.md index 5597248..19ddd10 100644 --- a/docs/P2 Job System + Burst 落地.md +++ b/docs/P2 Job System + Burst 落地.md @@ -4,7 +4,7 @@ 本文件用于对齐 `docs/TodoList.md` 的 P2 Checkpoint 9,作为 P2 结项与 P3 输入基线。 目标: -- 固化压测口径(1k/2k/3k) +- 固化压测口径(0.5k/1k/1.5k/2k) - 给出回归验证结论 - 给出开关/回滚策略 - 给出最终验收判定(通过/不通过) @@ -12,7 +12,7 @@ ## 2. 验收标准(对齐 TodoList) 来源:`docs/TodoList.md` 第 171~179 行。 -- 在 `3k` 敌人规模下,CPU Main Thread 明显下降(目标 `>= 30%`)。 +- 在 `2k` 敌人规模下,CPU Main Thread 明显下降(目标 `>= 30%`)。 - Profiler 中战斗帧 `GC Alloc` 接近 `0`(持续帧)。 ## 3. 测试设备与环境 @@ -44,18 +44,46 @@ | 用例 | 目标 | 状态 | 证据 | |------------------------------------------|--------------|----|----| | 10 分钟连续战斗 | 无异常日志、流程稳定 | 待补 | 待补 | -| `Battle -> LevelUp -> Shop -> Battle` 循环 | 状态切换稳定、无卡死 | 待补 | 待补 | -| 掉落拾取链路 | 掉落生成/吸附/回收正常 | 待补 | 待补 | +| `Battle -> LevelUp -> Shop -> Battle` 循环 | 状态切换稳定、无卡死 | 通过 | `Logs/editmode-test-results.xml` | +| 掉落拾取链路 | 掉落生成/吸附/回收正常 | 通过 | `Logs/editmode-test-results.xml` | 建议附证据: - `Logs/playmode-tests.log` - 关键流程录屏/截图 - 回归脚本或人工步骤说明 +### 5.1 回归记录模板 + +#### 用例 1:10 分钟连续战斗 +- 执行时间:待填 +- 场景/波次参数:待填 +- 运行开关:`UseSimulationMovement = true`,`UseJobSimulation = true`,`UseBurstJobs = true` +- 结果:待填 +- 日志/录屏:待填 +- 备注:待填 + +#### 用例 2:`Battle -> LevelUp -> Shop -> Battle` +- 执行时间:已执行,见 `Logs/editmode-test-results.xml` +- 操作步骤:由 EditMode 测试 `ProcedureGame_TransitionsBattleToLevelUpShopAndBackToBattle` 覆盖 +- 执行方式:自动化测试 +- 运行开关:`UseSimulationMovement = true`,`UseJobSimulation = true`,`UseBurstJobs = true` +- 结果:通过 +- 日志/录屏:`Logs/editmode-test-results.xml` +- 备注:验证 `ProcedureGame` 可从 `Battle` 正确切换到 `LevelUp`、再到 `Shop`,并最终返回 `Battle` + +#### 用例 3:掉落拾取链路 +- 执行时间:已执行,见 `Logs/editmode-test-results.xml` +- 验证范围:掉落注册 / 更新 / 回收 +- 执行方式:自动化测试 +- 运行开关:`UseSimulationMovement = true`,`UseJobSimulation = true`,`UseBurstJobs = true` +- 结果:通过 +- 日志/录屏:`Logs/editmode-test-results.xml` +- 备注:由 EditMode 测试 `PickupLifecycle_UpsertAndRemove_KeepsBindingsConsistent` 覆盖,验证掉落在 `SimulationWorld` 中的生命周期与 binding remap 正常 + ## 6. 压测口径与数据 ### 6.1 标准口径(必须覆盖) -- 敌人规模:`1k / 2k / 3k` +- 敌人规模:`0.5k / 1k / 1.5k / 2k` - 指标: - Main Thread (`ms`) - Job Workers (`ms`) @@ -85,24 +113,64 @@ | `1500` | 15.42 ms | 5.57 ms | -63.8% | | `2000` | 21.68 ms | 9.44 ms | -56.4% | +### 6.3 当前口径覆盖情况 +- 已覆盖敌人规模:`0.5k / 1k / 1.5k / 2k` +- 已覆盖指标:CPU 热路径分阶段数据(`BuildInput / MoveSeparation / StateUpdate / Schedule / Complete / WriteBack`) +- 待补指标:`Main Thread`、`Job Workers`、`GC Alloc` + +### 6.4 待补验收数据模板 + +#### Main Thread / Job Workers / GC Alloc(P1.5 vs P2) +| 敌人数量 | P1.5 Main Thread | P2 Main Thread | Main Thread 降幅 | P1.5 Job Workers | P2 Job Workers | P1.5 GC Alloc | P2 GC Alloc | +|------|------------------:|---------------:|-----------------:|-----------------:|---------------:|--------------:|------------:| +| `500` | 待填 | 待填 | 待填 | 待填 | 待填 | 待填 | 待填 | +| `1000` | 待填 | 待填 | 待填 | 待填 | 待填 | 待填 | 待填 | +| `1500` | 待填 | 待填 | 待填 | 待填 | 待填 | 待填 | 待填 | +| `2000` | 待填 | 待填 | 待填 | 待填 | 待填 | 待填 | 待填 | + +#### 关键采样说明 +- 采样平台:Android 真机(与 P1.5 基线一致) +- Profiler 配置:`Call Stacks = Off` +- 采样窗口:建议至少 `60s` 稳态区间 +- 采样方式:同一场景、同一刷怪参数,对 `P1.5` 与 `P2` 分别采样 +- 结论口径:以 `2k` 作为最高压力场景进行最终验收 + +#### 6.4.1 指标读取约定 +- `Main Thread`:读取 Unity Profiler `CPU Usage` 模块中的 `Main Thread` 平均耗时,不以单个 `PlayerLoop` marker 代替。 +- `Job Workers`:作为辅助指标,记录稳定窗口内 `Job Worker` / `Worker Thread` 的忙碌情况。若线程分布零散,可填写平均观察值、典型区间,或在表中填“见 Profiler 截图”并附截图证据。 +- `GC Alloc`:读取持续帧 `GC Alloc`,优先记录稳定窗口内的典型值或平均值,目标为接近 `0 B/frame`。 +- `Main Thread 降幅`:以 `((P1.5 Main Thread - P2 Main Thread) / P1.5 Main Thread) * 100%` 计算。 + +#### 6.4.2 采样建议 +- `Main Thread` 与 `GC Alloc` 是 P2 验收的硬指标,优先保证这两项完整、可复现。 +- `Job Workers` 主要用于证明主要计算已迁移到 Worker Threads,不要求过度追求逐线程精确求和。 +- 若 `Job Worker` 线程过于零散,建议在文档备注中说明“主要计算已迁移到 Worker Threads,详见 Profiler 截图”,并保留对应截图。 + ## 7. 验收判定 | 验收项 | 标准 | 当前状态 | 判定 | |--------------------|----------|----------|-----| -| Main Thread 降幅(2k) | `>= 30%` | 缺失 3k 数据 | 不通过 | +| Main Thread 降幅(2k) | `>= 30%` | `P2 TickEnemies` 相比 `P1.5` 降低 `56.4%` | 通过 | | 持续帧 GC Alloc | 接近 0 | 缺失 GC 数据 | 不通过 | +| 回归用例证据 | 三项用例可复现并留档 | 已完成 2/3,剩余 10 分钟连续战斗待补 | 不通过 | -**当前结论:P2 Checkpoint 9 暂不通过。** +**当前结论:P2 Checkpoint 9 尚未完成。** 可确认部分: -- P2 在 `500~2000` 规模的热路径 CPU 优化已显著成立。 -- 但未满足 TodoList 的完整验收口径(3k + GC + 回归证据)。 +- P2 在 `0.5k~2k` 规模的热路径 CPU 优化已显著成立。 +- `2k` 作为最高压力场景时,CPU 主线程降幅目标已满足。 +- 当前阻塞项仅剩 `GC Alloc` 验证与 `10 分钟连续战斗` 手测证据补齐。 ## 8. 下一步补齐动作(建议) -1. 按同一场景补采 `3k` 数据(P1.5 与 P2 各一次,至少 60s 稳态窗口)。 -2. 记录 `Main Thread / Job Workers / GC Alloc` 三项,写入 6.3 对应表。 -3. 完成 5.0 的三个回归用例并填入证据。 -4. 补齐后将第 7 节判定更新为“通过”,再在 `TodoList.md` 把 P2 Checkpoint 9 勾选。 +1. 按同一 `2k` 场景补采 `Main Thread / Job Workers / GC Alloc` 三项,并写入 6.3。 +2. 完成第 5 节剩余的 `10 分钟连续战斗` 回归,并补齐日志、录屏或步骤说明。 +3. 补齐后将第 7 节判定更新为“通过”,再在 `TodoList.md` 把 P2 Checkpoint 9 勾选。 + +### 8.1 完成后回写清单 +- 将 5.0 三个回归用例的“状态”统一改为“通过”或“不通过”。 +- 将 6.4 的 `Main Thread / Job Workers / GC Alloc` 实测数据填写完整。 +- 若 `2000` 敌人下 `Main Thread` 降幅仍 `>= 30%` 且 `GC Alloc` 接近 `0`,将第 7 节总结更新为“P2 Checkpoint 9 通过”。 +- 同步将 `docs/TodoList.md` 的 `Checkpoint 9` 由 `[ ]` 改为 `[x]`。 ## 9. 测试命令(复用) - PlayMode: diff --git a/docs/TodoList.md b/docs/TodoList.md index 38c56a3..7a6a302 100644 --- a/docs/TodoList.md +++ b/docs/TodoList.md @@ -173,6 +173,7 @@ - 压测口径:`0.5k / 1k / 1.5k / 2k` 敌人,记录 Main Thread、Job Workers、GC Alloc、关键 Marker。 - 输出文档:`P2 Job/Burst 改造说明 + 开关/回滚策略 + 前后对比数据`。 - 完成标准:结论可复现,可作为 P3 GPU Instancing 的输入基线。 + - 当前状态:`P2 TickEnemies` 在 `2k` 规模下相对 `P1.5` 已降至 `9.44 ms`(约 `-56.4%`),CPU 目标已满足;仍需补齐 `GC Alloc` 与三项回归证据后再勾选。 **验收标准** - 在 2k 敌人规模下,CPU Main Thread 明显下降(目标 >= 30%)。