引入 UISerializationRoot 和 Item

- 完成 MenuForm 和 SelectRoleForm 的迁移
This commit is contained in:
2026-09-13 22:18:29 +08:00
parent e2fdb660ab
commit 091a29db63
101 changed files with 5281 additions and 5753 deletions
@@ -34,7 +34,7 @@ namespace SepCore.UI
private static string BuildPromptContext(InputActionId actionId)
{
if (GameEntry.InputModule == null || !GameEntry.InputModule.TryGetPrompt(actionId, out InputPrompt prompt))
if (!GameEntry.InputModule.TryGetPrompt(actionId, out InputPrompt prompt))
{
return null;
}
@@ -46,7 +46,7 @@ namespace SepCore.UI
{
if (userData != null)
{
Log.Warning("HudController.OpenUIAsync() userData type is invalid.");
Log.Error("HudController.OpenUIAsync() userData type is invalid.");
return;
}
@@ -60,11 +60,6 @@ namespace SepCore.UI
private void OnDeviceKindChanged(InputDeviceKind deviceKind)
{
if (Context == null || Form == null)
{
return;
}
Context.MovePrompt = BuildPromptContext(InputActionId.Move);
Form.RefreshMovePrompt(Context.MovePrompt);
}
@@ -51,21 +51,14 @@ namespace SepCore.UI
public void RefreshUI(HudContext hudContext)
{
RefreshMovePrompt(hudContext?.MovePrompt);
RefreshMovePrompt(hudContext.MovePrompt);
}
public void RefreshMovePrompt(string prompt)
{
bool visible = !string.IsNullOrEmpty(prompt);
if (_movePromptRoot != null)
{
_movePromptRoot.SetActive(visible);
}
if (_movePromptText != null)
{
_movePromptText.text = prompt;
}
_movePromptRoot.SetActive(visible);
_movePromptText.text = prompt;
}
#region FSM
@@ -182,8 +175,6 @@ namespace SepCore.UI
private void RefreshPerformanceStats()
{
if (_cpuTimeCostText == null) return;
bool hasCpuFrame = TryGetLastValue(_cpuTimeRecorder, out long cpuFrameNanoseconds);
if (!hasCpuFrame)
@@ -29,7 +29,7 @@ namespace SepCore.UI
ShowRecycleButton = false;
}
public ItemTooltipContext(ItemTooltipRawData rawData)
public ItemTooltipContext(ItemTooltipRawData rawData) : this()
{
Index = rawData.Index;
ItemType = rawData.ItemType;
@@ -37,19 +37,15 @@ namespace SepCore.UI
Price = rawData.Price;
TargetPos = rawData.TargetPos;
ShowRecycleButton = rawData.OnRecycle != null;
if (ItemType == ItemType.None)
{
IconAssetName = string.Empty;
Title = string.Empty;
}
else if (ItemType == ItemType.Weapon)
if (ItemType == ItemType.Weapon)
{
var data = rawData.WeaponData;
IconAssetName = data.IconAssetName;
Title = data.Title;
Description = ItemDescUtility.CreateWeaponDescription(data);
}
else
else if (ItemType == ItemType.Prop)
{
var data = rawData.PropData;
IconAssetName = data.IconAssetName;
@@ -71,7 +71,6 @@ namespace SepCore.UI
ItemTooltipContext context = BuildContext(rawData);
if (context == null)
{
Log.Warning("ItemTooltipController.OpenUIAsync() rawData is invalid.");
return;
}
@@ -89,7 +88,7 @@ namespace SepCore.UI
{
if (useCase != null)
{
Log.Warning("ItemTooltipController does not use a use case.");
Log.Error("ItemTooltipController does not use a use case.");
}
}
@@ -40,33 +40,21 @@ namespace SepCore.UI
public async UniTaskVoid RefreshUI(ItemTooltipContext context)
{
if (context == null)
{
Log.Warning("ItemTooltipForm context is invalid.");
return;
}
_context = context;
_targetPos = ConvertWorldToAnchored(_context.TargetPos);
Log.Info($"{_context.TargetPos}->{_targetPos}");
if (_content != null)
{
_content.anchoredPosition3D = _targetPos;
}
_content.anchoredPosition3D = _targetPos;
_itemTitle.text = _context.Title ?? string.Empty;
_itemTypeText.text = BuildTypeText(_context.ItemType) ?? string.Empty;
_itemDescription.text = _context.Description ?? string.Empty;
_itemTitle.text = _context.Title;
_itemTypeText.text = BuildTypeText(_context.ItemType);
_itemDescription.text = _context.Description;
_itemPrice.text = $"{_context.Price} <sprite name=\"coin\" index=0>";
if (_recycleButton != null)
{
_recycleButton.gameObject.SetActive(_context.ShowRecycleButton);
}
_recycleButton.gameObject.SetActive(_context.ShowRecycleButton);
if (_iconArea != null && !string.IsNullOrEmpty(_context.IconAssetName))
if (!string.IsNullOrEmpty(_context.IconAssetName))
{
string iconAssetName = _context.IconAssetName;
var sprite = await SpriteLoader.LoadSpriteByKeyAsync(iconAssetName);
@@ -103,12 +91,7 @@ namespace SepCore.UI
private void ResizeToFitContent()
{
if (!_autoResizeHeight || _content == null)
{
return;
}
if (_itemDescription == null)
if (!_autoResizeHeight)
{
return;
}
@@ -126,11 +109,6 @@ namespace SepCore.UI
private void ApplyClampedTargetPos()
{
if (_content == null)
{
return;
}
RectTransform parent = _content.parent as RectTransform;
if (parent == null)
{
@@ -160,11 +138,6 @@ namespace SepCore.UI
private Vector3 ConvertWorldToAnchored(Vector3 worldPos)
{
if (_content == null)
{
return worldPos;
}
RectTransform parent = _content.parent as RectTransform;
if (parent == null)
{
@@ -192,7 +165,6 @@ namespace SepCore.UI
public void OnRecycleButtonClick()
{
if (_context == null) return;
GameEntry.Event.Fire(this, ItemTooltipRecycleEventArgs.Create(_context.Index, _context.Price));
}
@@ -38,7 +38,6 @@ namespace SepCore.UI
private void OnCommandTriggered(InputCommand cmd)
{
if (cmd.ActionId != InputActionId.Move) return;
if (Context == null || Form == null) return;
Context.Direction = cmd.Vector2Value;
Context.Magnitude = cmd.Vector2Value.magnitude;
@@ -39,20 +39,9 @@ namespace SepCore.UI
return null;
}
if (rawData.Rewards == null)
{
Log.Error("LevelUpFormController.BuildContext() rewards are null.");
return null;
}
List<LevelUpRewardItemContext> props = new List<LevelUpRewardItemContext>(rawData.Rewards.Count);
foreach (var reward in rawData.Rewards)
{
if (reward == null)
{
continue;
}
props.Add(new LevelUpRewardItemContext
{
Title = reward.Title,
@@ -80,7 +69,7 @@ namespace SepCore.UI
if (userData != null)
{
Log.Warning("LevelUpController.OpenUIAsync() userData type is invalid.");
Log.Error("LevelUpController.OpenUIAsync() userData type is invalid.");
return;
}
@@ -22,7 +22,6 @@ namespace SepCore.UI
propItem.gameObject.SetActive(false);
}
if (_context.Props == null) return;
int count = Mathf.Min(_propItems.Length, _context.Props.Count);
for (int i = 0; i < count; i++)
{
@@ -40,13 +39,13 @@ namespace SepCore.UI
{
base.OnOpen(userData);
if (userData is LevelUpContext context)
if (userData is not LevelUpContext context)
{
RefreshUI(context);
Log.Error("LevelUpForm requires LevelUpContext as userData.");
return;
}
Log.Warning("LevelUpForm requires LevelUpFormContext as userData.");
RefreshUI(context);
}
protected override void OnClose(bool isShutdown, object userData)
@@ -30,17 +30,15 @@ namespace SepCore.UI
_context = context;
if (_titleText != null) _titleText.text = context.Title;
if (_descriptionText != null) _descriptionText.text = context.Description;
if (_iconArea != null) _iconArea.OnInit(context.Icon, context.Rarity);
_titleText.text = context.Title;
_descriptionText.text = context.Description;
_iconArea.OnInit(context.Icon, context.Rarity);
LoadIcon(_context.IconAssetName).Forget();
}
private async UniTaskVoid LoadIcon(string iconAssetName)
{
if (_iconArea == null) return;
if (string.IsNullOrEmpty(iconAssetName)) return;
var sprite = await SpriteLoader.LoadSpriteByKeyAsync(iconAssetName);
@@ -23,16 +23,11 @@ namespace SepCore.UI
public async UniTaskVoid OnInit(DisplayItemContext context, int index)
{
if (context == null) return;
_context = context;
_index = index;
if (_iconArea != null)
{
var sprite = await SpriteLoader.LoadSpriteByKeyAsync(_context.IconAssetName);
_iconArea.OnInit(sprite, context.Rarity);
}
var sprite = await SpriteLoader.LoadSpriteByKeyAsync(_context.IconAssetName);
_iconArea.OnInit(sprite, _context.Rarity);
}
public void OnReset()
@@ -40,16 +35,12 @@ namespace SepCore.UI
_context = null;
_index = -1;
if (_iconArea != null)
{
_iconArea.OnReset();
}
_iconArea.OnReset();
}
public void OnItemInfoShow()
{
if (_index < 0) return;
if (_itemRect == null) return;
Rect rect = _itemRect.rect;
Vector3 targetPos = _itemRect.TransformPoint(new Vector3(
@@ -44,20 +44,13 @@ namespace SepCore.UI
public void OnInit(DisplayListAreaContext context)
{
if (context == null)
{
Log.Warning("DisplayListArea context is invalid.");
return;
}
EnsurePool();
_context = context;
if (_titleText != null) _titleText.text = context.Title;
_titleText.text = context.Title;
UpdateCountText(context.CurrentCount, context.MaxCount);
int itemCount = context.ItemContexts?.Length ?? 0;
int targetCount = Mathf.Min(context.CurrentCount, itemCount);
int targetCount = Mathf.Min(context.CurrentCount, context.ItemContexts.Length);
EnsureItemCount(targetCount);
for (int i = 0; i < targetCount; i++)
{
@@ -85,16 +78,8 @@ namespace SepCore.UI
public DisplayItem AddItem(DisplayItemContext itemContext)
{
EnsurePool();
if (_displayItemObjectPool == null)
{
return null;
}
DisplayItem item = CreateItem();
if (item == null)
{
return null;
}
int index = _activeItems.Count;
_activeItems.Add(item);
@@ -105,13 +90,9 @@ namespace SepCore.UI
LayoutItem(item, index, columnCount);
int currentCount = _activeItems.Count;
int maxCount = _context != null ? _context.MaxCount : -1;
if (_context != null)
{
_context.CurrentCount = currentCount;
}
_context.CurrentCount = currentCount;
UpdateCountText(currentCount, maxCount);
UpdateCountText(currentCount, _context.MaxCount);
return item;
}
@@ -127,13 +108,9 @@ namespace SepCore.UI
LayoutItems();
int currentCount = _activeItems.Count;
int maxCount = _context != null ? _context.MaxCount : -1;
if (_context != null)
{
_context.CurrentCount = currentCount;
}
_context.CurrentCount = currentCount;
UpdateCountText(currentCount, maxCount);
UpdateCountText(currentCount, _context.MaxCount);
return true;
}
@@ -142,11 +119,6 @@ namespace SepCore.UI
{
if (_displayItemObjectPool != null) return;
if (_displayItemRoot == null)
{
_displayItemRoot = transform;
}
GridLayoutGroup gridLayout = _displayItemRoot.GetComponent<GridLayoutGroup>();
if (gridLayout != null)
{
@@ -159,12 +131,6 @@ namespace SepCore.UI
return;
}
if (_displayItemTemplate == null)
{
Debug.LogError("DisplayListArea requires a DisplayItem template.");
return;
}
_displayItemObjectPool =
GameEntry.ObjectPool.CreateSingleSpawnObjectPool<DisplayItemObject>(DisplayItemPoolName,
_instancePoolCapacity);
@@ -172,17 +138,9 @@ namespace SepCore.UI
private void EnsureItemCount(int count)
{
if (_displayItemObjectPool == null) return;
for (int i = _activeItems.Count; i < count; i++)
{
DisplayItem item = CreateItem();
if (item == null)
{
break;
}
_activeItems.Add(item);
_activeItems.Add(CreateItem());
}
for (int i = _activeItems.Count - 1; i >= count; i--)
@@ -193,7 +151,7 @@ namespace SepCore.UI
private DisplayItem CreateItem()
{
DisplayItem item = null;
DisplayItem item;
DisplayItemObject itemObject = _displayItemObjectPool.Spawn();
if (itemObject != null)
{
@@ -201,39 +159,25 @@ namespace SepCore.UI
}
else
{
if (_displayItemTemplate == null)
{
Debug.LogError("DisplayListArea requires a DisplayItem template.");
return null;
}
item = Instantiate(_displayItemTemplate);
_displayItemObjectPool.Register(DisplayItemObject.Create(item), true);
}
if (_displayItemRoot != null)
{
Transform itemTransform = item.transform;
itemTransform.SetParent(_displayItemRoot, false);
}
item.transform.SetParent(_displayItemRoot, false);
item.gameObject.SetActive(true);
return item;
}
private void HideItem(DisplayItem item)
{
if (item == null) return;
item.OnReset();
_activeItems.Remove(item);
item.gameObject.SetActive(false);
_displayItemObjectPool?.Unspawn(item);
_displayItemObjectPool.Unspawn(item);
}
private void LayoutItems()
{
if (_displayItemRoot == null) return;
int columnCount = _fixedColumnCount > 0 ? _fixedColumnCount : CalculateColumnCount(GetRootWidth());
for (int i = 0; i < _activeItems.Count; i++)
@@ -246,9 +190,7 @@ namespace SepCore.UI
private void LayoutItem(DisplayItem item, int index, int columnCount)
{
if (item == null) return;
RectTransform itemRect = item.GetComponent<RectTransform>();
if (itemRect == null) return;
float step = _itemSize + _itemSpacing;
int row = index / columnCount;
@@ -263,8 +205,8 @@ namespace SepCore.UI
private float GetRootWidth()
{
RectTransform rootRect = _displayItemRoot as RectTransform;
return rootRect != null ? rootRect.rect.width : 0f;
RectTransform rootRect = (RectTransform)_displayItemRoot;
return rootRect.rect.width;
}
private int CalculateColumnCount(float rootWidth)
@@ -278,7 +220,6 @@ namespace SepCore.UI
private void UpdateCountText(int currentCount, int maxCount)
{
if (_countText == null) return;
_countText.text = maxCount == -1 ? $"({currentCount})" : $"({currentCount}/{maxCount})";
}
}
@@ -25,29 +25,13 @@ namespace SepCore.UI
ItemType = ItemType.Weapon;
}
public GoodsItemContext(GoodsItemRawData rawData)
public GoodsItemContext(GoodsItemRawData rawData) : this()
{
if (rawData == null)
{
Name = string.Empty;
Description = string.Empty;
Rarity = Rarity.White;
Price = 0;
Icon = null;
ItemType = ItemType.Weapon;
return;
}
Price = rawData.Price;
Rarity = rawData.Rarity;
ItemType = rawData.ItemType;
if (rawData.DataRow == null)
{
Icon = null;
Name = string.Empty;
}
else if (ItemType == ItemType.Weapon)
if (ItemType == ItemType.Weapon)
{
if (rawData.DataRow is WeaponConfig weaponConfig)
{
@@ -44,53 +44,32 @@ namespace SepCore.UI
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)
IReadOnlyList<WeaponBase> weapons = (IReadOnlyList<WeaponBase>)items;
for (int i = 0; i < weapons.Count; i++)
{
for (int i = 0; i < weapons.Count; i++)
{
WeaponBase weapon = weapons[i];
if (weapon == null) break;
itemContexts[i] = BuildWeaponItem(weapon);
}
itemContexts[i] = BuildWeaponItem(weapons[i]);
}
break;
case DisplayListAreaType.Prop:
if (items is IReadOnlyList<PropItem> propItems)
IReadOnlyList<PropItem> propItems = (IReadOnlyList<PropItem>)items;
for (int i = 0; i < propItems.Count; i++)
{
for (int i = 0; i < propItems.Count; i++)
{
PropItem propItem = propItems[i];
if (propItem == null) break;
itemContexts[i] = BuildPropItem(propItem);
}
itemContexts[i] = BuildPropItem(propItems[i]);
}
break;
}
int currentCount = itemContexts.Length;
return new DisplayListAreaContext
{
Title = title,
CurrentCount = currentCount,
Title = GetDisplayListTitle(listType),
CurrentCount = itemContexts.Length,
MaxCount = maxCount,
ItemContexts = itemContexts
};
@@ -108,45 +87,27 @@ namespace SepCore.UI
private static DisplayItemContext BuildPropItem(PropItem propItem)
{
string iconAssetName = null;
Rarity rarity = Rarity.None;
if (propItem != null)
{
iconAssetName = propItem.IconAssetName;
rarity = propItem.Rarity;
}
return new DisplayItemContext
{
IconAssetName = iconAssetName,
Rarity = rarity,
IconAssetName = propItem.IconAssetName,
Rarity = propItem.Rarity,
IsWeapon = false
};
}
private static DisplayItemContext BuildWeaponItem(WeaponBase weaponBase)
{
string iconAssetName = null;
Rarity rarity = Rarity.None;
if (weaponBase != null && weaponBase.WeaponData != null)
{
iconAssetName = weaponBase.WeaponData.IconAssetName;
rarity = weaponBase.WeaponData.Rarity;
}
return new DisplayItemContext
{
IconAssetName = iconAssetName,
Rarity = rarity,
IconAssetName = weaponBase.WeaponData.IconAssetName,
Rarity = weaponBase.WeaponData.Rarity,
IsWeapon = true
};
}
private static void AppendDisplayItemContext(DisplayListAreaContext listContext, DisplayItemContext newItem)
{
int oldCount = listContext.ItemContexts != null ? listContext.ItemContexts.Length : 0;
int oldCount = listContext.ItemContexts.Length;
DisplayItemContext[] newContexts = new DisplayItemContext[oldCount + 1];
if (oldCount > 0)
{
@@ -126,25 +126,22 @@ namespace SepCore.UI
private void ApplyGoodsPurchased(ShopPurchaseResult result)
{
if (Context.GoodsItems != null && result.GoodsIndex >= 0 && result.GoodsIndex < Context.GoodsItems.Count)
if (result.GoodsIndex >= 0 && result.GoodsIndex < Context.GoodsItems.Count)
{
Context.GoodsItems[result.GoodsIndex] = null;
}
DisplayItemContext context = new DisplayItemContext(result.DisplayItem);
if (result.DisplayItem != null)
if (result.DisplayItem.IsWeapon)
{
if (result.DisplayItem.IsWeapon)
{
AppendDisplayItemContext(Context.WeaponListContext, context);
Context.NeedRefreshWeaponList = true;
}
else
{
AppendDisplayItemContext(Context.PropListContext, context);
Context.NeedRefreshPropList = true;
}
AppendDisplayItemContext(Context.WeaponListContext, context);
Context.NeedRefreshWeaponList = true;
}
else
{
AppendDisplayItemContext(Context.PropListContext, context);
Context.NeedRefreshPropList = true;
}
Context.NeedRefreshGoodsItems = true;
@@ -242,11 +239,8 @@ namespace SepCore.UI
return;
}
if (Context.WeaponListContext != null)
{
int currentCount = Mathf.Max(0, Context.WeaponListContext.CurrentCount - 1);
Context.WeaponListContext.CurrentCount = currentCount;
}
int currentCount = Mathf.Max(0, Context.WeaponListContext.CurrentCount - 1);
Context.WeaponListContext.CurrentCount = currentCount;
Context.NeedRefreshWeaponList = true;
RefreshUI(Form, Context);
@@ -301,7 +295,7 @@ namespace SepCore.UI
return;
}
_useCase?.Continue();
_useCase.Continue();
}
private void DisplayItemHover(object sender, GameEventArgs e)
@@ -359,7 +353,6 @@ namespace SepCore.UI
private void PlayerCoinChange(object sender, GameEventArgs e)
{
if (e is not PlayerCoinChangeEventArgs args) return;
if (Context == null) return;
Context.PlayerCoin = args.CoinCount;
Context.NeedRefreshPlayerCoin = true;
@@ -54,13 +54,13 @@ namespace SepCore.UI
context.NeedRefreshPlayerCoin = false;
}
if (context.NeedRefreshPropList && _propListArea != null && context.PropListContext != null)
if (context.NeedRefreshPropList)
{
_propListArea.OnInit(context.PropListContext);
context.NeedRefreshPropList = false;
}
if (context.NeedRefreshWeaponList && _weaponListArea != null && context.WeaponListContext != null)
if (context.NeedRefreshWeaponList)
{
_weaponListArea.OnInit(context.WeaponListContext);
context.NeedRefreshWeaponList = false;
@@ -74,11 +74,6 @@ namespace SepCore.UI
item.gameObject.SetActive(false);
}
if (goodsItems == null)
{
return;
}
int count = Mathf.Min(_goodsItems.Length, goodsItems.Count);
for (int i = 0; i < count; i++)
{
@@ -94,54 +89,6 @@ namespace SepCore.UI
_refreshPriceText.text = $"刷新 -{refreshPrice} <sprite name=\"coin\">";
}
private 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()
@@ -167,21 +114,21 @@ namespace SepCore.UI
{
base.OnOpen(userData);
if (userData is ShopContext context)
if (userData is not ShopContext context)
{
RefreshUI(context);
Log.Error("ShopForm requires ShopContext as userData.");
return;
}
Log.Warning("ShopForm requires ShopFormContext as userData.");
RefreshUI(context);
}
protected override void OnClose(bool isShutdown, object userData)
{
_context = null;
_propListArea?.OnReset();
_weaponListArea?.OnReset();
_propListArea.OnReset();
_weaponListArea.OnReset();
base.OnClose(isShutdown, userData);
}