From 2a0cbc8f0e053284d4ecea14613f9e4dd3576422 Mon Sep 17 00:00:00 2001 From: SepComet <202308010230@stu.csust.edu.cn> Date: Thu, 4 Jun 2026 22:37:55 +0800 Subject: [PATCH 01/22] =?UTF-8?q?=E8=B0=83=E6=95=B4=20SpriteCache=20?= =?UTF-8?q?=E7=9A=84=E8=AF=BB=E5=8F=96=E8=B5=84=E6=BA=90=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将原来 UGF 事件回调风格的调用改成使用 UniTask 的异步操作 --- .../Presentation/Common/View/RoleItem.cs | 5 +- .../Main/Context/GoodsItemContext.cs | 37 +++++++----- .../DisplayItemInfo/DisplayItemInfoForm.cs | 22 +++----- .../Presentation/Main/Shop/ShopController.cs | 16 +++--- .../Presentation/Main/View/DisplayItem.cs | 7 ++- .../Main/View/LevelUpRewardItem.cs | 8 ++- .../AsyncTask/ResourceAsyncExtension.cs | 20 +++++++ .../SpriteCache/SpriteCacheComponent.cs | 56 ++++++------------- 8 files changed, 88 insertions(+), 83 deletions(-) diff --git a/Assets/GameMain/Scripts/Presentation/Common/View/RoleItem.cs b/Assets/GameMain/Scripts/Presentation/Common/View/RoleItem.cs index 65db07d..8e9043c 100644 --- a/Assets/GameMain/Scripts/Presentation/Common/View/RoleItem.cs +++ b/Assets/GameMain/Scripts/Presentation/Common/View/RoleItem.cs @@ -1,3 +1,4 @@ +using Cysharp.Threading.Tasks; using SepCore.Event; using UnityEngine; using UnityEngine.UI; @@ -10,7 +11,7 @@ namespace SepCore.UI private RoleItemContext _context = null; - public void OnInit(RoleItemContext context) + public async UniTaskVoid OnInit(RoleItemContext context) { _context = context; if (_context == null || string.IsNullOrEmpty(_context.IconName)) @@ -18,7 +19,7 @@ namespace SepCore.UI return; } - GameEntry.SpriteCache.GetSprite(_context.IconName, sprite => _roleImage.sprite = sprite); + _roleImage.sprite = await GameEntry.SpriteCache.GetSprite(_context.IconName); } public void OnReset() diff --git a/Assets/GameMain/Scripts/Presentation/Main/Context/GoodsItemContext.cs b/Assets/GameMain/Scripts/Presentation/Main/Context/GoodsItemContext.cs index c139629..1258a62 100644 --- a/Assets/GameMain/Scripts/Presentation/Main/Context/GoodsItemContext.cs +++ b/Assets/GameMain/Scripts/Presentation/Main/Context/GoodsItemContext.cs @@ -1,3 +1,4 @@ +using Cysharp.Threading.Tasks; using SepCore.CustomUtility; using SepCore.DataTable; using SepCore.Definition; @@ -24,31 +25,37 @@ namespace SepCore.UI ItemType = ItemType.None; } - public GoodsItemContext(GoodsItemRawData rawData) + public static async UniTask CreateAsync(GoodsItemRawData rawData) { - Price = rawData.Price; - Rarity = rawData.Rarity; - ItemType = rawData.ItemType; - if (ItemType == ItemType.None) + var context = new GoodsItemContext { - Description = string.Empty; - Icon = null; - Title = string.Empty; + Price = rawData.Price, + Rarity = rawData.Rarity, + ItemType = rawData.ItemType + }; + + if (context.ItemType == ItemType.None) + { + context.Description = string.Empty; + context.Icon = null; + context.Title = string.Empty; } - else if (ItemType == ItemType.Weapon) + else if (context.ItemType == ItemType.Weapon) { var weapon = (DRWeapon)rawData.DataRow; - Title = weapon.Title; - Description = ItemDescUtility.CreateWeaponDescription(weapon); - GameEntry.SpriteCache.GetSprite(weapon.IconAssetName, sprite => Icon = sprite); + context.Title = weapon.Title; + context.Description = ItemDescUtility.CreateWeaponDescription(weapon); + context.Icon = await GameEntry.SpriteCache.GetSprite(weapon.IconAssetName); } else { var prop = (DRProp)rawData.DataRow; - Title = prop.Title; - Description = ItemDescUtility.CreatePropDescription(prop.Modifiers); - GameEntry.SpriteCache.GetSprite(prop.IconAssetName, sprite => Icon = sprite); + context.Title = prop.Title; + context.Description = ItemDescUtility.CreatePropDescription(prop.Modifiers); + context.Icon = await GameEntry.SpriteCache.GetSprite(prop.IconAssetName); } + + return context; } } } diff --git a/Assets/GameMain/Scripts/Presentation/Main/DisplayItemInfo/DisplayItemInfoForm.cs b/Assets/GameMain/Scripts/Presentation/Main/DisplayItemInfo/DisplayItemInfoForm.cs index a7f6902..c31454d 100644 --- a/Assets/GameMain/Scripts/Presentation/Main/DisplayItemInfo/DisplayItemInfoForm.cs +++ b/Assets/GameMain/Scripts/Presentation/Main/DisplayItemInfo/DisplayItemInfoForm.cs @@ -1,4 +1,5 @@ using System; +using Cysharp.Threading.Tasks; using SepCore.Event; using TMPro; using UnityEngine; @@ -36,7 +37,7 @@ namespace SepCore.UI private Vector3 _targetPos; - public void RefreshUI(DisplayItemInfoContext context) + public async UniTaskVoid RefreshUI(DisplayItemInfoContext context) { if (context == null) { @@ -64,20 +65,15 @@ namespace SepCore.UI _recycleButton.gameObject.SetActive(_context.ItemType == ItemType.Weapon); } - if (_iconArea != null) + if (_iconArea != null && !string.IsNullOrEmpty(_context.IconAssetName)) { - if (!string.IsNullOrEmpty(_context.IconAssetName)) + string iconAssetName = _context.IconAssetName; + var sprite = await GameEntry.SpriteCache.GetSprite(iconAssetName); + if (_context != null && _context.IconAssetName == iconAssetName) { - string iconAssetName = _context.IconAssetName; - GameEntry.SpriteCache.GetSprite(iconAssetName, sprite => - { - if (_context != null && _context.IconAssetName == iconAssetName) - { - _iconArea.OnInit(sprite, context.Rarity); - ResizeToFitContent(); - ApplyClampedTargetPos(); - } - }); + _iconArea.OnInit(sprite, context.Rarity); + ResizeToFitContent(); + ApplyClampedTargetPos(); } } diff --git a/Assets/GameMain/Scripts/Presentation/Main/Shop/ShopController.cs b/Assets/GameMain/Scripts/Presentation/Main/Shop/ShopController.cs index e0b5c75..c91307f 100644 --- a/Assets/GameMain/Scripts/Presentation/Main/Shop/ShopController.cs +++ b/Assets/GameMain/Scripts/Presentation/Main/Shop/ShopController.cs @@ -42,7 +42,7 @@ namespace SepCore.UI #region BuildContext - private ShopContext BuildContext(ShopRawData rawData) + private async UniTask BuildContext(ShopRawData rawData) { if (rawData == null) { @@ -55,7 +55,7 @@ namespace SepCore.UI List goodsItems = new List(); foreach (var item in rawData.GoodsItems) { - goodsItems.Add(new GoodsItemContext(item)); + goodsItems.Add(await GoodsItemContext.CreateAsync(item)); } return new ShopContext @@ -214,7 +214,7 @@ namespace SepCore.UI public async UniTask OpenUIAsync(ShopRawData rawData, float timeout = 30f) { - ShopContext context = BuildContext(rawData); + ShopContext context = await BuildContext(rawData); return await OpenFormAsync(context, timeout); } @@ -256,7 +256,7 @@ namespace SepCore.UI #region Service - private void RefreshGoodsItems(ShopRefreshResult result) + private async UniTask RefreshGoodsItems(ShopRefreshResult result) { if (result == null) { @@ -272,8 +272,8 @@ namespace SepCore.UI for (int i = 0; i < result.GoodsItems.Count; i++) { - if (i < Context.GoodsItems.Count) Context.GoodsItems[i] = new GoodsItemContext(result.GoodsItems[i]); - else Context.GoodsItems.Add(new GoodsItemContext(result.GoodsItems[i])); + if (i < Context.GoodsItems.Count) Context.GoodsItems[i] = await GoodsItemContext.CreateAsync(result.GoodsItems[i]); + else Context.GoodsItems.Add(await GoodsItemContext.CreateAsync(result.GoodsItems[i])); } if (Context.GoodsItems.Count != result.GoodsItems.Count) @@ -425,7 +425,7 @@ namespace SepCore.UI #region Event Handlers - private void Refresh(object sender, GameEventArgs e) + private async void Refresh(object sender, GameEventArgs e) { if (sender is not ShopForm) { @@ -443,7 +443,7 @@ namespace SepCore.UI return; } - RefreshGoodsItems(result); + await RefreshGoodsItems(result); } private void ShopPurchase(object sender, GameEventArgs e) diff --git a/Assets/GameMain/Scripts/Presentation/Main/View/DisplayItem.cs b/Assets/GameMain/Scripts/Presentation/Main/View/DisplayItem.cs index 4c32ad3..692358f 100644 --- a/Assets/GameMain/Scripts/Presentation/Main/View/DisplayItem.cs +++ b/Assets/GameMain/Scripts/Presentation/Main/View/DisplayItem.cs @@ -1,3 +1,4 @@ +using Cysharp.Threading.Tasks; using SepCore.Event; using UnityEngine; using UnityEngine.UI; @@ -19,7 +20,7 @@ namespace SepCore.UI _index = index; } - public void OnInit(DisplayItemContext context, int index) + public async UniTaskVoid OnInit(DisplayItemContext context, int index) { if (context == null) return; @@ -28,8 +29,8 @@ namespace SepCore.UI if (_iconArea != null) { - string iconName = _context.IconAssetName; - GameEntry.SpriteCache.GetSprite(iconName, sprite => { _iconArea.OnInit(sprite, context.Rarity); }); + var sprite = await GameEntry.SpriteCache.GetSprite(_context.IconAssetName); + _iconArea.OnInit(sprite, context.Rarity); } } diff --git a/Assets/GameMain/Scripts/Presentation/Main/View/LevelUpRewardItem.cs b/Assets/GameMain/Scripts/Presentation/Main/View/LevelUpRewardItem.cs index d87e9a6..7735b7e 100644 --- a/Assets/GameMain/Scripts/Presentation/Main/View/LevelUpRewardItem.cs +++ b/Assets/GameMain/Scripts/Presentation/Main/View/LevelUpRewardItem.cs @@ -1,3 +1,4 @@ +using Cysharp.Threading.Tasks; using TMPro; using UnityEngine; using UnityEngine.UI; @@ -32,16 +33,17 @@ namespace SepCore.UI if (_descriptionText != null) _descriptionText.text = context.Description; if (_iconArea != null) _iconArea.OnInit(context.Icon, context.ItemRarity); - LoadIcon(_context.IconAssetName); + LoadIcon(_context.IconAssetName).Forget(); } - private void LoadIcon(string iconAssetName) + private async UniTaskVoid LoadIcon(string iconAssetName) { if (_iconArea == null) return; if (string.IsNullOrEmpty(iconAssetName)) return; - GameEntry.SpriteCache.GetSprite(iconAssetName, sprite => _iconArea.SetIcon(sprite)); + var sprite = await GameEntry.SpriteCache.GetSprite(iconAssetName); + _iconArea.SetIcon(sprite); } } } \ No newline at end of file diff --git a/Assets/GameMain/Scripts/Runtime/BuiltinComponent/AsyncTask/ResourceAsyncExtension.cs b/Assets/GameMain/Scripts/Runtime/BuiltinComponent/AsyncTask/ResourceAsyncExtension.cs index db0b9d2..b892eac 100644 --- a/Assets/GameMain/Scripts/Runtime/BuiltinComponent/AsyncTask/ResourceAsyncExtension.cs +++ b/Assets/GameMain/Scripts/Runtime/BuiltinComponent/AsyncTask/ResourceAsyncExtension.cs @@ -1,4 +1,6 @@ +using System; using Cysharp.Threading.Tasks; +using GameFramework.Resource; using UnityGameFramework.Runtime; using ResourceApplySuccessEventArgs = UnityGameFramework.Runtime.ResourceApplySuccessEventArgs; using ResourceUpdateAllCompleteEventArgs = UnityGameFramework.Runtime.ResourceUpdateAllCompleteEventArgs; @@ -11,6 +13,24 @@ namespace SepCore.AsyncTask /// public static class ResourceAsyncExtension { + /// + /// 异步加载资源 + /// + /// 资源类型 + /// 资源组件 + /// 资源名称 + /// 加载优先级 + /// 加载完成的资源 + public static UniTask LoadAssetAsync(this ResourceComponent resourceComponent, string assetName, int priority = 0) + { + var tcs = AutoResetUniTaskCompletionSource.Create(); + resourceComponent.LoadAsset(assetName, priority, new LoadAssetCallbacks( + (_, asset, _, _) => tcs.TrySetResult((T)asset), + (_, _, errorMessage, _) => tcs.TrySetException(new Exception(errorMessage)) + )); + return tcs.Task; + } + /// /// 异步等待资源更新完成 /// diff --git a/Assets/GameMain/Scripts/Runtime/CustomComponent/SpriteCache/SpriteCacheComponent.cs b/Assets/GameMain/Scripts/Runtime/CustomComponent/SpriteCache/SpriteCacheComponent.cs index 1e454f9..42c043b 100644 --- a/Assets/GameMain/Scripts/Runtime/CustomComponent/SpriteCache/SpriteCacheComponent.cs +++ b/Assets/GameMain/Scripts/Runtime/CustomComponent/SpriteCache/SpriteCacheComponent.cs @@ -1,8 +1,9 @@ -using System; using System.Collections.Generic; -using SepCore.Definition; +using Cysharp.Threading.Tasks; using GameFramework.Resource; +using SepCore.AsyncTask; using SepCore.CustomUtility; +using SepCore.Definition; using UnityEngine; using UnityGameFramework.Runtime; @@ -22,45 +23,22 @@ namespace SepCore.SpriteCache _resource = GameEntry.Resource; } - public void GetSprite(string assetName, Action callback) + public async UniTask GetSprite(string assetName) { if (_spriteCache.TryGetValue(assetName, out var sprite)) - { - callback?.Invoke(sprite); - return; - } - else - { - _resource.LoadAsset - ( - AssetUtility.GetUITextureIconAsset(assetName), - Constant.AssetPriority.UIFormAsset, - new LoadAssetCallbacks( - (resourcePath, asset, duration, userData) => - { - Log.Debug(resourcePath); - Texture2D texture = asset as Texture2D; - if (texture != null) - { - Sprite newSprite = Sprite.Create( - texture, - new Rect(0, 0, texture.width, texture.height), - _defaultPivot, - _pixelsPerUnit); - _spriteCache.TryAdd(assetName, newSprite); - callback?.Invoke(newSprite); - } - }, - (resourcePath, status, errorMessage, userData) => - { - Log.Error("Can not load icon '{0}' from '{1}' with error message '{2}'.", - assetName, - resourcePath, - errorMessage); - } - ) - ); - } + return sprite; + + var texture = await _resource.LoadAssetAsync( + AssetUtility.GetUITextureIconAsset(assetName), + Constant.AssetPriority.UIFormAsset); + + var newSprite = Sprite.Create( + texture, + new Rect(0, 0, texture.width, texture.height), + _defaultPivot, + _pixelsPerUnit); + _spriteCache.TryAdd(assetName, newSprite); + return newSprite; } private void OnDestroy() From 7ac38cd99931cf383eb0906d86bf5d4266a41e25 Mon Sep 17 00:00:00 2001 From: SepComet <202308010230@stu.csust.edu.cn> Date: Sat, 6 Jun 2026 13:20:20 +0800 Subject: [PATCH 02/22] Update AsyncTaskHelper.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - WaitEventAsync() 增加可选 CancellationToken - WaitSuccessOrFailureAsync() 增加可选 CancellationToken - 保持现有调用兼容:原来的参数顺序和默认行为不变 - 抽出统一完成/清理逻辑: - 成功 - 失败 - 超时 - 取消 - 防止重复完成导致重复 Unsubscribe - 超时任务在事件完成后会被取消,避免后台 delay 继续跑 - 取消时会反订阅事件,适合后续 Procedure 生命周期迁移 --- .../AsyncTask/AsyncTaskHelper.cs | 152 ++++++++++++++---- 1 file changed, 123 insertions(+), 29 deletions(-) diff --git a/Assets/GameMain/Scripts/Runtime/BuiltinComponent/AsyncTask/AsyncTaskHelper.cs b/Assets/GameMain/Scripts/Runtime/BuiltinComponent/AsyncTask/AsyncTaskHelper.cs index 001196d..e699d11 100644 --- a/Assets/GameMain/Scripts/Runtime/BuiltinComponent/AsyncTask/AsyncTaskHelper.cs +++ b/Assets/GameMain/Scripts/Runtime/BuiltinComponent/AsyncTask/AsyncTaskHelper.cs @@ -17,13 +17,43 @@ namespace SepCore.AsyncTask /// 事件ID /// 事件过滤条件 /// 超时时间(秒),0表示不超时 + /// 取消令牌 /// 事件参数 - public static UniTask WaitEventAsync(int eventId, Func predicate = null, float timeout = 0f) where T : GameEventArgs + public static UniTask WaitEventAsync( + int eventId, + Func predicate = null, + float timeout = 0f, + CancellationToken cancellationToken = default) where T : GameEventArgs { var tcs = new UniTaskCompletionSource(); var eventComponent = global::GameEntry.Event; + var timeoutCancellationTokenSource = timeout > 0f ? new CancellationTokenSource() : null; + CancellationTokenRegistration cancellationRegistration = default; + int isCompleted = 0; EventHandler handler = null; + void Cleanup(bool disposeCancellationRegistration) + { + eventComponent.Unsubscribe(eventId, handler); + timeoutCancellationTokenSource?.Cancel(); + timeoutCancellationTokenSource?.Dispose(); + if (disposeCancellationRegistration) + { + cancellationRegistration.Dispose(); + } + } + + bool TryComplete(bool disposeCancellationRegistration = true) + { + if (Interlocked.Exchange(ref isCompleted, 1) != 0) + { + return false; + } + + Cleanup(disposeCancellationRegistration); + return true; + } + handler = (_, e) => { var args = e as T; @@ -31,23 +61,35 @@ namespace SepCore.AsyncTask if (predicate != null && !predicate(args)) return; - eventComponent.Unsubscribe(eventId, handler); - tcs.TrySetResult(args); + if (TryComplete()) + { + tcs.TrySetResult(args); + } }; eventComponent.Subscribe(eventId, handler); - // 超时处理 - if (timeout > 0f) + if (cancellationToken.CanBeCanceled) { - UniTask.Delay(TimeSpan.FromSeconds(timeout), cancellationToken: CancellationToken.None) - .ContinueWith(() => + cancellationRegistration = cancellationToken.Register(() => + { + if (TryComplete(false)) { - if (tcs.TrySetException(new TimeoutException($"等待事件超时: {timeout}秒"))) - { - eventComponent.Unsubscribe(eventId, handler); - } - }); + tcs.TrySetCanceled(cancellationToken); + } + }); + } + + // 超时处理 + if (timeoutCancellationTokenSource != null) + { + WaitTimeoutAsync(timeout, timeoutCancellationTokenSource.Token, () => + { + if (TryComplete()) + { + tcs.TrySetException(new TimeoutException($"等待事件超时: {timeout}秒")); + } + }).Forget(); } return tcs.Task; @@ -63,22 +105,49 @@ namespace SepCore.AsyncTask /// 成功事件过滤条件 /// 失败事件过滤条件 /// 超时时间(秒),0表示不超时 + /// 取消令牌 /// 成功事件参数 public static UniTask WaitSuccessOrFailureAsync( int successEventId, int failureEventId, Func successPredicate = null, Func failurePredicate = null, - float timeout = 0f) + float timeout = 0f, + CancellationToken cancellationToken = default) where TSuccess : GameEventArgs where TFailure : GameEventArgs { var tcs = new UniTaskCompletionSource(); var eventComponent = global::GameEntry.Event; + var timeoutCancellationTokenSource = timeout > 0f ? new CancellationTokenSource() : null; + CancellationTokenRegistration cancellationRegistration = default; + int isCompleted = 0; // 先声明两个 handler,再赋值 lambda,避免前向引用 EventHandler successHandler = null; EventHandler failureHandler = null; + void Cleanup(bool disposeCancellationRegistration) + { + eventComponent.Unsubscribe(successEventId, successHandler); + eventComponent.Unsubscribe(failureEventId, failureHandler); + timeoutCancellationTokenSource?.Cancel(); + timeoutCancellationTokenSource?.Dispose(); + if (disposeCancellationRegistration) + { + cancellationRegistration.Dispose(); + } + } + + bool TryComplete(bool disposeCancellationRegistration = true) + { + if (Interlocked.Exchange(ref isCompleted, 1) != 0) + { + return false; + } + + Cleanup(disposeCancellationRegistration); + return true; + } successHandler = (_, e) => { @@ -87,9 +156,10 @@ namespace SepCore.AsyncTask if (successPredicate != null && !successPredicate(args)) return; - eventComponent.Unsubscribe(successEventId, successHandler); - eventComponent.Unsubscribe(failureEventId, failureHandler); - tcs.TrySetResult(args); + if (TryComplete()) + { + tcs.TrySetResult(args); + } }; failureHandler = (_, e) => @@ -99,29 +169,53 @@ namespace SepCore.AsyncTask if (failurePredicate != null && !failurePredicate(args)) return; - eventComponent.Unsubscribe(successEventId, successHandler); - eventComponent.Unsubscribe(failureEventId, failureHandler); - tcs.TrySetException(new Exception($"操作失败: {args}")); + if (TryComplete()) + { + tcs.TrySetException(new Exception($"操作失败: {args}")); + } }; eventComponent.Subscribe(successEventId, successHandler); eventComponent.Subscribe(failureEventId, failureHandler); - // 超时处理 - if (timeout > 0f) + if (cancellationToken.CanBeCanceled) { - UniTask.Delay(TimeSpan.FromSeconds(timeout), cancellationToken: CancellationToken.None) - .ContinueWith(() => + cancellationRegistration = cancellationToken.Register(() => + { + if (TryComplete(false)) { - if (tcs.TrySetException(new TimeoutException($"等待事件超时: {timeout}秒"))) - { - eventComponent.Unsubscribe(successEventId, successHandler); - eventComponent.Unsubscribe(failureEventId, failureHandler); - } - }); + tcs.TrySetCanceled(cancellationToken); + } + }); + } + + // 超时处理 + if (timeoutCancellationTokenSource != null) + { + WaitTimeoutAsync(timeout, timeoutCancellationTokenSource.Token, () => + { + if (TryComplete()) + { + tcs.TrySetException(new TimeoutException($"等待事件超时: {timeout}秒")); + } + }).Forget(); } return tcs.Task; } + + private static async UniTaskVoid WaitTimeoutAsync(float timeout, CancellationToken cancellationToken, Action onTimeout) + { + try + { + await UniTask.Delay(TimeSpan.FromSeconds(timeout), cancellationToken: cancellationToken); + } + catch (OperationCanceledException) + { + return; + } + + onTimeout?.Invoke(); + } } } From 788f717335b98e95a478cdb51d1551c3847ea11b Mon Sep 17 00:00:00 2001 From: SepComet <202308010230@stu.csust.edu.cn> Date: Sat, 6 Jun 2026 13:33:33 +0800 Subject: [PATCH 03/22] =?UTF-8?q?=E8=B0=83=E6=95=B4=20UIRouter=20=E7=9A=84?= =?UTF-8?q?=E6=89=93=E5=BC=80=E5=85=B3=E9=97=AD=20UI=20=E7=9A=84=E8=BF=94?= =?UTF-8?q?=E5=9B=9E=E5=80=BC=EF=BC=8C=E7=BB=9F=E4=B8=80=E6=88=90=20UniTas?= =?UTF-8?q?k?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Presentation/Common/Dialog/DialogController.cs | 8 ++++---- .../DisplayItemInfo/DisplayItemInfoController.cs | 8 ++++---- .../Scripts/Presentation/Main/Hud/HudController.cs | 6 +++--- .../Presentation/Main/LevelUp/LevelUpController.cs | 12 ++++++------ .../Presentation/Main/Shop/ShopController.cs | 13 +++++++------ .../Presentation/Menu/Menu/MenuController.cs | 6 +++--- .../Menu/SelectRole/SelectRoleController.cs | 10 +++++----- Assets/GameMain/Scripts/Procedure/ProcedureMenu.cs | 4 ++-- .../Runtime/CustomComponent/UIRouterComponent.cs | 4 ++-- .../Runtime/UIBase/Base/IUIFormController.cs | 2 +- .../Scripts/Runtime/UIBase/Base/UIControllerBase.cs | 2 +- 11 files changed, 38 insertions(+), 37 deletions(-) diff --git a/Assets/GameMain/Scripts/Presentation/Common/Dialog/DialogController.cs b/Assets/GameMain/Scripts/Presentation/Common/Dialog/DialogController.cs index b59b5fc..05add67 100644 --- a/Assets/GameMain/Scripts/Presentation/Common/Dialog/DialogController.cs +++ b/Assets/GameMain/Scripts/Presentation/Common/Dialog/DialogController.cs @@ -41,7 +41,7 @@ namespace SepCore.UI }; } - public override async UniTask OpenUIAsync(object userData = null, float timeout = 30f) + public override async UniTask OpenUIAsync(object userData = null, float timeout = 30f) { if (userData is not DialogRawData rawData) { @@ -54,14 +54,14 @@ namespace SepCore.UI Log.Warning("DialogController.OpenUIAsync() rawData is required."); } - return null; + return; } DialogContext context = BuildContext(rawData); if (context == null) { Log.Warning("DialogController.OpenUIAsync() rawData is invalid."); - return null; + return; } _onClickConfirmGFAction = rawData.OnClickConfirm; @@ -69,7 +69,7 @@ namespace SepCore.UI _onClickOtherGFAction = rawData.OnClickOther; _currentUserData = rawData.UserData; - return await OpenFormAsync(context, timeout); + await OpenFormAsync(context, timeout); } public override async UniTask CloseUIAsync(object userData = null, float timeout = 30f) diff --git a/Assets/GameMain/Scripts/Presentation/Main/DisplayItemInfo/DisplayItemInfoController.cs b/Assets/GameMain/Scripts/Presentation/Main/DisplayItemInfo/DisplayItemInfoController.cs index 2bb9728..aa6a8fe 100644 --- a/Assets/GameMain/Scripts/Presentation/Main/DisplayItemInfo/DisplayItemInfoController.cs +++ b/Assets/GameMain/Scripts/Presentation/Main/DisplayItemInfo/DisplayItemInfoController.cs @@ -46,7 +46,7 @@ namespace SepCore.UI return new DisplayItemInfoContext(rawData); } - public override async UniTask OpenUIAsync(object userData = null, float timeout = 30f) + public override async UniTask OpenUIAsync(object userData = null, float timeout = 30f) { if (userData is not DisplayItemInfoRawData rawData) { @@ -59,18 +59,18 @@ namespace SepCore.UI Log.Warning("DisplayItemInfoController.OpenUIAsync() rawData is required."); } - return null; + return; } DisplayItemInfoContext context = BuildContext(rawData); if (context == null) { Log.Warning("DisplayItemInfoController.OpenUIAsync() rawData is invalid."); - return null; + return; } _locked = false; - return await OpenFormAsync(context, timeout); + await OpenFormAsync(context, timeout); } public override async UniTask CloseUIAsync(object userData = null, float timeout = 30f) diff --git a/Assets/GameMain/Scripts/Presentation/Main/Hud/HudController.cs b/Assets/GameMain/Scripts/Presentation/Main/Hud/HudController.cs index c534034..e14f28f 100644 --- a/Assets/GameMain/Scripts/Presentation/Main/Hud/HudController.cs +++ b/Assets/GameMain/Scripts/Presentation/Main/Hud/HudController.cs @@ -18,15 +18,15 @@ namespace SepCore.UI return new HudContext(); } - public override async UniTask OpenUIAsync(object userData = null, float timeout = 30f) + public override async UniTask OpenUIAsync(object userData = null, float timeout = 30f) { if (userData != null) { Log.Warning("HudController.OpenUIAsync() userData type is invalid."); - return null; + return; } - return await OpenFormAsync(BuildHudContext(), timeout); + await OpenFormAsync(BuildHudContext(), timeout); } public override void BindUseCase(IUIUseCase useCase) diff --git a/Assets/GameMain/Scripts/Presentation/Main/LevelUp/LevelUpController.cs b/Assets/GameMain/Scripts/Presentation/Main/LevelUp/LevelUpController.cs index f3bd461..4c10af5 100644 --- a/Assets/GameMain/Scripts/Presentation/Main/LevelUp/LevelUpController.cs +++ b/Assets/GameMain/Scripts/Presentation/Main/LevelUp/LevelUpController.cs @@ -57,7 +57,7 @@ namespace SepCore.UI { Title = reward.Title, Icon = null, - ItemRarity = reward.Rarity, + ItemRarity = reward.Rarity, Description = ItemDescUtility.CreatePropDescription(reward.Modifiers), IconAssetName = reward.IconAssetName }); @@ -70,27 +70,27 @@ namespace SepCore.UI }; } - public override async UniTask OpenUIAsync(object userData = null, float timeout = 30f) + public override async UniTask OpenUIAsync(object userData = null, float timeout = 30f) { if (userData is LevelUpRawData rawData) { - return await OpenUIAsync(rawData, timeout); + await OpenUIAsync(rawData, timeout); } if (userData != null) { Log.Warning("LevelUpController.OpenUIAsync() userData type is invalid."); - return null; + return; } if (_useCase == null) { Log.Error("LevelUpController.OpenUIAsync() useCase is null."); - return null; + return; } LevelUpRawData initialRawData = _useCase.CreateInitialModel(); - return await OpenUIAsync(initialRawData, timeout); + await OpenUIAsync(initialRawData, timeout); } public async UniTask OpenUIAsync(LevelUpRawData rawData, float timeout = 30f) diff --git a/Assets/GameMain/Scripts/Presentation/Main/Shop/ShopController.cs b/Assets/GameMain/Scripts/Presentation/Main/Shop/ShopController.cs index c91307f..7439e09 100644 --- a/Assets/GameMain/Scripts/Presentation/Main/Shop/ShopController.cs +++ b/Assets/GameMain/Scripts/Presentation/Main/Shop/ShopController.cs @@ -218,27 +218,27 @@ namespace SepCore.UI return await OpenFormAsync(context, timeout); } - public override async UniTask OpenUIAsync(object userData = null, float timeout = 30f) + public override async UniTask OpenUIAsync(object userData = null, float timeout = 30f) { if (userData is ShopRawData rawData) { - return await OpenUIAsync(rawData, timeout); + await OpenUIAsync(rawData, timeout); } if (userData != null) { Log.Warning("ShopController.OpenUIAsync() userData type is invalid."); - return null; + return; } if (_useCase == null) { Log.Error("ShopController.OpenUIAsync() useCase is null."); - return null; + return; } ShopRawData initialRawData = _useCase.CreateInitialModel(); - return await OpenUIAsync(initialRawData, timeout); + await OpenUIAsync(initialRawData, timeout); } public override void BindUseCase(IUIUseCase useCase) @@ -272,7 +272,8 @@ namespace SepCore.UI for (int i = 0; i < result.GoodsItems.Count; i++) { - if (i < Context.GoodsItems.Count) Context.GoodsItems[i] = await GoodsItemContext.CreateAsync(result.GoodsItems[i]); + if (i < Context.GoodsItems.Count) + Context.GoodsItems[i] = await GoodsItemContext.CreateAsync(result.GoodsItems[i]); else Context.GoodsItems.Add(await GoodsItemContext.CreateAsync(result.GoodsItems[i])); } diff --git a/Assets/GameMain/Scripts/Presentation/Menu/Menu/MenuController.cs b/Assets/GameMain/Scripts/Presentation/Menu/Menu/MenuController.cs index 500861e..a165d53 100644 --- a/Assets/GameMain/Scripts/Presentation/Menu/Menu/MenuController.cs +++ b/Assets/GameMain/Scripts/Presentation/Menu/Menu/MenuController.cs @@ -40,15 +40,15 @@ namespace SepCore.UI return new MenuContext(); } - public override async UniTask OpenUIAsync(object userData = null, float timeout = 30f) + public override async UniTask OpenUIAsync(object userData = null, float timeout = 30f) { if (userData != null) { Log.Warning("MenuController.OpenUIAsync() userData type is invalid."); - return null; + return; } - return await OpenFormAsync(BuildMenuContext(), timeout); + await OpenFormAsync(BuildMenuContext(), timeout); } public override void BindUseCase(IUIUseCase useCase) diff --git a/Assets/GameMain/Scripts/Presentation/Menu/SelectRole/SelectRoleController.cs b/Assets/GameMain/Scripts/Presentation/Menu/SelectRole/SelectRoleController.cs index dce580f..7860ebd 100644 --- a/Assets/GameMain/Scripts/Presentation/Menu/SelectRole/SelectRoleController.cs +++ b/Assets/GameMain/Scripts/Presentation/Menu/SelectRole/SelectRoleController.cs @@ -73,27 +73,27 @@ namespace SepCore.UI }; } - public override async UniTask OpenUIAsync(object userData = null, float timeout = 30f) + public override async UniTask OpenUIAsync(object userData = null, float timeout = 30f) { if (userData is SelectRoleRawData rawData) { - return await OpenUIAsync(rawData, timeout); + await OpenUIAsync(rawData, timeout); } if (userData != null) { Log.Warning("SelectRoleController.OpenUIAsync() userData type is invalid."); - return null; + return; } if (_useCase == null) { Log.Error("SelectRoleController.OpenUIAsync() useCase is null."); - return null; + return; } SelectRoleRawData initialRawData = _useCase.CreateModel(); - return await OpenUIAsync(initialRawData, timeout); + await OpenUIAsync(initialRawData, timeout); } public async UniTask OpenUIAsync(SelectRoleRawData rawData, float timeout = 30f) diff --git a/Assets/GameMain/Scripts/Procedure/ProcedureMenu.cs b/Assets/GameMain/Scripts/Procedure/ProcedureMenu.cs index e0b7bc2..12a20ef 100644 --- a/Assets/GameMain/Scripts/Procedure/ProcedureMenu.cs +++ b/Assets/GameMain/Scripts/Procedure/ProcedureMenu.cs @@ -23,11 +23,11 @@ namespace SepCore.Procedure #region FSM - protected override async void OnEnter(ProcedureOwner procedureOwner) + protected override void OnEnter(ProcedureOwner procedureOwner) { base.OnEnter(procedureOwner); - await GameEntry.UIRouter.OpenUIAsync(UIFormType.MenuForm); + GameEntry.UIRouter.OpenUIAsync(UIFormType.MenuForm); var useCase = new SelectRoleUseCase(StartGame); GameEntry.UIRouter.BindUIUseCase(UIFormType.SelectRoleForm, useCase); diff --git a/Assets/GameMain/Scripts/Runtime/CustomComponent/UIRouterComponent.cs b/Assets/GameMain/Scripts/Runtime/CustomComponent/UIRouterComponent.cs index 7e5e47a..c110278 100644 --- a/Assets/GameMain/Scripts/Runtime/CustomComponent/UIRouterComponent.cs +++ b/Assets/GameMain/Scripts/Runtime/CustomComponent/UIRouterComponent.cs @@ -66,12 +66,12 @@ namespace SepCore.UIRouter controller.BindUseCase(useCase); } - public UniTask OpenUIAsync(UIFormType uiFormType, object userData = null, float timeout = 30f) + public UniTask OpenUIAsync(UIFormType uiFormType, object userData = null, float timeout = 30f) { IUIFormController controller = GetOrCreateController(uiFormType); if (controller == null) { - return UniTask.FromResult(null); + return default; } return controller.OpenUIAsync(userData, timeout); diff --git a/Assets/GameMain/Scripts/Runtime/UIBase/Base/IUIFormController.cs b/Assets/GameMain/Scripts/Runtime/UIBase/Base/IUIFormController.cs index 2d656eb..d73d09f 100644 --- a/Assets/GameMain/Scripts/Runtime/UIBase/Base/IUIFormController.cs +++ b/Assets/GameMain/Scripts/Runtime/UIBase/Base/IUIFormController.cs @@ -4,7 +4,7 @@ namespace SepCore.UI { public interface IUIFormController { - UniTask OpenUIAsync(object userData = null, float timeout = 30f); + UniTask OpenUIAsync(object userData = null, float timeout = 30f); UniTask CloseUIAsync(object userData = null, float timeout = 30f); diff --git a/Assets/GameMain/Scripts/Runtime/UIBase/Base/UIControllerBase.cs b/Assets/GameMain/Scripts/Runtime/UIBase/Base/UIControllerBase.cs index 920e0be..92f4bde 100644 --- a/Assets/GameMain/Scripts/Runtime/UIBase/Base/UIControllerBase.cs +++ b/Assets/GameMain/Scripts/Runtime/UIBase/Base/UIControllerBase.cs @@ -100,7 +100,7 @@ namespace SepCore.UI } } - public abstract UniTask OpenUIAsync(object userData = null, float timeout = 30f); + public abstract UniTask OpenUIAsync(object userData = null, float timeout = 30f); public virtual UniTask CloseUIAsync(object userData = null, float timeout = 30f) { From f896a4a9c86a1bdda44a2092d1d839e84a0a711d Mon Sep 17 00:00:00 2001 From: SepComet <202308010230@stu.csust.edu.cn> Date: Sat, 6 Jun 2026 13:40:59 +0800 Subject: [PATCH 04/22] Update EntityAsyncExtension.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 为 EntityAsyncExtension 添加了便利的打开各类实体的方法 --- .../AsyncTask/EntityAsyncExtension.cs | 259 +++++++++++++++++- 1 file changed, 249 insertions(+), 10 deletions(-) diff --git a/Assets/GameMain/Scripts/Runtime/BuiltinComponent/AsyncTask/EntityAsyncExtension.cs b/Assets/GameMain/Scripts/Runtime/BuiltinComponent/AsyncTask/EntityAsyncExtension.cs index ea447e4..9f1a182 100644 --- a/Assets/GameMain/Scripts/Runtime/BuiltinComponent/AsyncTask/EntityAsyncExtension.cs +++ b/Assets/GameMain/Scripts/Runtime/BuiltinComponent/AsyncTask/EntityAsyncExtension.cs @@ -1,5 +1,12 @@ using System; +using System.Collections.Generic; +using System.Threading; using Cysharp.Threading.Tasks; +using SepCore.CustomUtility; +using SepCore.DataTable; +using SepCore.Definition; +using SepCore.Entity; +using SepCore.Entity.Weapon; using UnityGameFramework.Runtime; using HideEntityCompleteEventArgs = UnityGameFramework.Runtime.HideEntityCompleteEventArgs; using ShowEntityFailureEventArgs = UnityGameFramework.Runtime.ShowEntityFailureEventArgs; @@ -12,6 +19,11 @@ namespace SepCore.AsyncTask /// public static class EntityAsyncExtension { + private const string EntityNamespace = "SepCore.Entity."; + + private static readonly Dictionary _typeDict = new(); + private static readonly Dictionary _assetNameDict = new(); + /// /// 异步显示实体 /// @@ -22,6 +34,7 @@ namespace SepCore.AsyncTask /// 实体组名称 /// 用户自定义数据 /// 超时时间(秒),0表示不超时 + /// 取消令牌 /// 显示的实体 public static UniTask ShowEntityAsync(this EntityComponent entityComponent, int entityId, @@ -29,17 +42,46 @@ namespace SepCore.AsyncTask string entityAssetName, string entityGroupName, object userData = null, - float timeout = 30f) + float timeout = 30f, + CancellationToken cancellationToken = default) + { + return ShowEntityAsync(entityComponent, entityId, entityLogicType, entityAssetName, entityGroupName, 0, + userData, timeout, cancellationToken); + } + + /// + /// 异步显示实体 + /// + /// 实体组件 + /// 实体编号 + /// 实体逻辑类型 + /// 实体资源名称 + /// 实体组名称 + /// 加载优先级 + /// 用户自定义数据 + /// 超时时间(秒),0表示不超时 + /// 取消令牌 + /// 显示的实体 + public static UniTask ShowEntityAsync(this EntityComponent entityComponent, + int entityId, + Type entityLogicType, + string entityAssetName, + string entityGroupName, + int priority, + object userData = null, + float timeout = 30f, + CancellationToken cancellationToken = default) { UniTask waitTask = AsyncTaskHelper.WaitSuccessOrFailureAsync( ShowEntitySuccessEventArgs.EventId, ShowEntityFailureEventArgs.EventId, successArgs => successArgs.Entity.Id == entityId && ReferenceEquals(successArgs.UserData, userData), failureArgs => failureArgs.EntityId == entityId && ReferenceEquals(failureArgs.UserData, userData), - timeout + timeout, + cancellationToken ).ContinueWith(successArgs => successArgs.Entity); - entityComponent.ShowEntity(entityId, entityLogicType, entityAssetName, entityGroupName, userData); + entityComponent.ShowEntity(entityId, entityLogicType, entityAssetName, entityGroupName, priority, userData); return waitTask; } @@ -53,26 +95,157 @@ namespace SepCore.AsyncTask /// 实体组名称 /// 用户自定义数据 /// 超时时间(秒),0表示不超时 + /// 取消令牌 /// 显示的实体 public static UniTask ShowEntityAsync(this EntityComponent entityComponent, int entityId, string entityAssetName, string entityGroupName, object userData = null, - float timeout = 30f) where T : EntityLogic + float timeout = 30f, + CancellationToken cancellationToken = default) where T : EntityLogic + { + return ShowEntityAsync(entityComponent, entityId, entityAssetName, entityGroupName, 0, userData, timeout, + cancellationToken); + } + + /// + /// 异步显示实体(泛型版本) + /// + /// 实体逻辑类型 + /// 实体组件 + /// 实体编号 + /// 实体资源名称 + /// 实体组名称 + /// 加载优先级 + /// 用户自定义数据 + /// 超时时间(秒),0表示不超时 + /// 取消令牌 + /// 显示的实体 + public static UniTask ShowEntityAsync(this EntityComponent entityComponent, + int entityId, + string entityAssetName, + string entityGroupName, + int priority, + object userData = null, + float timeout = 30f, + CancellationToken cancellationToken = default) where T : EntityLogic { UniTask waitTask = AsyncTaskHelper.WaitSuccessOrFailureAsync( ShowEntitySuccessEventArgs.EventId, ShowEntityFailureEventArgs.EventId, successArgs => successArgs.Entity.Id == entityId && ReferenceEquals(successArgs.UserData, userData), failureArgs => failureArgs.EntityId == entityId && ReferenceEquals(failureArgs.UserData, userData), - timeout + timeout, + cancellationToken ).ContinueWith(successArgs => (T)successArgs.Entity.Logic); - entityComponent.ShowEntity(entityId, entityAssetName, entityGroupName, userData); + entityComponent.ShowEntity(entityId, entityAssetName, entityGroupName, priority, userData); return waitTask; } + /// + /// 异步显示玩家实体。 + /// + public static UniTask ShowPlayerAsync(this EntityComponent entityComponent, + PlayerData data, + float timeout = 30f, + CancellationToken cancellationToken = default) + { + return ShowEntityByDataAsync(entityComponent, typeof(Player), "Player", + Constant.AssetPriority.PlayerAsset, data, timeout, cancellationToken); + } + + /// + /// 异步显示敌人实体。 + /// + public static UniTask ShowEnemyAsync(this EntityComponent entityComponent, + EnemyData data, + float timeout = 30f, + CancellationToken cancellationToken = default) + { + if (data == null) + { + Log.Warning("Enemy data is invalid."); + return UniTask.FromResult(null); + } + + Type enemyType = TryGetType(data.EnemyType.ToString()); + string assetName = TryGetAssetName(data.EntityTypeId); + if (enemyType == null || string.IsNullOrEmpty(assetName)) + { + return UniTask.FromResult(null); + } + + return entityComponent + .ShowEntityAsync(data.Id, enemyType, AssetUtility.GetEntityAsset(assetName), "Enemy", + Constant.AssetPriority.BulletAsset, data, timeout, cancellationToken) + .ContinueWith(entity => entity?.Logic as EnemyBase); + } + + /// + /// 异步显示武器实体。 + /// + public static UniTask ShowWeaponAsync(this EntityComponent entityComponent, + WeaponData data, + float timeout = 30f, + CancellationToken cancellationToken = default) + { + if (data == null) + { + Log.Warning("Weapon data is invalid."); + return UniTask.FromResult(null); + } + + Type weaponType = TryGetType("Weapon." + data.WeaponType); + string assetName = TryGetAssetName(data.EntityTypeId); + if (weaponType == null || string.IsNullOrEmpty(assetName)) + { + return UniTask.FromResult(null); + } + + return entityComponent + .ShowEntityAsync(data.Id, weaponType, AssetUtility.GetEntityAsset(assetName), "Weapon", + Constant.AssetPriority.BulletAsset, data, timeout, cancellationToken) + .ContinueWith(entity => entity?.Logic as WeaponBase); + } + + /// + /// 异步显示特效实体。 + /// + public static UniTask ShowEffectAsync(this EntityComponent entityComponent, + EffectData data, + float timeout = 30f, + CancellationToken cancellationToken = default) + { + return ShowEntityByDataAsync(entityComponent, typeof(Effect), "Effect", + Constant.AssetPriority.EffectAsset, data, timeout, cancellationToken); + } + + /// + /// 异步显示金币掉落实体。 + /// + public static UniTask ShowCoinAsync(this EntityComponent entityComponent, + CoinData data, + float timeout = 30f, + CancellationToken cancellationToken = default) + { + return ShowEntityByDataAsync(entityComponent, typeof(CoinEntity), "Drop", + Constant.AssetPriority.EffectAsset, data, timeout, cancellationToken); + } + + /// + /// 异步显示经验掉落实体。 + /// + public static UniTask ShowExpAsync(this EntityComponent entityComponent, + ExpData data, + float timeout = 30f, + CancellationToken cancellationToken = default) + { + return ShowEntityByDataAsync(entityComponent, typeof(ExpEntity), "Drop", + Constant.AssetPriority.EffectAsset, data, timeout, cancellationToken); + } + /// /// 异步隐藏实体 /// @@ -80,16 +253,19 @@ namespace SepCore.AsyncTask /// 实体编号 /// 用户自定义数据 /// 超时时间(秒),0表示不超时 + /// 取消令牌 /// 隐藏完成事件 public static UniTask HideEntityAsync(this EntityComponent entityComponent, int entityId, object userData = null, - float timeout = 30f) + float timeout = 30f, + CancellationToken cancellationToken = default) { UniTask waitTask = AsyncTaskHelper.WaitEventAsync( HideEntityCompleteEventArgs.EventId, args => args.EntityId == entityId, - timeout + timeout, + cancellationToken ); entityComponent.HideEntity(entityId, userData); @@ -103,13 +279,76 @@ namespace SepCore.AsyncTask /// 要隐藏的实体 /// 用户自定义数据 /// 超时时间(秒),0表示不超时 + /// 取消令牌 /// 隐藏完成事件 public static UniTask HideEntityAsync(this EntityComponent entityComponent, UnityGameFramework.Runtime.Entity entity, object userData = null, - float timeout = 30f) + float timeout = 30f, + CancellationToken cancellationToken = default) { - return HideEntityAsync(entityComponent, entity.Id, userData, timeout); + return HideEntityAsync(entityComponent, entity.Id, userData, timeout, cancellationToken); + } + + private static UniTask ShowEntityByDataAsync(EntityComponent entityComponent, + Type logicType, + string entityGroupName, + int priority, + EntityDataBase data, + float timeout, + CancellationToken cancellationToken) where T : EntityLogic + { + if (data == null) + { + Log.Warning("Data is invalid."); + return UniTask.FromResult(null); + } + + DREntity drEntity = GameEntry.DataTable.GetDataTableRow(data.TypeId); + if (drEntity == null) + { + Log.Warning("Can not load entity id '{0}' from data table.", data.TypeId.ToString()); + return UniTask.FromResult(null); + } + + return entityComponent.ShowEntityAsync(data.Id, AssetUtility.GetEntityAsset(drEntity.AssetName), + entityGroupName, priority, data, timeout, cancellationToken); + } + + private static Type TryGetType(string rawTypeName) + { + string typeName = EntityNamespace + rawTypeName; + if (!_typeDict.TryGetValue(typeName, out Type type)) + { + type = Type.GetType(typeName); + if (type == null) + { + Log.Warning("Can not load entity type '{0}'.", typeName); + return null; + } + + _typeDict.Add(typeName, type); + } + + return type; + } + + private static string TryGetAssetName(int entityId) + { + if (!_assetNameDict.TryGetValue(entityId, out string assetName)) + { + DREntity drEntity = GameEntry.DataTable.GetDataTableRow(entityId); + if (drEntity == null) + { + Log.Warning("Can not load entity id '{0}' from data table.", entityId.ToString()); + return null; + } + + assetName = drEntity.AssetName; + _assetNameDict.Add(entityId, assetName); + } + + return assetName; } } } From 41ff2c6df83e79bb91afa3bdfff6593575bb6ce9 Mon Sep 17 00:00:00 2001 From: SepComet <202308010230@stu.csust.edu.cn> Date: Sat, 6 Jun 2026 14:12:05 +0800 Subject: [PATCH 05/22] =?UTF-8?q?=E8=BF=81=E7=A7=BB=20Player/ProcedureGame?= =?UTF-8?q?/ShopUseCase=20=E9=87=8C=E7=9A=84=E5=AE=9E=E4=BD=93=E6=96=B9?= =?UTF-8?q?=E6=B3=95=E8=B0=83=E7=94=A8=E9=A3=8E=E6=A0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Player/ShopUseCase:调整了 Weapon 实体的打开方式 - ProcedureGame:调整初始化时的 Hud 和 Player 的初始化写法,流程更清楚 --- .../Presentation/Main/Shop/ShopController.cs | 7 +- .../Scripts/Procedure/Game/GameStateBase.cs | 11 +- .../Scripts/Procedure/Game/GameStateBattle.cs | 9 +- .../Procedure/Game/GameStateLevelUp.cs | 16 ++- .../Scripts/Procedure/Game/GameStateShop.cs | 16 +-- .../Scripts/Procedure/Game/ProcedureGame.cs | 100 +++++++----------- .../Runtime/Entity/EntityLogic/Player.cs | 50 +++++---- .../Runtime/UIBase/Main/Shop/ShopUseCase.cs | 16 ++- 8 files changed, 107 insertions(+), 118 deletions(-) diff --git a/Assets/GameMain/Scripts/Presentation/Main/Shop/ShopController.cs b/Assets/GameMain/Scripts/Presentation/Main/Shop/ShopController.cs index 7439e09..5eee229 100644 --- a/Assets/GameMain/Scripts/Presentation/Main/Shop/ShopController.cs +++ b/Assets/GameMain/Scripts/Presentation/Main/Shop/ShopController.cs @@ -448,6 +448,11 @@ namespace SepCore.UI } private void ShopPurchase(object sender, GameEventArgs e) + { + ShopPurchaseAsync(sender, e).Forget(); + } + + private async UniTaskVoid ShopPurchaseAsync(object sender, GameEventArgs e) { if (sender is not ShopForm) { @@ -459,7 +464,7 @@ namespace SepCore.UI return; } - ShopPurchaseResult result = _useCase.TryPurchase(args.GoodsIndex); + ShopPurchaseResult result = await _useCase.TryPurchaseAsync(args.GoodsIndex); if (result == null) { return; diff --git a/Assets/GameMain/Scripts/Procedure/Game/GameStateBase.cs b/Assets/GameMain/Scripts/Procedure/Game/GameStateBase.cs index fd4cfee..ae91c3b 100644 --- a/Assets/GameMain/Scripts/Procedure/Game/GameStateBase.cs +++ b/Assets/GameMain/Scripts/Procedure/Game/GameStateBase.cs @@ -10,12 +10,11 @@ namespace SepCore.Procedure { public abstract GameStateType GameStateType { get; } public abstract void OnInit(ProcedureGame master); - public abstract void OnEnter(IFsm procedureOwner); + public abstract void OnEnter(); - public abstract void OnUpdate(IFsm procedureOwner, float elapseSeconds, - float realElapseSeconds); + public abstract void OnUpdate(float elapseSeconds, float realElapseSeconds); - public abstract void OnLeave(IFsm procedureOwner); - public abstract void OnDestroy(IFsm procedureOwner); + public abstract void OnLeave(); + public abstract void OnDestroy(); } -} \ No newline at end of file +} diff --git a/Assets/GameMain/Scripts/Procedure/Game/GameStateBattle.cs b/Assets/GameMain/Scripts/Procedure/Game/GameStateBattle.cs index 4ed6f24..fe674fe 100644 --- a/Assets/GameMain/Scripts/Procedure/Game/GameStateBattle.cs +++ b/Assets/GameMain/Scripts/Procedure/Game/GameStateBattle.cs @@ -38,7 +38,7 @@ namespace SepCore.Procedure _procedureGame = master; } - public override void OnEnter(IFsm procedureOwner) + public override void OnEnter() { _currentLevel = _procedureGame.CurrentLevel; @@ -55,8 +55,7 @@ namespace SepCore.Procedure if (Player != null) Player.Enable = true; } - public override void OnUpdate(IFsm procedureOwner, float elapseSeconds, - float realElapseSeconds) + public override void OnUpdate(float elapseSeconds, float realElapseSeconds) { if (_levelTimeLeft < 0) { @@ -77,7 +76,7 @@ namespace SepCore.Procedure GameEntry.Event.Fire(this, LevelProcessEventArgs.Create((int)_levelTimeLeft)); } - public override void OnLeave(IFsm procedureOwner) + public override void OnLeave() { // 隐藏所有敌人实体 _enemyManager.OnReset(); @@ -97,7 +96,7 @@ namespace SepCore.Procedure HideEntityGroup("EnemyProjectile"); } - public override void OnDestroy(IFsm procedureOwner) + public override void OnDestroy() { _enemyManager = null; _procedureGame = null; diff --git a/Assets/GameMain/Scripts/Procedure/Game/GameStateLevelUp.cs b/Assets/GameMain/Scripts/Procedure/Game/GameStateLevelUp.cs index d8708ef..7c93ce9 100644 --- a/Assets/GameMain/Scripts/Procedure/Game/GameStateLevelUp.cs +++ b/Assets/GameMain/Scripts/Procedure/Game/GameStateLevelUp.cs @@ -12,7 +12,7 @@ namespace SepCore.Procedure public override GameStateType GameStateType => GameStateType.LevelUp; private ProcedureGame _procedureGame; - + public bool IsCompleted { get; set; } #region FSM @@ -22,22 +22,21 @@ namespace SepCore.Procedure Log.Debug("GameStateLevelUp::OnInit"); _procedureGame = master; - + var useCase = new LevelUpUseCase(_procedureGame.Player, () => IsCompleted = true); GameEntry.UIRouter.BindUIUseCase(UIFormType.LevelUpForm, useCase); } - public override void OnEnter(IFsm procedureOwner) + public override void OnEnter() { Log.Debug("GameStateLevelUp::OnEnter"); IsCompleted = false; - + GameEntry.UIRouter.OpenUIAsync(UIFormType.LevelUpForm).Forget(); } - public override void OnUpdate(IFsm procedureOwner, float elapseSeconds, - float realElapseSeconds) + public override void OnUpdate(float elapseSeconds, float realElapseSeconds) { if (IsCompleted) { @@ -45,14 +44,14 @@ namespace SepCore.Procedure } } - public override void OnLeave(IFsm procedureOwner) + public override void OnLeave() { Log.Debug("GameStateLevelUp::OnLeave"); GameEntry.UIRouter.CloseUIAsync(UIFormType.LevelUpForm).Forget(); } - public override void OnDestroy(IFsm procedureOwner) + public override void OnDestroy() { _procedureGame = null; @@ -60,6 +59,5 @@ namespace SepCore.Procedure } #endregion - } } diff --git a/Assets/GameMain/Scripts/Procedure/Game/GameStateShop.cs b/Assets/GameMain/Scripts/Procedure/Game/GameStateShop.cs index 07497fd..61a00de 100644 --- a/Assets/GameMain/Scripts/Procedure/Game/GameStateShop.cs +++ b/Assets/GameMain/Scripts/Procedure/Game/GameStateShop.cs @@ -21,12 +21,13 @@ namespace SepCore.Procedure { Log.Debug("GameStateShop::OnInit"); _procedureGame = master; - - var shopFormUseCase = new ShopUseCase(_procedureGame.Player, _procedureGame.CurrentLevel, () => ShopFinish = true); + + var shopFormUseCase = + new ShopUseCase(_procedureGame.Player, _procedureGame.CurrentLevel, () => ShopFinish = true); GameEntry.UIRouter.BindUIUseCase(UIFormType.ShopForm, shopFormUseCase); } - public override void OnEnter(IFsm procedureOwner) + public override void OnEnter() { Log.Debug("GameStateShop::OnEnter"); @@ -35,8 +36,7 @@ namespace SepCore.Procedure GameEntry.UIRouter.OpenUIAsync(UIFormType.ShopForm).Forget(); } - public override void OnUpdate(IFsm procedureOwner, float elapseSeconds, - float realElapseSeconds) + public override void OnUpdate(float elapseSeconds, float realElapseSeconds) { if (ShopFinish) { @@ -44,14 +44,14 @@ namespace SepCore.Procedure } } - public override void OnLeave(IFsm procedureOwner) + public override void OnLeave() { Log.Debug("GameStateShop::OnLeave"); GameEntry.UIRouter.CloseUIAsync(UIFormType.ShopForm).Forget(); } - public override void OnDestroy(IFsm procedureOwner) + public override void OnDestroy() { _procedureGame = null; @@ -60,4 +60,4 @@ namespace SepCore.Procedure #endregion } -} \ No newline at end of file +} diff --git a/Assets/GameMain/Scripts/Procedure/Game/ProcedureGame.cs b/Assets/GameMain/Scripts/Procedure/Game/ProcedureGame.cs index c45ccad..3061767 100644 --- a/Assets/GameMain/Scripts/Procedure/Game/ProcedureGame.cs +++ b/Assets/GameMain/Scripts/Procedure/Game/ProcedureGame.cs @@ -1,13 +1,13 @@ +using System; using System.Collections.Generic; using Cysharp.Threading.Tasks; using SepCore.DataTable; using SepCore.Definition; using SepCore.Entity; -using GameFramework.Event; using GameFramework.Fsm; using GameFramework.Procedure; using UnityGameFramework.Runtime; -using SepCore.UI; +using SepCore.AsyncTask; namespace SepCore.Procedure { @@ -23,10 +23,6 @@ namespace SepCore.Procedure { public override bool UseNativeDialog => false; - private HudForm _hudForm; - private bool _hudInitialized; - - private IFsm _procedureOwner; private PlayerData _currentPlayerData; public int CurrentLevel = 1; @@ -50,7 +46,7 @@ namespace SepCore.Procedure _gameStates[GameStateType.Shop].OnInit(this); _currentGameState = GameStateType.Battle; - _gameStates[_currentGameState].OnEnter(_procedureOwner); + _gameStates[_currentGameState].OnEnter(); } public void BattleToShopOrLevelUp() @@ -58,27 +54,27 @@ namespace SepCore.Procedure CurrentLevel++; if (_currentGameState == GameStateType.Shop || _currentGameState == GameStateType.LevelUp) return; - _gameStates[_currentGameState].OnLeave(_procedureOwner); + _gameStates[_currentGameState].OnLeave(); _currentGameState = Player.PendingLevelPoints > 0 ? GameStateType.LevelUp : GameStateType.Shop; - _gameStates[_currentGameState].OnEnter(_procedureOwner); + _gameStates[_currentGameState].OnEnter(); } public void ShopToBattle() { if (_currentGameState == GameStateType.Battle) return; - _gameStates[_currentGameState].OnLeave(_procedureOwner); + _gameStates[_currentGameState].OnLeave(); _currentGameState = GameStateType.Battle; - _gameStates[_currentGameState].OnEnter(_procedureOwner); + _gameStates[_currentGameState].OnEnter(); } public void LevelUpToShop() { if (_currentGameState == GameStateType.Shop) return; - _gameStates[_currentGameState].OnLeave(_procedureOwner); + _gameStates[_currentGameState].OnLeave(); _currentGameState = GameStateType.Shop; - _gameStates[_currentGameState].OnEnter(_procedureOwner); + _gameStates[_currentGameState].OnEnter(); } #region FSM @@ -87,87 +83,65 @@ namespace SepCore.Procedure { base.OnEnter(procedureOwner); - _procedureOwner = procedureOwner; GameEntry.SimulationWorld?.ClearSimulationState(); - GameEntry.Event.Subscribe(OpenUIFormSuccessEventArgs.EventId, OpenUIFormSuccess); - GameEntry.Event.Subscribe(ShowEntitySuccessEventArgs.EventId, ShowEntitySuccess); - CurrentLevel = 1; _currentPlayerData = new PlayerData(-1, 1001); - GameEntry.Entity.ShowPlayer(_currentPlayerData); - GameEntry.UIRouter.OpenUIAsync(UIFormType.HudForm).Forget(); + EnterAsync(procedureOwner).Forget(); } protected override void OnUpdate(IFsm procedureOwner, float elapseSeconds, float realElapseSeconds) { base.OnUpdate(procedureOwner, elapseSeconds, realElapseSeconds); - - _procedureOwner = procedureOwner; - - if (!_hudInitialized && _hudForm != null && Player != null) - { - int selectedRoleId = _procedureOwner.GetData("SelectedRoleId").Value; - var role = GameEntry.DataTable.GetDataTableRow(selectedRoleId); - Player.InitRole(role); - _hudInitialized = true; - InitGameState(); - } - - if (_hudInitialized) - _gameStates[_currentGameState].OnUpdate(procedureOwner, elapseSeconds, realElapseSeconds); + _gameStates?[_currentGameState].OnUpdate(elapseSeconds, realElapseSeconds); } protected override void OnLeave(IFsm procedureOwner, bool isShutdown) { - foreach (var state in _gameStates.Values) + if (_gameStates != null) { - state.OnDestroy(procedureOwner); - } + foreach (var state in _gameStates.Values) + { + state.OnDestroy(); + } - _gameStates.Clear(); + _gameStates.Clear(); + } GameEntry.UIRouter.CloseUIAsync(UIFormType.HudForm).Forget(); - _hudForm = null; - Player = null; - - _procedureOwner = null; GameEntry.SimulationWorld?.ClearSimulationState(); - GameEntry.Event.Unsubscribe(OpenUIFormSuccessEventArgs.EventId, OpenUIFormSuccess); - GameEntry.Event.Unsubscribe(ShowEntitySuccessEventArgs.EventId, ShowEntitySuccess); - base.OnLeave(procedureOwner, isShutdown); } #endregion - #region Event Handler - - private void OpenUIFormSuccess(object sender, GameEventArgs e) + private async UniTaskVoid EnterAsync(IFsm procedureOwner) { - if (!(e is OpenUIFormSuccessEventArgs args)) return; - - if (args.UIForm.Logic is HudForm hudForm) + try { - _hudForm = hudForm; + Player = await GameEntry.Entity.ShowPlayerAsync(_currentPlayerData); + await GameEntry.UIRouter.OpenUIAsync(UIFormType.HudForm); + + int selectedRoleId = procedureOwner.GetData("SelectedRoleId").Value; + var role = GameEntry.DataTable.GetDataTableRow(selectedRoleId); + if (role == null) + { + Log.Error("ProcedureGame.EnterAsync() can not load role '{0}'.", selectedRoleId.ToString()); + return; + } + + Player.InitRole(role); + InitGameState(); + } + catch (Exception exception) + { + Log.Error("ProcedureGame.EnterAsync() failed with exception '{0}'.", exception); } } - - private void ShowEntitySuccess(object sender, GameEventArgs e) - { - if (!(e is ShowEntitySuccessEventArgs args)) return; - - if (args.Entity.Logic is Player player) - { - Player = player; - } - } - - #endregion } } diff --git a/Assets/GameMain/Scripts/Runtime/Entity/EntityLogic/Player.cs b/Assets/GameMain/Scripts/Runtime/Entity/EntityLogic/Player.cs index 853a793..aaabea4 100644 --- a/Assets/GameMain/Scripts/Runtime/Entity/EntityLogic/Player.cs +++ b/Assets/GameMain/Scripts/Runtime/Entity/EntityLogic/Player.cs @@ -1,13 +1,13 @@ using System.Collections.Generic; +using System.Threading; +using Cysharp.Threading.Tasks; using SepCore.Components; using SepCore.Event; using SepCore.DataTable; using SepCore.Definition; -using SepCore.Entity; -using GameFramework.Event; -using UnityGameFramework.Runtime; using SepCore.Entity.Weapon; using SepCore.CustomUtility; +using SepCore.AsyncTask; namespace SepCore.Entity { @@ -92,6 +92,28 @@ namespace SepCore.Entity return _backpackComponent.AttachProp(prop); } + public async UniTask AddWeaponAsync(WeaponData weaponData, float timeout = 30f) + { + if (weaponData == null || _backpackComponent == null) + { + return null; + } + + WeaponBase weapon = await GameEntry.Entity.ShowWeaponAsync(weaponData, timeout); + if (weapon == null) + { + return null; + } + + if (!_backpackComponent.AttachWeapon(weapon)) + { + GameEntry.Entity.HideEntity(weapon); + return null; + } + + return weapon; + } + public bool RemoveWeapon(WeaponBase weapon) { if (weapon == null || _backpackComponent == null) @@ -141,8 +163,8 @@ namespace SepCore.Entity // BackpackComponent Coin = role.Coin; _backpackComponent.OnInit(this, role.WeaponCapacity); - GameEntry.Entity.ShowWeapon(EntityDataFactory.Create(GameEntry.Entity.NextId(), - WeaponType.WeaponKnife, Id, _playerData.Camp)); + AddWeaponAsync(EntityDataFactory.Create(GameEntry.Entity.NextId(), + WeaponType.WeaponKnife, Id, _playerData.Camp)).Forget(); // StatComponent foreach (var modifier in role.InitialProperties) @@ -158,8 +180,7 @@ namespace SepCore.Entity GameEntry.Event.Fire(this, PlayerExpChangeEventArgs.Create(0, 100)); - GameEntry.Event.Fire(this, - PlayerCoinChangeEventArgs.Create(Coin, Coin)); + GameEntry.Event.Fire(this, PlayerCoinChangeEventArgs.Create(Coin, Coin)); } #region FSM @@ -190,8 +211,6 @@ namespace SepCore.Entity { base.OnShow(userData); - GameEntry.Event.Subscribe(ShowEntitySuccessEventArgs.EventId, ShowEntitySuccess); - _statComponent.OnInit(); if (userData is PlayerData playerData) @@ -230,18 +249,5 @@ namespace SepCore.Entity } #endregion - - #region Event Handlers - - private void ShowEntitySuccess(object sender, GameEventArgs e) - { - if (!(e is ShowEntitySuccessEventArgs args)) return; - if (args.Entity.Logic is WeaponBase weapon) - { - _backpackComponent.AttachWeapon(weapon); - } - } - - #endregion } } diff --git a/Assets/GameMain/Scripts/Runtime/UIBase/Main/Shop/ShopUseCase.cs b/Assets/GameMain/Scripts/Runtime/UIBase/Main/Shop/ShopUseCase.cs index b983d6e..2729339 100644 --- a/Assets/GameMain/Scripts/Runtime/UIBase/Main/Shop/ShopUseCase.cs +++ b/Assets/GameMain/Scripts/Runtime/UIBase/Main/Shop/ShopUseCase.cs @@ -1,10 +1,12 @@ using System; using System.Collections.Generic; using System.Linq; +using Cysharp.Threading.Tasks; using SepCore.DataTable; using SepCore.Definition; using SepCore.Entity; using SepCore.CustomUtility; +using SepCore.Entity.Weapon; using GameFramework.DataTable; using UnityEngine; using UnityGameFramework.Runtime; @@ -107,7 +109,7 @@ namespace SepCore.UI }; } - public ShopPurchaseResult TryPurchase(int goodsIndex) + public async UniTask TryPurchaseAsync(int goodsIndex) { if (goodsIndex < 0 || goodsIndex >= _selections.Count) { @@ -128,7 +130,7 @@ namespace SepCore.UI return null; } - DisplayItemRawData displayItem = ApplyGoodsPurchase(selection); + DisplayItemRawData displayItem = await ApplyGoodsPurchaseAsync(selection); if (displayItem == null) { return null; @@ -318,7 +320,7 @@ namespace SepCore.UI return Mathf.Max(1, finalPrice); } - private DisplayItemRawData ApplyGoodsPurchase(ShopGoodsSelection goods) + private async UniTask ApplyGoodsPurchaseAsync(ShopGoodsSelection goods) { if (goods == null || Player == null) { @@ -362,7 +364,13 @@ namespace SepCore.UI return null; } - GameEntry.Entity.ShowWeapon(weaponData); + WeaponBase weapon = await Player.AddWeaponAsync(weaponData); + if (weapon == null) + { + Log.Warning( + $"ShopFormUseCase::ApplyGoodsPurchase: Show weapon failed, weapon type id = {goods.GoodsTypeId}"); + return null; + } return new DisplayItemRawData { From 4790bd0091f88c710fb28b08dd36fa8cbe8dfd09 Mon Sep 17 00:00:00 2001 From: SepComet <202308010230@stu.csust.edu.cn> Date: Sat, 6 Jun 2026 14:57:08 +0800 Subject: [PATCH 06/22] =?UTF-8?q?=E8=B0=83=E6=95=B4=20EnemyManagerComponen?= =?UTF-8?q?t=20=E9=83=A8=E5=88=86=E9=80=BB=E8=BE=91=E5=88=B0=E5=BC=82?= =?UTF-8?q?=E6=AD=A5=E6=93=8D=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Scripts/Procedure/Game/GameStateBattle.cs | 2 +- .../EnemyManager/EnemyManagerComponent.cs | 68 +++++++++---------- 2 files changed, 34 insertions(+), 36 deletions(-) diff --git a/Assets/GameMain/Scripts/Procedure/Game/GameStateBattle.cs b/Assets/GameMain/Scripts/Procedure/Game/GameStateBattle.cs index fe674fe..89c77de 100644 --- a/Assets/GameMain/Scripts/Procedure/Game/GameStateBattle.cs +++ b/Assets/GameMain/Scripts/Procedure/Game/GameStateBattle.cs @@ -50,7 +50,7 @@ namespace SepCore.Procedure } _levelTimeLeft = drLevel.Duration; - _enemyManager.OnInit(drLevel); + _enemyManager.OnInit(drLevel, Player); if (Player != null) Player.Enable = true; } diff --git a/Assets/GameMain/Scripts/Runtime/CustomComponent/EnemyManager/EnemyManagerComponent.cs b/Assets/GameMain/Scripts/Runtime/CustomComponent/EnemyManager/EnemyManagerComponent.cs index 602951d..1ccb19e 100644 --- a/Assets/GameMain/Scripts/Runtime/CustomComponent/EnemyManager/EnemyManagerComponent.cs +++ b/Assets/GameMain/Scripts/Runtime/CustomComponent/EnemyManager/EnemyManagerComponent.cs @@ -1,8 +1,10 @@ using System.Collections.Generic; +using Cysharp.Threading.Tasks; using SepCore.DataTable; using SepCore.Definition; using SepCore.Entity; using GameFramework.Event; +using SepCore.AsyncTask; using SepCore.CustomUtility; using UnityEngine; using UnityGameFramework.Runtime; @@ -59,25 +61,22 @@ namespace SepCore.EnemyManager _enemies = new List(); _enemyById = new Dictionary(); - GameEntry.Event.Subscribe(ShowEntitySuccessEventArgs.EventId, OnShowEntitySuccess); GameEntry.Event.Subscribe(HideEntityCompleteEventArgs.EventId, OnHideEntityComplete); } private void OnDestroy() { - if (GameEntry.Event != null) - { - GameEntry.Event.Unsubscribe(ShowEntitySuccessEventArgs.EventId, OnShowEntitySuccess); - GameEntry.Event.Unsubscribe(HideEntityCompleteEventArgs.EventId, OnHideEntityComplete); - } + GameEntry.Event.Unsubscribe(HideEntityCompleteEventArgs.EventId, OnHideEntityComplete); _enemies = null; _enemyById = null; _entity = null; } - public void OnInit(DRLevel level) + public void OnInit(DRLevel level, Player player) { + _player = player != null ? player.CachedTransform : null; + _baseSpawnEnemyIntervals = (float[])level.Intervals.Clone(); _spawnEnemyIntervals = (float[])_baseSpawnEnemyIntervals.Clone(); _spawnEnemyTypes = level.EntityTypes; @@ -101,7 +100,7 @@ namespace SepCore.EnemyManager if (_spawnEnemyTimer < nextSpawnTime) continue; for (int j = 0; j < _spawnEnemyCounts[i]; j++) { - SpawnEnemy(_spawnEnemyTypes[i]); + SpawnEnemyAsync(_spawnEnemyTypes[i]).Forget(); } _nextSpawnTimes[i] += _spawnEnemyIntervals[i]; @@ -130,7 +129,7 @@ namespace SepCore.EnemyManager #endregion - private void SpawnEnemy(EnemyType enemyType) + private async UniTaskVoid SpawnEnemyAsync(EnemyType enemyType) { if (_player == null) return; @@ -138,8 +137,15 @@ namespace SepCore.EnemyManager int entityPoolId = _currentSpawnEnemyId % _spawnEnemyMaxCount; var enemyData = EntityDataFactory.Create(entityPoolId, enemyType, _currentLevel); enemyData.Position = GetRandomPosition(); - _entity.ShowEnemy(enemyData); _currentSpawnEnemyId++; + + EnemyBase enemy = await _entity.ShowEnemyAsync(enemyData); + if (enemy == null) + { + return; + } + + RegisterEnemy(enemy); } private Vector3 GetRandomPosition() @@ -227,41 +233,33 @@ namespace SepCore.EnemyManager #region Event Handler - private void OnShowEntitySuccess(object sender, GameEventArgs e) + private void RegisterEnemy(EnemyBase enemy) { - if (!(e is ShowEntitySuccessEventArgs ne)) return; - - string entityGroupName = ne.Entity?.EntityGroup?.Name; - - if (entityGroupName == EnemyGroupName && ne.Entity.Logic is EnemyBase enemy) + if (enemy == null) { - _currentEnemyCount++; - enemy.SetTarget(_player); - RemoveEnemyFromCache(enemy.Id); - _enemies.Add(enemy); - _enemyById[enemy.Id] = enemy; + return; } - if (ne.EntityLogicType == typeof(Player)) - { - _player = ne.Entity.transform; - } + _currentEnemyCount++; + enemy.SetTarget(_player); + RemoveEnemyFromCache(enemy.Id); + _enemies.Add(enemy); + _enemyById[enemy.Id] = enemy; } private void OnHideEntityComplete(object sender, GameEventArgs e) { - if (e is HideEntityCompleteEventArgs ne) - { - string entityGroupName = ne.EntityGroup.Name; - if (entityGroupName == EnemyGroupName) - { - if (_currentEnemyCount > 0) - { - _currentEnemyCount--; - } + if (e is not HideEntityCompleteEventArgs ne) return; - RemoveEnemyFromCache(ne.EntityId); + string entityGroupName = ne.EntityGroup.Name; + if (entityGroupName == EnemyGroupName) + { + if (_currentEnemyCount > 0) + { + _currentEnemyCount--; } + + RemoveEnemyFromCache(ne.EntityId); } } From c8c26b8b2e3c4f2432430565a38e3265910ef61e Mon Sep 17 00:00:00 2001 From: SepComet <202308010230@stu.csust.edu.cn> Date: Sat, 6 Jun 2026 15:13:52 +0800 Subject: [PATCH 07/22] =?UTF-8?q?=E8=BF=81=E7=A7=BB=20Coin/Exp/Projectile/?= =?UTF-8?q?Effect=20=E5=AE=9E=E4=BD=93=E7=9A=84=E7=94=9F=E6=88=90=E8=B7=AF?= =?UTF-8?q?=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SimulationWorld.HitPresentation.cs | 4 ++- .../Entity/EntityLogic/Enemy/EnemyBase.cs | 30 ++++++++++++++++- .../Entity/EntityLogic/Enemy/MeleeEnemy.cs | 32 ++++--------------- .../Entity/EntityLogic/Enemy/RemoteEnemy.cs | 22 +++++++------ .../Entity/EntityLogic/TargetableObject.cs | 10 +----- 5 files changed, 53 insertions(+), 45 deletions(-) diff --git a/Assets/GameMain/Scripts/Runtime/CustomComponent/Simulation/Presentation/SimulationWorld.HitPresentation.cs b/Assets/GameMain/Scripts/Runtime/CustomComponent/Simulation/Presentation/SimulationWorld.HitPresentation.cs index 90768a0..4845897 100644 --- a/Assets/GameMain/Scripts/Runtime/CustomComponent/Simulation/Presentation/SimulationWorld.HitPresentation.cs +++ b/Assets/GameMain/Scripts/Runtime/CustomComponent/Simulation/Presentation/SimulationWorld.HitPresentation.cs @@ -1,6 +1,8 @@ using SepCore.Event; using SepCore.Entity; +using SepCore.AsyncTask; using SepCore.Entity.Weapon; +using Cysharp.Threading.Tasks; using GameFramework.Event; using UnityEngine; @@ -127,7 +129,7 @@ namespace SepCore.Simulation { Position = args.HitPosition }; - entityComponent.ShowEffect(effectData); + entityComponent.ShowEffectAsync(effectData).Forget(); } } } diff --git a/Assets/GameMain/Scripts/Runtime/Entity/EntityLogic/Enemy/EnemyBase.cs b/Assets/GameMain/Scripts/Runtime/Entity/EntityLogic/Enemy/EnemyBase.cs index 2417f0a..437244e 100644 --- a/Assets/GameMain/Scripts/Runtime/Entity/EntityLogic/Enemy/EnemyBase.cs +++ b/Assets/GameMain/Scripts/Runtime/Entity/EntityLogic/Enemy/EnemyBase.cs @@ -1,4 +1,6 @@ -using SepCore.Definition; +using Cysharp.Threading.Tasks; +using SepCore.AsyncTask; +using SepCore.Definition; using SepCore.Entity; using UnityEngine; @@ -10,5 +12,31 @@ public abstract class EnemyBase : TargetableObject public virtual float AttackRange => 1f; public virtual void SetTarget(Transform target) => _target = target; + + protected EnemyData _enemyData; + + + protected override void OnDead(EntityBase attacker) + { + if (Random.value < _enemyData.DropPercent) + { + var data = new CoinData(_enemyData.DropCoin, GameEntry.Entity.NextId(), 10001) + { + Position = this.CachedTransform.position + }; + GameEntry.Entity.ShowCoinAsync(data).Forget(); + } + + if (Random.value < _enemyData.DropPercent) + { + var data = new ExpData(_enemyData.DropExp, GameEntry.Entity.NextId(), 10002) + { + Position = this.CachedTransform.position + }; + GameEntry.Entity.ShowExpAsync(data).Forget(); + } + + base.OnDead(attacker); + } } diff --git a/Assets/GameMain/Scripts/Runtime/Entity/EntityLogic/Enemy/MeleeEnemy.cs b/Assets/GameMain/Scripts/Runtime/Entity/EntityLogic/Enemy/MeleeEnemy.cs index 2d44bf3..cf20541 100644 --- a/Assets/GameMain/Scripts/Runtime/Entity/EntityLogic/Enemy/MeleeEnemy.cs +++ b/Assets/GameMain/Scripts/Runtime/Entity/EntityLogic/Enemy/MeleeEnemy.cs @@ -1,7 +1,6 @@ using SepCore.Components; using SepCore.CustomUtility; using SepCore.Definition; -using SepCore.Entity; using UnityEngine; using UnityGameFramework.Runtime; @@ -27,7 +26,13 @@ namespace SepCore.Entity private float _currAttackTimer; private AttackStateType _attackState = AttackStateType.Idle; - private EnemyData _meleeEnemyData; + + private EnemyData _meleeEnemyData + { + get => _enemyData; + set => _enemyData = value; + } + private TargetableObject _targetableTarget; protected override TargetableObjectData _targetableObjectData => _meleeEnemyData; @@ -83,29 +88,6 @@ namespace SepCore.Entity UpdateAttackState(elapseSeconds); } - protected override void OnDead(EntityBase attacker) - { - if (Random.value < _meleeEnemyData.DropPercent) - { - var data = new CoinData(_meleeEnemyData.DropCoin, GameEntry.Entity.NextId(), 10001) - { - Position = this.CachedTransform.position - }; - GameEntry.Entity.ShowCoin(data); - } - - if (Random.value < _meleeEnemyData.DropPercent) - { - var data = new ExpData(_meleeEnemyData.DropExp, GameEntry.Entity.NextId(), 10002) - { - Position = this.CachedTransform.position - }; - GameEntry.Entity.ShowExp(data); - } - - base.OnDead(attacker); - } - protected override void OnHide(bool isShutdown, object userData) { _movementComponent.OnReset(); diff --git a/Assets/GameMain/Scripts/Runtime/Entity/EntityLogic/Enemy/RemoteEnemy.cs b/Assets/GameMain/Scripts/Runtime/Entity/EntityLogic/Enemy/RemoteEnemy.cs index 8104de7..25c3af0 100644 --- a/Assets/GameMain/Scripts/Runtime/Entity/EntityLogic/Enemy/RemoteEnemy.cs +++ b/Assets/GameMain/Scripts/Runtime/Entity/EntityLogic/Enemy/RemoteEnemy.cs @@ -1,7 +1,8 @@ using SepCore.Components; +using SepCore.AsyncTask; using SepCore.CustomUtility; using SepCore.Definition; -using SepCore.Entity; +using Cysharp.Threading.Tasks; using UnityEngine; using UnityGameFramework.Runtime; @@ -34,7 +35,11 @@ namespace SepCore.Entity [SerializeField] private float _projectileSpawnHeightOffset = 0.6f; [SerializeField] private string _projectileAssetName = "BulletHandgun"; - private EnemyData _remoteEnemyData; + private EnemyData _remoteEnemyData + { + get => _enemyData; + set => _enemyData = value; + } protected override TargetableObjectData _targetableObjectData => _remoteEnemyData; public override float AttackRange => _attackRange; @@ -154,13 +159,12 @@ namespace SepCore.Entity Rotation = Quaternion.LookRotation(direction, Vector3.up) }; - GameEntry.Entity.ShowEntity( - entityId: projectileEntityId, - entityLogicType: typeof(EnemyProjectile), - entityAssetName: AssetUtility.GetEntityAsset(_projectileAssetName), - entityGroupName: EnemyProjectileGroupName, - priority: Constant.AssetPriority.BulletAsset, - userData: projectileData); + GameEntry.Entity.ShowEntityAsync( + projectileEntityId, + AssetUtility.GetEntityAsset(_projectileAssetName), + EnemyProjectileGroupName, + Constant.AssetPriority.BulletAsset, + projectileData).Forget(); _currAttackTimer = 0f; } diff --git a/Assets/GameMain/Scripts/Runtime/Entity/EntityLogic/TargetableObject.cs b/Assets/GameMain/Scripts/Runtime/Entity/EntityLogic/TargetableObject.cs index 847e14a..0e94ab5 100644 --- a/Assets/GameMain/Scripts/Runtime/Entity/EntityLogic/TargetableObject.cs +++ b/Assets/GameMain/Scripts/Runtime/Entity/EntityLogic/TargetableObject.cs @@ -1,13 +1,5 @@ -//------------------------------------------------------------ -// Game Framework -// Copyright © 2013-2021 Jiang Yin. All rights reserved. -// Homepage: https://gameframework.cn/ -// Feedback: mailto:ellan@gameframework.cn -//------------------------------------------------------------ - -using SepCore.Components; +using SepCore.Components; using SepCore.Definition; -using SepCore.Entity; using SepCore.CustomUtility; using UnityEngine; From 5ede6846dbe50f3ecbc045b988ad139a4079956c Mon Sep 17 00:00:00 2001 From: SepComet <202308010230@stu.csust.edu.cn> Date: Sat, 6 Jun 2026 16:35:39 +0800 Subject: [PATCH 08/22] =?UTF-8?q?=E6=B8=85=E7=90=86=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E7=9B=AE=E5=BD=95=EF=BC=8C=E5=BC=95=E5=85=A5=20InputModule=20?= =?UTF-8?q?=E5=81=9A=E5=A4=9A=E5=B9=B3=E5=8F=B0=E8=BE=93=E5=85=A5=E9=80=82?= =?UTF-8?q?=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .claude/commands/opsx/apply.md | 152 - .claude/commands/opsx/archive.md | 157 - .claude/commands/opsx/explore.md | 173 - .claude/commands/opsx/propose.md | 106 - .claude/skills/openspec-apply-change/SKILL.md | 156 - .../skills/openspec-archive-change/SKILL.md | 114 - .claude/skills/openspec-explore/SKILL.md | 288 -- .claude/skills/openspec-propose/SKILL.md | 110 - Assets/GameMain/DataTables/Enemy.txt | 2 +- Assets/GameMain/DataTables/UIForm.txt | 1 + Assets/GameMain/DataTables/Weapon.txt | 2 +- .../Base/Definition/Enum/UIFormType.cs | 5 + .../Common/Dialog/DialogContext.cs | 4 +- .../DisplayItemInfoController.cs | 2 +- .../DisplayItemInfo/DisplayItemInfoForm.cs | 2 +- .../Scripts/Presentation/Main/Joystick.meta | 8 + .../Main/Joystick/JoystickContext.cs | 10 + .../Main/Joystick/JoystickContext.cs.meta} | 2 +- .../Main/Joystick/JoystickController.cs | 48 + .../Main/Joystick/JoystickController.cs.meta | 11 + .../Main/Joystick/JoystickForm.cs | 25 + .../Main/Joystick/JoystickForm.cs.meta | 11 + .../Presentation/Main/View/DisplayListArea.cs | 4 +- .../Menu/SelectRole/SelectRoleForm.cs | 4 +- .../Presentation/SepCore.Presentation.asmdef | 3 +- .../Procedure/Base/ProcedurePreload.cs | 2 + .../Scripts/Procedure/Game/GameStateBattle.cs | 9 + .../Scripts/Procedure/Game/ProcedureGame.cs | 1 + .../Scripts/Runtime/Base/GameEntry.Custom.cs | 4 + .../Runtime/Components/InputComponent.cs | 28 +- .../Scripts/Runtime/PlayerInputActions.cs | 411 -- .../Scripts/Runtime/SepCore.Runtime.asmdef | 3 +- .../Scripts/Runtime/UIBase/Main/Joystick.meta | 8 + .../UIBase/Main/LevelUp/LevelUpUseCase.cs | 3 +- .../GameMain/UI/UIForms/JoystickForm.prefab | 324 ++ .../UI/UIForms/JoystickForm.prefab.meta | 7 + Assets/Launcher.unity | 54 + Assets/PlayerInputActions.inputactions | 2 +- Assets/PlayerInputActions.inputactions.meta | 2 +- Assets/Plugins/InputModule.meta | 8 + Assets/Plugins/InputModule/Assets.meta | 8 + .../InputModule/Assets/InputPrompt.meta | 8 + .../Assets/InputPrompt/InputPrompt.asset | 2638 +++++++++++++ .../Assets/InputPrompt/InputPrompt.asset.meta | 8 + .../Assets/InputPrompt/Input_Prompt.png | Bin 0 -> 15664 bytes .../Assets/InputPrompt/Input_Prompt.png.meta | 3392 +++++++++++++++++ .../Plugins/InputModule/Assets/Joystick.meta | 8 + .../InputModule/Assets/Joystick/Prefabs.meta | 8 + .../Joystick/Prefabs/Dynamic Joystick.prefab | 250 ++ .../Prefabs/Dynamic Joystick.prefab.meta | 7 + .../Joystick/Prefabs/Fixed Joystick.prefab | 173 + .../Prefabs/Fixed Joystick.prefab.meta | 7 + .../Joystick/Prefabs/Floating Joystick.prefab | 249 ++ .../Prefabs/Floating Joystick.prefab.meta | 7 + .../Joystick/Prefabs/Variable Joystick.prefab | 251 ++ .../Prefabs/Variable Joystick.prefab.meta | 7 + .../InputModule/Assets/Joystick/Sprites.meta | 8 + .../Sprites/All Axis Backgrounds.meta | 8 + .../All Axis Backgrounds/AllAxis_Outline.png | Bin 0 -> 10076 bytes .../AllAxis_Outline.png.meta | 153 + .../AllAxis_Outline_Arrows.png | Bin 0 -> 11326 bytes .../AllAxis_Outline_Arrows.png.meta | 153 + .../All Axis Backgrounds/AllAxis_Plain.png | Bin 0 -> 6774 bytes .../AllAxis_Plain.png.meta | 153 + .../AllAxis_Plain_Arrows.png | Bin 0 -> 8488 bytes .../AllAxis_Plain_Arrows.png.meta | 153 + .../All Axis Backgrounds/AllAxis_Ridged.png | Bin 0 -> 23051 bytes .../AllAxis_Ridged.png.meta | 153 + .../AllAxis_Ridged_Arrows.png | Bin 0 -> 24356 bytes .../AllAxis_Ridged_Arrows.png.meta | 153 + .../Assets/Joystick/Sprites/Handles.meta | 8 + .../Sprites/Handles/Handle_Outline.png | Bin 0 -> 4763 bytes .../Sprites/Handles/Handle_Outline.png.meta | 153 + .../Sprites/Handles/Handle_Outline_Arrows.png | Bin 0 -> 5447 bytes .../Handles/Handle_Outline_Arrows.png.meta | 153 + .../Joystick/Sprites/Handles/Handle_Plain.png | Bin 0 -> 3178 bytes .../Sprites/Handles/Handle_Plain.png.meta | 153 + .../Sprites/Handles/Handle_Plain_Arrows.png | Bin 0 -> 4187 bytes .../Handles/Handle_Plain_Arrows.png.meta | 153 + .../Sprites/Handles/Handle_Ridged.png | Bin 0 -> 8878 bytes .../Sprites/Handles/Handle_Ridged.png.meta | 153 + .../Sprites/Handles/Handle_Ridged_Arrows.png | Bin 0 -> 10048 bytes .../Handles/Handle_Ridged_Arrows.png.meta | 153 + .../Sprites/Horizontal Backgrounds.meta | 8 + .../Horizontal_Outline.png | Bin 0 -> 5496 bytes .../Horizontal_Outline.png.meta | 153 + .../Horizontal_Outline_Arrows.png | Bin 0 -> 6131 bytes .../Horizontal_Outline_Arrows.png.meta | 153 + .../Horizontal_Plain.png | Bin 0 -> 3939 bytes .../Horizontal_Plain.png.meta | 153 + .../Horizontal_Plain_Arrows.png | Bin 0 -> 4801 bytes .../Horizontal_Plain_Arrows.png.meta | 153 + .../Horizontal_Ridged.png | Bin 0 -> 10634 bytes .../Horizontal_Ridged.png.meta | 153 + .../Horizontal_Ridged_Arrows.png | Bin 0 -> 11243 bytes .../Horizontal_Ridged_Arrows.png.meta | 153 + .../Sprites/Vertical Backgrounds.meta | 8 + .../Vertical Backgrounds/Vertical_Outline.png | Bin 0 -> 5367 bytes .../Vertical_Outline.png.meta | 153 + .../Vertical_Outline_Arrows.png | Bin 0 -> 5851 bytes .../Vertical_Outline_Arrows.png.meta | 153 + .../Vertical Backgrounds/Vertical_Plain.png | Bin 0 -> 3792 bytes .../Vertical_Plain.png.meta | 153 + .../Vertical_Plain_Arrows.png | Bin 0 -> 4697 bytes .../Vertical_Plain_Arrows.png.meta | 153 + .../Vertical Backgrounds/Vertical_Ridged.png | Bin 0 -> 12445 bytes .../Vertical_Ridged.png.meta | 153 + .../Vertical_Ridged_Arrows.png | Bin 0 -> 13111 bytes .../Vertical_Ridged_Arrows.png.meta | 153 + Assets/Plugins/InputModule/Base.meta | 8 + .../Plugins/InputModule/Base/Definition.meta | 8 + .../Base/Definition/DataStruct.meta | 8 + .../DataStruct/InputBindingSnapshot.cs | 15 + .../DataStruct/InputBindingSnapshot.cs.meta | 11 + .../Definition/DataStruct/InputCommand.cs | 42 + .../DataStruct/InputCommand.cs.meta | 11 + .../Base/Definition/DataStruct/InputPrompt.cs | 21 + .../Definition/DataStruct/InputPrompt.cs.meta | 11 + .../Definition/DataStruct/RebindResult.cs | 33 + .../DataStruct/RebindResult.cs.meta | 11 + .../InputModule/Base/Definition/Enum.meta | 8 + .../Base/Definition/Enum/InputActionId.cs | 15 + .../Definition/Enum/InputActionId.cs.meta | 11 + .../Base/Definition/Enum/InputCommandPhase.cs | 10 + .../Definition/Enum/InputCommandPhase.cs.meta | 11 + .../Base/Definition/Enum/InputContextId.cs | 13 + .../Definition/Enum/InputContextId.cs.meta | 11 + .../Base/Definition/Enum/InputDeviceKind.cs | 11 + .../Definition/Enum/InputDeviceKind.cs.meta | 11 + .../Base/Definition/Enum/RebindStatus.cs | 12 + .../Base/Definition/Enum/RebindStatus.cs.meta | 11 + .../Base/Definition/IInputPromptMap.cs | 7 + .../Base/Definition/IInputPromptMap.cs.meta | 11 + .../Base/SepCore.InputModule.Base.asmdef | 14 + .../Base/SepCore.InputModule.Base.asmdef.meta | 7 + Assets/Plugins/InputModule/Base/Utility.meta | 8 + .../Base/Utility/InputContextStack.cs | 79 + .../Base/Utility/InputContextStack.cs.meta | 11 + Assets/Plugins/InputModule/Editor.meta | 8 + .../Editor/InputModuleComponentEditor.cs | 34 + .../Editor/InputModuleComponentEditor.cs.meta | 11 + .../InputModule/Editor/InputModuleDebugger.cs | 237 ++ .../Editor/InputModuleDebugger.cs.meta | 11 + .../Editor/SepCore.InputModule.Editor.asmdef | 21 + .../SepCore.InputModule.Editor.asmdef.meta | 7 + Assets/Plugins/InputModule/Presentation.meta | 8 + .../Presentation/JoystickPack.meta | 8 + .../Presentation/JoystickPack/Base.meta | 8 + .../JoystickPack/Base/Joystick.cs | 150 + .../JoystickPack/Base/Joystick.cs.meta | 11 + .../Presentation/JoystickPack/Editor.meta | 8 + .../Editor/DynamicJoystickEditor.cs | 35 + .../Editor/DynamicJoystickEditor.cs.meta | 11 + .../Editor/FloatingJoystickEditor.cs | 21 + .../Editor/FloatingJoystickEditor.cs.meta | 11 + .../JoystickPack/Editor/JoystickEditor.cs | 64 + .../Editor/JoystickEditor.cs.meta | 11 + .../Editor/VariableJoystickEditor.cs | 37 + .../Editor/VariableJoystickEditor.cs.meta | 11 + .../Presentation/JoystickPack/Joysticks.meta | 8 + .../JoystickPack/Joysticks/DynamicJoystick.cs | 41 + .../Joysticks/DynamicJoystick.cs.meta | 11 + .../JoystickPack/Joysticks/FixedJoystick.cs | 8 + .../Joysticks/FixedJoystick.cs.meta | 11 + .../Joysticks/FloatingJoystick.cs | 26 + .../Joysticks/FloatingJoystick.cs.meta | 11 + .../Joysticks/VariableJoystick.cs | 63 + .../Joysticks/VariableJoystick.cs.meta | 11 + .../SepCore.Presentation.InputModule.asmref | 3 + ...pCore.Presentation.InputModule.asmref.meta | 7 + .../Presentation/VirtualButtonBridge.cs | 73 + .../Presentation/VirtualButtonBridge.cs.meta | 11 + .../Presentation/VirtualJoystickBridge.cs | 96 + .../VirtualJoystickBridge.cs.meta | 11 + Assets/Plugins/InputModule/Runtime.meta | 8 + .../Runtime/BindingOverridePersistence.cs | 74 + .../BindingOverridePersistence.cs.meta | 11 + .../Runtime/InputDeviceKindUtility.cs | 33 + .../Runtime/InputDeviceKindUtility.cs.meta | 11 + .../Runtime/InputModuleComponent.cs | 839 ++++ .../Runtime/InputModuleComponent.cs.meta | 11 + .../Runtime/InputModuleDefaultActions.cs | 72 + .../Runtime/InputModuleDefaultActions.cs.meta | 11 + .../Runtime/InputModuleDefaultPromptMap.cs | 121 + .../InputModuleDefaultPromptMap.cs.meta | 11 + .../SepCore.Runtime.InputModule.asmref | 3 + .../SepCore.Runtime.InputModule.asmref.meta | 7 + Assets/Plugins/InputModule/Tests.meta | 8 + .../Plugins/InputModule/Tests/PlayMode.meta | 8 + .../Tests/PlayMode/InputModule.meta | 8 + .../BindingOverridePersistenceTests.cs | 78 + .../BindingOverridePersistenceTests.cs.meta | 11 + .../InputModule/InputContextStackTests.cs | 130 + .../InputContextStackTests.cs.meta | 11 + .../InputModuleComponentContextTests.cs | 265 ++ .../InputModuleComponentContextTests.cs.meta | 11 + .../InputModuleComponentEventTests.cs | 259 ++ .../InputModuleComponentEventTests.cs.meta | 11 + .../InputModuleComponentRebindTests.cs | 214 ++ .../InputModuleComponentRebindTests.cs.meta | 11 + .../InputModuleDebuggerReflectionTests.cs | 122 + ...InputModuleDebuggerReflectionTests.cs.meta | 11 + .../InputModuleDefaultActionsTests.cs | 141 + .../InputModuleDefaultActionsTests.cs.meta | 11 + .../InputModuleDefaultPromptMapTests.cs | 129 + .../InputModuleDefaultPromptMapTests.cs.meta | 11 + .../PlayMode/InputModule/InputPromptTests.cs | 89 + .../InputModule/InputPromptTests.cs.meta | 11 + .../SepCore.InputModule.Tests.asmdef | 27 + .../SepCore.InputModule.Tests.asmdef.meta | 7 + Assets/Plugins/InputModule/常见问题.md | 115 + Assets/Plugins/InputModule/常见问题.md.meta | 7 + Assets/Plugins/InputModule/接入说明.md | 925 +++++ Assets/Plugins/InputModule/接入说明.md.meta | 7 + .../.openspec.yaml | 2 - .../design.md | 61 - .../proposal.md | 24 - .../spec.md | 45 - .../tasks.md | 23 - .../.openspec.yaml | 2 - .../design.md | 48 - .../proposal.md | 26 - .../spec.md | 31 - .../tasks.md | 17 - openspec/config.yaml | 20 - .../spec.md | 59 - 数据表/UIForm.xlsx | Bin 10474 -> 10521 bytes 227 files changed, 16702 insertions(+), 2059 deletions(-) delete mode 100644 .claude/commands/opsx/apply.md delete mode 100644 .claude/commands/opsx/archive.md delete mode 100644 .claude/commands/opsx/explore.md delete mode 100644 .claude/commands/opsx/propose.md delete mode 100644 .claude/skills/openspec-apply-change/SKILL.md delete mode 100644 .claude/skills/openspec-archive-change/SKILL.md delete mode 100644 .claude/skills/openspec-explore/SKILL.md delete mode 100644 .claude/skills/openspec-propose/SKILL.md create mode 100644 Assets/GameMain/Scripts/Presentation/Main/Joystick.meta create mode 100644 Assets/GameMain/Scripts/Presentation/Main/Joystick/JoystickContext.cs rename Assets/GameMain/Scripts/{Runtime/PlayerInputActions.cs.meta => Presentation/Main/Joystick/JoystickContext.cs.meta} (83%) create mode 100644 Assets/GameMain/Scripts/Presentation/Main/Joystick/JoystickController.cs create mode 100644 Assets/GameMain/Scripts/Presentation/Main/Joystick/JoystickController.cs.meta create mode 100644 Assets/GameMain/Scripts/Presentation/Main/Joystick/JoystickForm.cs create mode 100644 Assets/GameMain/Scripts/Presentation/Main/Joystick/JoystickForm.cs.meta delete mode 100644 Assets/GameMain/Scripts/Runtime/PlayerInputActions.cs create mode 100644 Assets/GameMain/Scripts/Runtime/UIBase/Main/Joystick.meta create mode 100644 Assets/GameMain/UI/UIForms/JoystickForm.prefab create mode 100644 Assets/GameMain/UI/UIForms/JoystickForm.prefab.meta create mode 100644 Assets/Plugins/InputModule.meta create mode 100644 Assets/Plugins/InputModule/Assets.meta create mode 100644 Assets/Plugins/InputModule/Assets/InputPrompt.meta create mode 100644 Assets/Plugins/InputModule/Assets/InputPrompt/InputPrompt.asset create mode 100644 Assets/Plugins/InputModule/Assets/InputPrompt/InputPrompt.asset.meta create mode 100644 Assets/Plugins/InputModule/Assets/InputPrompt/Input_Prompt.png create mode 100644 Assets/Plugins/InputModule/Assets/InputPrompt/Input_Prompt.png.meta create mode 100644 Assets/Plugins/InputModule/Assets/Joystick.meta create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Prefabs.meta create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Prefabs/Dynamic Joystick.prefab create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Prefabs/Dynamic Joystick.prefab.meta create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Prefabs/Fixed Joystick.prefab create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Prefabs/Fixed Joystick.prefab.meta create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Prefabs/Floating Joystick.prefab create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Prefabs/Floating Joystick.prefab.meta create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Prefabs/Variable Joystick.prefab create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Prefabs/Variable Joystick.prefab.meta create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites.meta create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites/All Axis Backgrounds.meta create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites/All Axis Backgrounds/AllAxis_Outline.png create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites/All Axis Backgrounds/AllAxis_Outline.png.meta create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites/All Axis Backgrounds/AllAxis_Outline_Arrows.png create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites/All Axis Backgrounds/AllAxis_Outline_Arrows.png.meta create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites/All Axis Backgrounds/AllAxis_Plain.png create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites/All Axis Backgrounds/AllAxis_Plain.png.meta create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites/All Axis Backgrounds/AllAxis_Plain_Arrows.png create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites/All Axis Backgrounds/AllAxis_Plain_Arrows.png.meta create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites/All Axis Backgrounds/AllAxis_Ridged.png create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites/All Axis Backgrounds/AllAxis_Ridged.png.meta create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites/All Axis Backgrounds/AllAxis_Ridged_Arrows.png create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites/All Axis Backgrounds/AllAxis_Ridged_Arrows.png.meta create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites/Handles.meta create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites/Handles/Handle_Outline.png create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites/Handles/Handle_Outline.png.meta create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites/Handles/Handle_Outline_Arrows.png create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites/Handles/Handle_Outline_Arrows.png.meta create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites/Handles/Handle_Plain.png create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites/Handles/Handle_Plain.png.meta create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites/Handles/Handle_Plain_Arrows.png create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites/Handles/Handle_Plain_Arrows.png.meta create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites/Handles/Handle_Ridged.png create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites/Handles/Handle_Ridged.png.meta create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites/Handles/Handle_Ridged_Arrows.png create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites/Handles/Handle_Ridged_Arrows.png.meta create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites/Horizontal Backgrounds.meta create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites/Horizontal Backgrounds/Horizontal_Outline.png create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites/Horizontal Backgrounds/Horizontal_Outline.png.meta create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites/Horizontal Backgrounds/Horizontal_Outline_Arrows.png create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites/Horizontal Backgrounds/Horizontal_Outline_Arrows.png.meta create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites/Horizontal Backgrounds/Horizontal_Plain.png create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites/Horizontal Backgrounds/Horizontal_Plain.png.meta create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites/Horizontal Backgrounds/Horizontal_Plain_Arrows.png create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites/Horizontal Backgrounds/Horizontal_Plain_Arrows.png.meta create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites/Horizontal Backgrounds/Horizontal_Ridged.png create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites/Horizontal Backgrounds/Horizontal_Ridged.png.meta create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites/Horizontal Backgrounds/Horizontal_Ridged_Arrows.png create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites/Horizontal Backgrounds/Horizontal_Ridged_Arrows.png.meta create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites/Vertical Backgrounds.meta create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites/Vertical Backgrounds/Vertical_Outline.png create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites/Vertical Backgrounds/Vertical_Outline.png.meta create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites/Vertical Backgrounds/Vertical_Outline_Arrows.png create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites/Vertical Backgrounds/Vertical_Outline_Arrows.png.meta create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites/Vertical Backgrounds/Vertical_Plain.png create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites/Vertical Backgrounds/Vertical_Plain.png.meta create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites/Vertical Backgrounds/Vertical_Plain_Arrows.png create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites/Vertical Backgrounds/Vertical_Plain_Arrows.png.meta create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites/Vertical Backgrounds/Vertical_Ridged.png create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites/Vertical Backgrounds/Vertical_Ridged.png.meta create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites/Vertical Backgrounds/Vertical_Ridged_Arrows.png create mode 100644 Assets/Plugins/InputModule/Assets/Joystick/Sprites/Vertical Backgrounds/Vertical_Ridged_Arrows.png.meta create mode 100644 Assets/Plugins/InputModule/Base.meta create mode 100644 Assets/Plugins/InputModule/Base/Definition.meta create mode 100644 Assets/Plugins/InputModule/Base/Definition/DataStruct.meta create mode 100644 Assets/Plugins/InputModule/Base/Definition/DataStruct/InputBindingSnapshot.cs create mode 100644 Assets/Plugins/InputModule/Base/Definition/DataStruct/InputBindingSnapshot.cs.meta create mode 100644 Assets/Plugins/InputModule/Base/Definition/DataStruct/InputCommand.cs create mode 100644 Assets/Plugins/InputModule/Base/Definition/DataStruct/InputCommand.cs.meta create mode 100644 Assets/Plugins/InputModule/Base/Definition/DataStruct/InputPrompt.cs create mode 100644 Assets/Plugins/InputModule/Base/Definition/DataStruct/InputPrompt.cs.meta create mode 100644 Assets/Plugins/InputModule/Base/Definition/DataStruct/RebindResult.cs create mode 100644 Assets/Plugins/InputModule/Base/Definition/DataStruct/RebindResult.cs.meta create mode 100644 Assets/Plugins/InputModule/Base/Definition/Enum.meta create mode 100644 Assets/Plugins/InputModule/Base/Definition/Enum/InputActionId.cs create mode 100644 Assets/Plugins/InputModule/Base/Definition/Enum/InputActionId.cs.meta create mode 100644 Assets/Plugins/InputModule/Base/Definition/Enum/InputCommandPhase.cs create mode 100644 Assets/Plugins/InputModule/Base/Definition/Enum/InputCommandPhase.cs.meta create mode 100644 Assets/Plugins/InputModule/Base/Definition/Enum/InputContextId.cs create mode 100644 Assets/Plugins/InputModule/Base/Definition/Enum/InputContextId.cs.meta create mode 100644 Assets/Plugins/InputModule/Base/Definition/Enum/InputDeviceKind.cs create mode 100644 Assets/Plugins/InputModule/Base/Definition/Enum/InputDeviceKind.cs.meta create mode 100644 Assets/Plugins/InputModule/Base/Definition/Enum/RebindStatus.cs create mode 100644 Assets/Plugins/InputModule/Base/Definition/Enum/RebindStatus.cs.meta create mode 100644 Assets/Plugins/InputModule/Base/Definition/IInputPromptMap.cs create mode 100644 Assets/Plugins/InputModule/Base/Definition/IInputPromptMap.cs.meta create mode 100644 Assets/Plugins/InputModule/Base/SepCore.InputModule.Base.asmdef create mode 100644 Assets/Plugins/InputModule/Base/SepCore.InputModule.Base.asmdef.meta create mode 100644 Assets/Plugins/InputModule/Base/Utility.meta create mode 100644 Assets/Plugins/InputModule/Base/Utility/InputContextStack.cs create mode 100644 Assets/Plugins/InputModule/Base/Utility/InputContextStack.cs.meta create mode 100644 Assets/Plugins/InputModule/Editor.meta create mode 100644 Assets/Plugins/InputModule/Editor/InputModuleComponentEditor.cs create mode 100644 Assets/Plugins/InputModule/Editor/InputModuleComponentEditor.cs.meta create mode 100644 Assets/Plugins/InputModule/Editor/InputModuleDebugger.cs create mode 100644 Assets/Plugins/InputModule/Editor/InputModuleDebugger.cs.meta create mode 100644 Assets/Plugins/InputModule/Editor/SepCore.InputModule.Editor.asmdef create mode 100644 Assets/Plugins/InputModule/Editor/SepCore.InputModule.Editor.asmdef.meta create mode 100644 Assets/Plugins/InputModule/Presentation.meta create mode 100644 Assets/Plugins/InputModule/Presentation/JoystickPack.meta create mode 100644 Assets/Plugins/InputModule/Presentation/JoystickPack/Base.meta create mode 100644 Assets/Plugins/InputModule/Presentation/JoystickPack/Base/Joystick.cs create mode 100644 Assets/Plugins/InputModule/Presentation/JoystickPack/Base/Joystick.cs.meta create mode 100644 Assets/Plugins/InputModule/Presentation/JoystickPack/Editor.meta create mode 100644 Assets/Plugins/InputModule/Presentation/JoystickPack/Editor/DynamicJoystickEditor.cs create mode 100644 Assets/Plugins/InputModule/Presentation/JoystickPack/Editor/DynamicJoystickEditor.cs.meta create mode 100644 Assets/Plugins/InputModule/Presentation/JoystickPack/Editor/FloatingJoystickEditor.cs create mode 100644 Assets/Plugins/InputModule/Presentation/JoystickPack/Editor/FloatingJoystickEditor.cs.meta create mode 100644 Assets/Plugins/InputModule/Presentation/JoystickPack/Editor/JoystickEditor.cs create mode 100644 Assets/Plugins/InputModule/Presentation/JoystickPack/Editor/JoystickEditor.cs.meta create mode 100644 Assets/Plugins/InputModule/Presentation/JoystickPack/Editor/VariableJoystickEditor.cs create mode 100644 Assets/Plugins/InputModule/Presentation/JoystickPack/Editor/VariableJoystickEditor.cs.meta create mode 100644 Assets/Plugins/InputModule/Presentation/JoystickPack/Joysticks.meta create mode 100644 Assets/Plugins/InputModule/Presentation/JoystickPack/Joysticks/DynamicJoystick.cs create mode 100644 Assets/Plugins/InputModule/Presentation/JoystickPack/Joysticks/DynamicJoystick.cs.meta create mode 100644 Assets/Plugins/InputModule/Presentation/JoystickPack/Joysticks/FixedJoystick.cs create mode 100644 Assets/Plugins/InputModule/Presentation/JoystickPack/Joysticks/FixedJoystick.cs.meta create mode 100644 Assets/Plugins/InputModule/Presentation/JoystickPack/Joysticks/FloatingJoystick.cs create mode 100644 Assets/Plugins/InputModule/Presentation/JoystickPack/Joysticks/FloatingJoystick.cs.meta create mode 100644 Assets/Plugins/InputModule/Presentation/JoystickPack/Joysticks/VariableJoystick.cs create mode 100644 Assets/Plugins/InputModule/Presentation/JoystickPack/Joysticks/VariableJoystick.cs.meta create mode 100644 Assets/Plugins/InputModule/Presentation/SepCore.Presentation.InputModule.asmref create mode 100644 Assets/Plugins/InputModule/Presentation/SepCore.Presentation.InputModule.asmref.meta create mode 100644 Assets/Plugins/InputModule/Presentation/VirtualButtonBridge.cs create mode 100644 Assets/Plugins/InputModule/Presentation/VirtualButtonBridge.cs.meta create mode 100644 Assets/Plugins/InputModule/Presentation/VirtualJoystickBridge.cs create mode 100644 Assets/Plugins/InputModule/Presentation/VirtualJoystickBridge.cs.meta create mode 100644 Assets/Plugins/InputModule/Runtime.meta create mode 100644 Assets/Plugins/InputModule/Runtime/BindingOverridePersistence.cs create mode 100644 Assets/Plugins/InputModule/Runtime/BindingOverridePersistence.cs.meta create mode 100644 Assets/Plugins/InputModule/Runtime/InputDeviceKindUtility.cs create mode 100644 Assets/Plugins/InputModule/Runtime/InputDeviceKindUtility.cs.meta create mode 100644 Assets/Plugins/InputModule/Runtime/InputModuleComponent.cs create mode 100644 Assets/Plugins/InputModule/Runtime/InputModuleComponent.cs.meta create mode 100644 Assets/Plugins/InputModule/Runtime/InputModuleDefaultActions.cs create mode 100644 Assets/Plugins/InputModule/Runtime/InputModuleDefaultActions.cs.meta create mode 100644 Assets/Plugins/InputModule/Runtime/InputModuleDefaultPromptMap.cs create mode 100644 Assets/Plugins/InputModule/Runtime/InputModuleDefaultPromptMap.cs.meta create mode 100644 Assets/Plugins/InputModule/Runtime/SepCore.Runtime.InputModule.asmref create mode 100644 Assets/Plugins/InputModule/Runtime/SepCore.Runtime.InputModule.asmref.meta create mode 100644 Assets/Plugins/InputModule/Tests.meta create mode 100644 Assets/Plugins/InputModule/Tests/PlayMode.meta create mode 100644 Assets/Plugins/InputModule/Tests/PlayMode/InputModule.meta create mode 100644 Assets/Plugins/InputModule/Tests/PlayMode/InputModule/BindingOverridePersistenceTests.cs create mode 100644 Assets/Plugins/InputModule/Tests/PlayMode/InputModule/BindingOverridePersistenceTests.cs.meta create mode 100644 Assets/Plugins/InputModule/Tests/PlayMode/InputModule/InputContextStackTests.cs create mode 100644 Assets/Plugins/InputModule/Tests/PlayMode/InputModule/InputContextStackTests.cs.meta create mode 100644 Assets/Plugins/InputModule/Tests/PlayMode/InputModule/InputModuleComponentContextTests.cs create mode 100644 Assets/Plugins/InputModule/Tests/PlayMode/InputModule/InputModuleComponentContextTests.cs.meta create mode 100644 Assets/Plugins/InputModule/Tests/PlayMode/InputModule/InputModuleComponentEventTests.cs create mode 100644 Assets/Plugins/InputModule/Tests/PlayMode/InputModule/InputModuleComponentEventTests.cs.meta create mode 100644 Assets/Plugins/InputModule/Tests/PlayMode/InputModule/InputModuleComponentRebindTests.cs create mode 100644 Assets/Plugins/InputModule/Tests/PlayMode/InputModule/InputModuleComponentRebindTests.cs.meta create mode 100644 Assets/Plugins/InputModule/Tests/PlayMode/InputModule/InputModuleDebuggerReflectionTests.cs create mode 100644 Assets/Plugins/InputModule/Tests/PlayMode/InputModule/InputModuleDebuggerReflectionTests.cs.meta create mode 100644 Assets/Plugins/InputModule/Tests/PlayMode/InputModule/InputModuleDefaultActionsTests.cs create mode 100644 Assets/Plugins/InputModule/Tests/PlayMode/InputModule/InputModuleDefaultActionsTests.cs.meta create mode 100644 Assets/Plugins/InputModule/Tests/PlayMode/InputModule/InputModuleDefaultPromptMapTests.cs create mode 100644 Assets/Plugins/InputModule/Tests/PlayMode/InputModule/InputModuleDefaultPromptMapTests.cs.meta create mode 100644 Assets/Plugins/InputModule/Tests/PlayMode/InputModule/InputPromptTests.cs create mode 100644 Assets/Plugins/InputModule/Tests/PlayMode/InputModule/InputPromptTests.cs.meta create mode 100644 Assets/Plugins/InputModule/Tests/PlayMode/InputModule/SepCore.InputModule.Tests.asmdef create mode 100644 Assets/Plugins/InputModule/Tests/PlayMode/InputModule/SepCore.InputModule.Tests.asmdef.meta create mode 100644 Assets/Plugins/InputModule/常见问题.md create mode 100644 Assets/Plugins/InputModule/常见问题.md.meta create mode 100644 Assets/Plugins/InputModule/接入说明.md create mode 100644 Assets/Plugins/InputModule/接入说明.md.meta delete mode 100644 openspec/changes/archive/2026-04-02-converge-simulationworld-runtime-paths/.openspec.yaml delete mode 100644 openspec/changes/archive/2026-04-02-converge-simulationworld-runtime-paths/design.md delete mode 100644 openspec/changes/archive/2026-04-02-converge-simulationworld-runtime-paths/proposal.md delete mode 100644 openspec/changes/archive/2026-04-02-converge-simulationworld-runtime-paths/specs/simulationworld-runtime-convergence/spec.md delete mode 100644 openspec/changes/archive/2026-04-02-converge-simulationworld-runtime-paths/tasks.md delete mode 100644 openspec/changes/archive/2026-04-02-remove-simulationworld-legacy-movement-shims/.openspec.yaml delete mode 100644 openspec/changes/archive/2026-04-02-remove-simulationworld-legacy-movement-shims/design.md delete mode 100644 openspec/changes/archive/2026-04-02-remove-simulationworld-legacy-movement-shims/proposal.md delete mode 100644 openspec/changes/archive/2026-04-02-remove-simulationworld-legacy-movement-shims/specs/simulationworld-runtime-convergence/spec.md delete mode 100644 openspec/changes/archive/2026-04-02-remove-simulationworld-legacy-movement-shims/tasks.md delete mode 100644 openspec/config.yaml delete mode 100644 openspec/specs/simulationworld-runtime-convergence/spec.md diff --git a/.claude/commands/opsx/apply.md b/.claude/commands/opsx/apply.md deleted file mode 100644 index bf23721..0000000 --- a/.claude/commands/opsx/apply.md +++ /dev/null @@ -1,152 +0,0 @@ ---- -name: "OPSX: Apply" -description: Implement tasks from an OpenSpec change (Experimental) -category: Workflow -tags: [workflow, artifacts, experimental] ---- - -Implement tasks from an OpenSpec change. - -**Input**: Optionally specify a change name (e.g., `/opsx:apply add-auth`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes. - -**Steps** - -1. **Select the change** - - If a name is provided, use it. Otherwise: - - Infer from conversation context if the user mentioned a change - - Auto-select if only one active change exists - - If ambiguous, run `openspec list --json` to get available changes and use the **AskUserQuestion tool** to let the user select - - Always announce: "Using change: " and how to override (e.g., `/opsx:apply `). - -2. **Check status to understand the schema** - ```bash - openspec status --change "" --json - ``` - Parse the JSON to understand: - - `schemaName`: The workflow being used (e.g., "spec-driven") - - Which artifact contains the tasks (typically "tasks" for spec-driven, check status for others) - -3. **Get apply instructions** - - ```bash - openspec instructions apply --change "" --json - ``` - - This returns: - - Context file paths (varies by schema) - - Progress (total, complete, remaining) - - Task list with status - - Dynamic instruction based on current state - - **Handle states:** - - If `state: "blocked"` (missing artifacts): show message, suggest using `/opsx:continue` - - If `state: "all_done"`: congratulate, suggest archive - - Otherwise: proceed to implementation - -4. **Read context files** - - Read the files listed in `contextFiles` from the apply instructions output. - The files depend on the schema being used: - - **spec-driven**: proposal, specs, design, tasks - - Other schemas: follow the contextFiles from CLI output - -5. **Show current progress** - - Display: - - Schema being used - - Progress: "N/M tasks complete" - - Remaining tasks overview - - Dynamic instruction from CLI - -6. **Implement tasks (loop until done or blocked)** - - For each pending task: - - Show which task is being worked on - - Make the code changes required - - Keep changes minimal and focused - - Mark task complete in the tasks file: `- [ ]` → `- [x]` - - Continue to next task - - **Pause if:** - - Task is unclear → ask for clarification - - Implementation reveals a design issue → suggest updating artifacts - - Error or blocker encountered → report and wait for guidance - - User interrupts - -7. **On completion or pause, show status** - - Display: - - Tasks completed this session - - Overall progress: "N/M tasks complete" - - If all done: suggest archive - - If paused: explain why and wait for guidance - -**Output During Implementation** - -``` -## Implementing: (schema: ) - -Working on task 3/7: -[...implementation happening...] -✓ Task complete - -Working on task 4/7: -[...implementation happening...] -✓ Task complete -``` - -**Output On Completion** - -``` -## Implementation Complete - -**Change:** -**Schema:** -**Progress:** 7/7 tasks complete ✓ - -### Completed This Session -- [x] Task 1 -- [x] Task 2 -... - -All tasks complete! You can archive this change with `/opsx:archive`. -``` - -**Output On Pause (Issue Encountered)** - -``` -## Implementation Paused - -**Change:** -**Schema:** -**Progress:** 4/7 tasks complete - -### Issue Encountered - - -**Options:** -1.