重构:统一 UI 模块目录结构规范
- 事件命名规范化:RefreshEventArgs → LevelUpRefreshEventArgs - LevelUp 子组件目录调整:LevelUpRewardItem + LevelUpRewardItemContext 移动到 LevelUp/ - Shop 子组件目录调整:DisplayItem/DisplayListArea/GoodsItem/ShopGoodsItem 及其 Context/RawData 全部移动到 Shop/ - 删除冗余文件:LevelUpRewardItemRawData(从未被使用) - 新增 ObjectPool 目录结构 - 更新 LevelUpController 和 LevelUpForm 中的事件引用
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using SepCore.Event;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace SepCore.UI
|
||||
{
|
||||
public class DisplayItem : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private IconArea _iconArea;
|
||||
|
||||
[SerializeField] private RectTransform _itemRect;
|
||||
|
||||
private DisplayItemContext _context;
|
||||
|
||||
private int _index = -1;
|
||||
|
||||
public void SetIndex(int index)
|
||||
{
|
||||
_index = index;
|
||||
}
|
||||
|
||||
public async UniTaskVoid OnInit(DisplayItemContext context, int index)
|
||||
{
|
||||
if (context == null) return;
|
||||
|
||||
_context = context;
|
||||
_index = index;
|
||||
|
||||
if (_iconArea != null)
|
||||
{
|
||||
var sprite = await GameEntry.SpriteCache.GetSprite(_context.IconAssetName);
|
||||
_iconArea.OnInit(sprite, context.Rarity);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnReset()
|
||||
{
|
||||
_context = null;
|
||||
_index = -1;
|
||||
|
||||
if (_iconArea != null)
|
||||
{
|
||||
_iconArea.OnReset();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnItemInfoShow()
|
||||
{
|
||||
if (_index < 0) return;
|
||||
if (_itemRect == null) return;
|
||||
|
||||
Rect rect = _itemRect.rect;
|
||||
Vector3 targetPos = _itemRect.TransformPoint(new Vector3(
|
||||
(rect.xMin + rect.xMax) / 2,
|
||||
rect.yMax,
|
||||
0f)
|
||||
);
|
||||
GameEntry.Event.Fire(this, DisplayItemHoverEventArgs.Create(_index, _context.IsWeapon, targetPos));
|
||||
}
|
||||
|
||||
public void OnItemInfoLock()
|
||||
{
|
||||
GameEntry.Event.Fire(this, DisplayItemLockEventArgs.Create());
|
||||
}
|
||||
|
||||
public void OnItemInfoHide()
|
||||
{
|
||||
GameEntry.Event.Fire(this, DisplayItemHideEventArgs.Create());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7fe0ad0e96348114786974bb02d62914
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,25 @@
|
||||
using SepCore.Definition;
|
||||
|
||||
namespace SepCore.UI
|
||||
{
|
||||
public class DisplayItemContext : UIContext
|
||||
{
|
||||
public string IconAssetName;
|
||||
public ItemRarity Rarity;
|
||||
public bool IsWeapon;
|
||||
|
||||
public DisplayItemContext()
|
||||
{
|
||||
IconAssetName = string.Empty;
|
||||
Rarity = ItemRarity.None;
|
||||
IsWeapon = false;
|
||||
}
|
||||
|
||||
public DisplayItemContext(DisplayItemRawData rawData)
|
||||
{
|
||||
this.IconAssetName = rawData.IconAssetName;
|
||||
this.Rarity = rawData.Rarity;
|
||||
this.IsWeapon = rawData.IsWeapon;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: df1bccc6ff70fbc4089c7809e35c1f61
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,285 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using GameFramework.ObjectPool;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace SepCore.UI
|
||||
{
|
||||
public enum DisplayListAreaType : byte
|
||||
{
|
||||
None = 0,
|
||||
Prop = 1,
|
||||
Weapon = 2
|
||||
}
|
||||
|
||||
public class DisplayListArea : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private TMP_Text _titleText;
|
||||
|
||||
[SerializeField] private TMP_Text _countText;
|
||||
|
||||
[SerializeField] private DisplayItem _displayItemTemplate;
|
||||
|
||||
[SerializeField] private Transform _displayItemRoot;
|
||||
|
||||
[SerializeField] private Vector2 _startPosition = Vector2.zero;
|
||||
|
||||
[SerializeField] private float _itemSize = 150f;
|
||||
|
||||
[SerializeField] private float _itemSpacing = 10f;
|
||||
|
||||
[SerializeField] private int _fixedColumnCount = 0;
|
||||
|
||||
[SerializeField] private int _instancePoolCapacity = 32;
|
||||
|
||||
private DisplayListAreaContext _context;
|
||||
|
||||
private IObjectPool<DisplayItemObject> _displayItemObjectPool;
|
||||
private const string DisplayItemPoolName = "DisplayItem";
|
||||
|
||||
private readonly List<DisplayItem> _activeItems = new List<DisplayItem>();
|
||||
|
||||
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;
|
||||
UpdateCountText(context.CurrentCount, context.MaxCount);
|
||||
|
||||
int itemCount = context.ItemContexts?.Length ?? 0;
|
||||
int targetCount = Mathf.Min(context.CurrentCount, itemCount);
|
||||
EnsureItemCount(targetCount);
|
||||
for (int i = 0; i < targetCount; i++)
|
||||
{
|
||||
_activeItems[i].OnInit(context.ItemContexts[i], i).Forget();
|
||||
}
|
||||
|
||||
LayoutItems();
|
||||
}
|
||||
|
||||
public void OnReset()
|
||||
{
|
||||
_context = null;
|
||||
|
||||
for (int i = _activeItems.Count - 1; i >= 0; i--)
|
||||
{
|
||||
HideItem(_activeItems[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnDestroy()
|
||||
{
|
||||
_displayItemObjectPool?.ReleaseAllUnused();
|
||||
}
|
||||
|
||||
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);
|
||||
item.OnInit(itemContext, index).Forget();
|
||||
item.SetIndex(index);
|
||||
|
||||
int columnCount = _fixedColumnCount > 0 ? _fixedColumnCount : CalculateColumnCount(GetRootWidth());
|
||||
LayoutItem(item, index, columnCount);
|
||||
|
||||
int currentCount = _activeItems.Count;
|
||||
int maxCount = _context != null ? _context.MaxCount : -1;
|
||||
if (_context != null)
|
||||
{
|
||||
_context.CurrentCount = currentCount;
|
||||
}
|
||||
|
||||
UpdateCountText(currentCount, maxCount);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
public bool RemoveItemAt(int index)
|
||||
{
|
||||
if (index < 0 || index >= _activeItems.Count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
HideItem(_activeItems[index]);
|
||||
LayoutItems();
|
||||
|
||||
int currentCount = _activeItems.Count;
|
||||
int maxCount = _context != null ? _context.MaxCount : -1;
|
||||
if (_context != null)
|
||||
{
|
||||
_context.CurrentCount = currentCount;
|
||||
}
|
||||
|
||||
UpdateCountText(currentCount, maxCount);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void EnsurePool()
|
||||
{
|
||||
if (_displayItemObjectPool != null) return;
|
||||
|
||||
if (_displayItemRoot == null)
|
||||
{
|
||||
_displayItemRoot = transform;
|
||||
}
|
||||
|
||||
GridLayoutGroup gridLayout = _displayItemRoot.GetComponent<GridLayoutGroup>();
|
||||
if (gridLayout != null)
|
||||
{
|
||||
gridLayout.enabled = false;
|
||||
}
|
||||
|
||||
if (GameEntry.ObjectPool.HasObjectPool<DisplayItemObject>(DisplayItemPoolName))
|
||||
{
|
||||
_displayItemObjectPool = GameEntry.ObjectPool.GetObjectPool<DisplayItemObject>(DisplayItemPoolName);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_displayItemTemplate == null)
|
||||
{
|
||||
Debug.LogError("DisplayListArea requires a DisplayItem template.");
|
||||
return;
|
||||
}
|
||||
|
||||
_displayItemObjectPool =
|
||||
GameEntry.ObjectPool.CreateSingleSpawnObjectPool<DisplayItemObject>(DisplayItemPoolName,
|
||||
_instancePoolCapacity);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
for (int i = _activeItems.Count - 1; i >= count; i--)
|
||||
{
|
||||
HideItem(_activeItems[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private DisplayItem CreateItem()
|
||||
{
|
||||
DisplayItem item = null;
|
||||
DisplayItemObject itemObject = _displayItemObjectPool.Spawn();
|
||||
if (itemObject != null)
|
||||
{
|
||||
item = (DisplayItem)itemObject.Target;
|
||||
}
|
||||
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.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);
|
||||
}
|
||||
|
||||
private void LayoutItems()
|
||||
{
|
||||
if (_displayItemRoot == null) return;
|
||||
|
||||
int columnCount = _fixedColumnCount > 0 ? _fixedColumnCount : CalculateColumnCount(GetRootWidth());
|
||||
|
||||
for (int i = 0; i < _activeItems.Count; i++)
|
||||
{
|
||||
DisplayItem item = _activeItems[i];
|
||||
item.SetIndex(i);
|
||||
LayoutItem(item, i, columnCount);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
int col = index % columnCount;
|
||||
|
||||
itemRect.anchorMin = new Vector2(0f, 1f);
|
||||
itemRect.anchorMax = new Vector2(0f, 1f);
|
||||
itemRect.pivot = new Vector2(0f, 1f);
|
||||
itemRect.sizeDelta = new Vector2(_itemSize, _itemSize);
|
||||
itemRect.anchoredPosition = _startPosition + new Vector2(col * step, -row * step);
|
||||
}
|
||||
|
||||
private float GetRootWidth()
|
||||
{
|
||||
RectTransform rootRect = _displayItemRoot as RectTransform;
|
||||
return rootRect != null ? rootRect.rect.width : 0f;
|
||||
}
|
||||
|
||||
private int CalculateColumnCount(float rootWidth)
|
||||
{
|
||||
if (rootWidth <= 0f) return 1;
|
||||
|
||||
float step = _itemSize + _itemSpacing;
|
||||
int columns = Mathf.FloorToInt((rootWidth + _itemSpacing) / step);
|
||||
return Mathf.Max(1, columns);
|
||||
}
|
||||
|
||||
private void UpdateCountText(int currentCount, int maxCount)
|
||||
{
|
||||
if (_countText == null) return;
|
||||
_countText.text = maxCount == -1 ? $"({currentCount})" : $"({currentCount}/{maxCount})";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 58c6ff306e4be41458bdf4dd82ad92e1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,14 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace SepCore.UI
|
||||
{
|
||||
public class DisplayListAreaContext : UIContext
|
||||
{
|
||||
public string Title;
|
||||
public int CurrentCount;
|
||||
public int MaxCount = -1;
|
||||
public DisplayItemContext[] ItemContexts;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a10dfda6cc0f45247bfcdbfb578110bc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,56 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using SepCore.CustomUtility;
|
||||
using SepCore.DataTable;
|
||||
using SepCore.Definition;
|
||||
using UnityEngine;
|
||||
|
||||
namespace SepCore.UI
|
||||
{
|
||||
public class GoodsItemContext : UIContext
|
||||
{
|
||||
public string Title;
|
||||
public ItemRarity Rarity;
|
||||
public Sprite Icon;
|
||||
public ItemType ItemType;
|
||||
public string Description;
|
||||
public int Price;
|
||||
|
||||
public GoodsItemContext()
|
||||
{
|
||||
Title = string.Empty;
|
||||
Description = string.Empty;
|
||||
Rarity = ItemRarity.White;
|
||||
Price = 0;
|
||||
Icon = null;
|
||||
ItemType = ItemType.None;
|
||||
}
|
||||
|
||||
public GoodsItemContext(GoodsItemRawData rawData)
|
||||
{
|
||||
|
||||
Price = rawData.Price;
|
||||
Rarity = rawData.Rarity;
|
||||
ItemType = rawData.ItemType;
|
||||
|
||||
|
||||
if (ItemType == ItemType.None)
|
||||
{
|
||||
Description = string.Empty;
|
||||
Icon = null;
|
||||
Title = string.Empty;
|
||||
}
|
||||
else if (ItemType == ItemType.Weapon)
|
||||
{
|
||||
var weapon = (DRWeapon)rawData.DataRow;
|
||||
Title = weapon.Title;
|
||||
Description = ItemDescUtility.CreateWeaponDescription(weapon);
|
||||
}
|
||||
else
|
||||
{
|
||||
var prop = (DRProp)rawData.DataRow;
|
||||
Title = prop.Title;
|
||||
Description = ItemDescUtility.CreatePropDescription(prop.Modifiers);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c9d0b321b02be5f41a27cc0a8bf84e34
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace SepCore.UI
|
||||
{
|
||||
public class ShopGoodsItem : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private IconArea _iconArea;
|
||||
|
||||
[SerializeField] private TMP_Text _titleText;
|
||||
|
||||
[SerializeField] private TMP_Text _typeText;
|
||||
|
||||
[SerializeField] private TMP_Text _descriptionText;
|
||||
|
||||
[SerializeField] private TMP_Text _costText;
|
||||
|
||||
#region Init
|
||||
|
||||
public void Init(GoodsItemContext data)
|
||||
{
|
||||
_iconArea.OnInit(data.Icon, data.Rarity);
|
||||
_titleText.text = data.Title;
|
||||
_typeText.text = data.ItemType switch
|
||||
{
|
||||
ItemType.None => "error",
|
||||
ItemType.Weapon => "武器",
|
||||
ItemType.Prop => "道具",
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
_descriptionText.text = data.Description;
|
||||
_costText.text = $"{data.Price} <sprite name=\"coin\" index=0>";
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 271f2a0727c521c4280eb2d47fd8de33
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user