第一批重构,主要是物理迁移脚本和部分命名空间的调整

- 结构上迁移文件,将脚本分成 Base/Runtime/Presentation/Procedure/Editor 五大文件夹
- 调整 DataTable 和 Definition 下脚本的命名空间
- 依据 UI-5层架构设计规范 拆分原先的 UI 到 Runtime 和 Presentation 下
This commit is contained in:
SepComet
2026-06-02 19:12:34 +08:00
parent 56f74eee9a
commit 34c0784190
655 changed files with 1272 additions and 516 deletions
@@ -0,0 +1,192 @@
using System.Collections.Generic;
using CustomEvent;
using GameFramework.Event;
using TMPro;
using UnityEngine;
using UnityGameFramework.Runtime;
namespace UI
{
public class ShopForm : UGuiForm
{
#region Property
[SerializeField] private TMP_Text _titleText;
[SerializeField] private TMP_Text _continueButtonText;
[SerializeField] private TMP_Text _refreshPriceText;
[SerializeField] private TMP_Text _playerCoinText;
private int _currentCoin;
[SerializeField] private ShopGoodsItem[] _goodsItems;
[SerializeField] private DisplayListArea _propListArea;
[SerializeField] private DisplayListArea _weaponListArea;
private ShopFormContext _context;
#endregion
public void RefreshUI(ShopFormContext context)
{
_context = context;
_titleText.text = $"商店 (Lv.{context.CurrentLevel})";
_continueButtonText.text = $"继续 (Lv.{context.CurrentLevel + 1})";
RefreshRefreshPrice(context.RefreshPrice);
_playerCoinText.text = $"<sprite name=\"coin\" index=0> {context.PlayerCoin}";
RefreshGoodsItems(context.GoodsItems);
if (_propListArea != null && context.PropListContext != null)
{
_propListArea.OnInit(context.PropListContext);
}
if (_weaponListArea != null && context.WeaponListContext != null)
{
_weaponListArea.OnInit(context.WeaponListContext);
}
}
internal void RefreshGoodsItems(List<GoodsItemContext> goodsItems)
{
foreach (var item in _goodsItems)
{
item.gameObject.SetActive(false);
}
if (goodsItems == null)
{
return;
}
int count = Mathf.Min(_goodsItems.Length, goodsItems.Count);
for (int i = 0; i < count; i++)
{
_goodsItems[i].Init(goodsItems[i]);
_goodsItems[i].gameObject.SetActive(true);
}
}
internal void RefreshRefreshPrice(int refreshPrice)
{
_refreshPriceText.text = $"刷新 -{refreshPrice} <sprite name=\"coin\" index=0>";
}
internal void ApplyGoodsPurchased(int goodsIndex, DisplayItemContext displayItem)
{
SetGoodsItemVisible(goodsIndex, false);
if (displayItem == null)
{
return;
}
if (displayItem.IsWeapon)
{
AddWeaponDisplayItem(displayItem);
}
else
{
AddPropDisplayItem(displayItem);
}
}
private void SetGoodsItemVisible(int index, bool visible)
{
if (_goodsItems == null || index < 0 || index >= _goodsItems.Length)
{
Log.Warning("ShopForm.SetGoodsItemVisible: Invalid index.");
return;
}
_goodsItems[index].gameObject.SetActive(visible);
}
private void AddPropDisplayItem(DisplayItemContext context)
{
if (_propListArea == null || context == null) return;
_propListArea.AddItem(context);
}
private void AddWeaponDisplayItem(DisplayItemContext context)
{
if (_weaponListArea == null || context == null) return;
_weaponListArea.AddItem(context);
}
internal void RemoveWeaponDisplayItem(int index)
{
if (_weaponListArea == null) return;
_weaponListArea.RemoveItemAt(index);
}
#region ButtonClick
public void OnContinueButtonClick()
{
GameEntry.Event.Fire(this, ShopContinueEventArgs.Create());
}
public void OnPurchaseButtonClick(int index)
{
GameEntry.Event.Fire(this, ShopPurchaseEventArgs.Create(index));
}
public void OnRefreshButtonClick()
{
GameEntry.Event.Fire(this, RefreshEventArgs.Create(_context.RefreshPrice));
}
#endregion
#region FSM
protected override void OnOpen(object userData)
{
base.OnOpen(userData);
GameEntry.Event.Subscribe(PlayerCoinChangeEventArgs.EventId, OnPlayerCoinChange);
if (userData is ShopFormContext context)
{
RefreshUI(context);
return;
}
Log.Warning("ShopForm requires ShopFormContext as userData.");
}
protected override void OnClose(bool isShutdown, object userData)
{
_context = null;
GameEntry.Event.Unsubscribe(PlayerCoinChangeEventArgs.EventId, OnPlayerCoinChange);
_propListArea?.OnReset();
_weaponListArea?.OnReset();
base.OnClose(isShutdown, userData);
}
#endregion
#region Event Handlers
private void OnPlayerCoinChange(object sender, GameEventArgs e)
{
if (!(e is PlayerCoinChangeEventArgs args)) return;
if (args.CoinCount == _currentCoin) return;
_currentCoin = args.CoinCount;
_playerCoinText.text = $"<sprite name=\"coin\" index=0> {_currentCoin}";
}
#endregion
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a6f22432e5701804d9a78d0c07f4c334
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,15 @@
using System.Collections.Generic;
namespace UI
{
public class ShopFormContext : UIContext
{
public int CurrentLevel;
public int RefreshPrice;
public int PlayerCoin;
public List<GoodsItemContext> GoodsItems;
public DisplayListAreaContext PropListContext;
public DisplayListAreaContext WeaponListContext;
public float WeaponRecycleRate = 0.3f;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b0ef5183d2f87a54793b86d848e6524a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,543 @@
using System.Collections.Generic;
using CustomEvent;
using SepCore.Definition;
using CustomUtility;
using Entity.Weapon;
using GameFramework.Event;
using UnityEngine;
using UnityGameFramework.Runtime;
namespace UI
{
public class ShopFormController : UIFormControllerCommonBase<ShopFormContext, ShopForm>
{
private ShopFormUseCase _useCase;
private ShopFormRawData _rawData;
protected override UIFormType UIFormTypeId => UIFormType.ShopForm;
protected override void RefreshUI(ShopForm form, ShopFormContext context)
{
form.RefreshUI(context);
}
protected override void SubscribeCustomEvents()
{
GameEntry.Event.Subscribe(RefreshEventArgs.EventId, Refresh);
GameEntry.Event.Subscribe(ShopPurchaseEventArgs.EventId, ShopPurchase);
GameEntry.Event.Subscribe(ShopWeaponRecycleEventArgs.EventId, WeaponRecycle);
GameEntry.Event.Subscribe(ShopContinueEventArgs.EventId, ShopContinue);
GameEntry.Event.Subscribe(DisplayItemShowEventArgs.EventId, DisplayItemShow);
}
protected override void UnsubscribeCustomEvents()
{
GameEntry.Event.Unsubscribe(RefreshEventArgs.EventId, Refresh);
GameEntry.Event.Unsubscribe(ShopPurchaseEventArgs.EventId, ShopPurchase);
GameEntry.Event.Unsubscribe(ShopWeaponRecycleEventArgs.EventId, WeaponRecycle);
GameEntry.Event.Unsubscribe(ShopContinueEventArgs.EventId, ShopContinue);
GameEntry.Event.Unsubscribe(DisplayItemShowEventArgs.EventId, DisplayItemShow);
}
#region BuildContext
private ShopFormContext BuildContext(ShopFormRawData rawData)
{
if (rawData == null)
{
Log.Error("ShopFormController.BuildContext() rawData is null.");
return null;
}
_rawData = rawData;
return new ShopFormContext
{
CurrentLevel = rawData.CurrentLevel,
RefreshPrice = rawData.RefreshPrice,
PlayerCoin = rawData.PlayerCoin,
GoodsItems = rawData.GoodsItems,
PropListContext =
BuildDisplayListAreaContext(DisplayListAreaType.Prop, rawData.PropItems, rawData.PropMaxCount),
WeaponListContext = BuildDisplayListAreaContext(DisplayListAreaType.Weapon, rawData.WeaponItems,
rawData.WeaponMaxCount)
};
}
private static DisplayListAreaContext BuildDisplayListAreaContext(DisplayListAreaType listType,
IReadOnlyList<object> items,
int maxCount)
{
string title = GetDisplayListTitle(listType);
if (items == null)
{
return new DisplayListAreaContext
{
Title = title,
CurrentCount = 0,
MaxCount = maxCount,
ItemContexts = System.Array.Empty<DisplayItemContext>()
};
}
DisplayItemContext[] itemContexts = new DisplayItemContext[items.Count];
switch (listType)
{
case DisplayListAreaType.Weapon:
if (items is IReadOnlyList<WeaponBase> weapons)
{
for (int i = 0; i < weapons.Count; i++)
{
WeaponBase weapon = weapons[i];
if (weapon == null) break;
itemContexts[i] = BuildWeaponItem(weapon);
}
}
break;
case DisplayListAreaType.Prop:
if (items is IReadOnlyList<PropItem> propItems)
{
for (int i = 0; i < propItems.Count; i++)
{
PropItem propItem = propItems[i];
if (propItem == null) break;
itemContexts[i] = BuildPropItem(propItem);
}
}
break;
}
int currentCount = itemContexts.Length;
return new DisplayListAreaContext
{
Title = title,
CurrentCount = currentCount,
MaxCount = maxCount,
ItemContexts = itemContexts
};
}
private static string GetDisplayListTitle(DisplayListAreaType listType)
{
return listType switch
{
DisplayListAreaType.Weapon => "武器",
DisplayListAreaType.Prop => "道具",
_ => string.Empty
};
}
private static DisplayItemContext BuildPropItem(PropItem propItem)
{
string iconAssetName = null;
ItemRarity rarity = ItemRarity.None;
if (propItem != null)
{
iconAssetName = propItem.IconAssetName;
rarity = propItem.Rarity;
}
return new DisplayItemContext
{
IconAssetName = iconAssetName,
Rarity = rarity,
IsWeapon = false
};
}
private static DisplayItemContext BuildWeaponItem(WeaponBase weaponBase)
{
string iconAssetName = null;
ItemRarity rarity = ItemRarity.None;
if (weaponBase != null && weaponBase.WeaponData != null)
{
iconAssetName = weaponBase.WeaponData.IconAssetName;
rarity = weaponBase.WeaponData.Rarity;
}
return new DisplayItemContext
{
IconAssetName = iconAssetName,
Rarity = rarity,
IsWeapon = true
};
}
private static void AppendDisplayItemContext(DisplayListAreaContext listContext, DisplayItemContext newItem)
{
if (listContext == null)
{
Log.Error("ShopFormController.AppendDisplayItemContext() listContext is null.");
return;
}
if (newItem == null)
{
Log.Warning("ShopFormController.AppendDisplayItemContext() newItem is null.");
return;
}
int oldCount = listContext.ItemContexts != null ? listContext.ItemContexts.Length : 0;
DisplayItemContext[] newContexts = new DisplayItemContext[oldCount + 1];
if (oldCount > 0)
{
System.Array.Copy(listContext.ItemContexts, newContexts, oldCount);
}
newContexts[oldCount] = newItem;
listContext.ItemContexts = newContexts;
listContext.CurrentCount = oldCount + 1;
}
#endregion
#region UI Methods
public override void CloseUI()
{
base.CloseUI();
GameEntry.Event.Fire(this, DisplayItemInfoHideEventArgs.Create(true));
}
public int? OpenUI(ShopFormRawData rawData)
{
ShopFormContext context = BuildContext(rawData);
return OpenUIInternal(context);
}
public override int? OpenUI(object userData = null)
{
if (userData is ShopFormContext context)
{
return OpenUIInternal(context);
}
if (userData is ShopFormRawData rawDataFromUserData)
{
return OpenUI(rawDataFromUserData);
}
if (userData != null)
{
Log.Warning("ShopFormController.OpenUI() userData type is invalid.");
return null;
}
if (_useCase == null)
{
Log.Error("ShopForm.OpenUI():: useCase is null.");
return null;
}
ShopFormRawData rawData = _useCase.CreateInitialModel();
return OpenUI(rawData);
}
public override void BindUseCase(IUIUseCase useCase)
{
if (!(useCase is ShopFormUseCase shopFormUseCase))
{
Log.Error("LevelUpForm.BindUseCase() useCase is invalid.");
return;
}
_useCase = shopFormUseCase;
}
#endregion
#region Service
private void RefreshGoodsItems(ShopRefreshResult result)
{
if (result == null)
{
Log.Error("ShopFormController.RefreshGoodsItems() result is null.");
return;
}
if (Context == null)
{
Log.Error("ShopFormController.RefreshGoodsItems() Context is null.");
return;
}
Context.GoodsItems = result.GoodsItems;
Context.RefreshPrice = result.RefreshPrice;
if (Form == null)
{
Log.Error("ShopFormController.RefreshGoodsItems() Form is null.");
return;
}
Form.RefreshGoodsItems(result.GoodsItems);
Form.RefreshRefreshPrice(result.RefreshPrice);
}
private void ApplyGoodsPurchased(ShopPurchaseResult result)
{
if (result == null)
{
Log.Error("ShopFormController.ApplyGoodsPurchased() result is null.");
return;
}
if (Context == null)
{
Log.Error("ShopFormController.ApplyGoodsPurchased() Context is null.");
return;
}
if (Context.GoodsItems != null && result.GoodsIndex >= 0 && result.GoodsIndex < Context.GoodsItems.Count)
{
Context.GoodsItems[result.GoodsIndex] = null;
}
if (result.DisplayItem != null)
{
if (result.DisplayItem.IsWeapon)
{
AppendDisplayItemContext(Context.WeaponListContext, result.DisplayItem);
}
else
{
AppendDisplayItemContext(Context.PropListContext, result.DisplayItem);
}
}
Form?.ApplyGoodsPurchased(result.GoodsIndex, result.DisplayItem);
}
private bool IsCurrentFormEventSender(object sender)
{
if (sender is ShopForm shopForm)
{
return shopForm == Form;
}
if (sender is Component component && Form != null)
{
return component.transform.IsChildOf(Form.transform);
}
return false;
}
private bool TryGetWeaponInfoRawData(int index, Vector3 targetPos, out DisplayItemInfoFormRawData rawData)
{
rawData = null;
if (_rawData?.WeaponItems == null)
{
Log.Error("ShopFormController.TryGetWeaponInfoRawData() WeaponItems is null.");
return false;
}
if (Context == null)
{
Log.Error("ShopFormController.TryGetWeaponInfoRawData() Context is null.");
return false;
}
if (index < 0 || index >= _rawData.WeaponItems.Count)
{
Log.Error($"ShopFormController.TryGetWeaponInfoRawData() invalid weapon index: {index}.");
return false;
}
WeaponBase weapon = _rawData.WeaponItems[index];
if (weapon?.WeaponData == null)
{
Log.Error($"ShopFormController.TryGetWeaponInfoRawData() weapon data is null at index {index}.");
return false;
}
var weaponData = weapon.WeaponData;
rawData = new DisplayItemInfoFormRawData
{
TargetPos = targetPos,
Index = index,
IconAssetName = weaponData.IconAssetName,
Title = weaponData.Title,
Rarity = weaponData.Rarity,
TypeText = "武器",
Description = ItemDescUtility.CreateWeaponDescription(weaponData),
Price = Mathf.FloorToInt(weaponData.Price * Context.WeaponRecycleRate),
IsWeapon = true
};
return true;
}
private bool TryGetPropInfoRawData(int index, Vector3 targetPos, out DisplayItemInfoFormRawData rawData)
{
rawData = null;
if (_rawData?.PropItems == null)
{
Log.Error("ShopFormController.TryGetPropInfoRawData() PropItems is null.");
return false;
}
if (index < 0 || index >= _rawData.PropItems.Count)
{
Log.Error($"ShopFormController.TryGetPropInfoRawData() invalid prop index: {index}.");
return false;
}
PropItem propItem = _rawData.PropItems[index];
if (propItem == null)
{
Log.Error($"ShopFormController.TryGetPropInfoRawData() prop item is null at index {index}.");
return false;
}
rawData = new DisplayItemInfoFormRawData
{
TargetPos = targetPos,
Index = index,
IconAssetName = propItem.IconAssetName,
Title = propItem.Title,
Rarity = propItem.Rarity,
TypeText = "道具",
Description = ItemDescUtility.CreatePropDescription(propItem),
Price = 0,
IsWeapon = false
};
return true;
}
#endregion
#region Event Handlers
private void Refresh(object sender, GameEventArgs e)
{
if (!(sender is ShopForm))
{
return;
}
if (!(e is RefreshEventArgs args))
{
return;
}
ShopRefreshResult result = _useCase.TryRefresh(args.Cost);
if (result == null)
{
return;
}
RefreshGoodsItems(result);
}
private void ShopPurchase(object sender, GameEventArgs e)
{
if (!(sender is ShopForm))
{
return;
}
if (!(e is ShopPurchaseEventArgs args))
{
return;
}
ShopPurchaseResult result = _useCase.TryPurchase(args.GoodsIndex);
if (result == null)
{
return;
}
ApplyGoodsPurchased(result);
}
private void ShopContinue(object sender, GameEventArgs e)
{
if (!(sender is ShopForm))
{
return;
}
if (!(e is ShopContinueEventArgs))
{
return;
}
_useCase?.Continue();
}
private void DisplayItemShow(object sender, GameEventArgs e)
{
if (!(e is DisplayItemShowEventArgs args))
{
return;
}
if (!IsCurrentFormEventSender(sender))
{
return;
}
if (_rawData == null)
{
Log.Error("ShopFormController.DisplayItemShow() _rawData is null.");
return;
}
DisplayItemInfoFormRawData rawData;
bool success = args.IsWeapon
? TryGetWeaponInfoRawData(args.Index, args.TargetPos, out rawData)
: TryGetPropInfoRawData(args.Index, args.TargetPos, out rawData);
if (!success)
{
return;
}
GameEntry.UIRouter.OpenUI(UIFormType.DisplayItemInfoForm, rawData);
}
private void WeaponRecycle(object sender, GameEventArgs e)
{
if (!(e is ShopWeaponRecycleEventArgs args))
{
return;
}
if (sender is not DisplayItemInfoForm)
{
return;
}
if (_useCase == null || Context == null)
{
Log.Error("ShopFormController.WeaponRecycle() controller state is invalid.");
return;
}
bool success = _useCase.TryRecycleWeapon(args.Index, args.Price);
if (!success)
{
return;
}
if (Context.WeaponListContext != null)
{
int currentCount = Mathf.Max(0, Context.WeaponListContext.CurrentCount - 1);
Context.WeaponListContext.CurrentCount = currentCount;
}
Form?.RemoveWeaponDisplayItem(args.Index);
GameEntry.Event.Fire(this, DisplayItemInfoHideEventArgs.Create(true));
}
#endregion
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: be57ace1e7b07be429632ea49bcab6c5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: