- 规范 UI 设计成为:
- UIForm 负责展示
- Controller 负责控制
- UseCase 负责业务逻辑
- RawData 负责提供原始数据
- Context 负责为 UIForm 提供数据
- 添加和补充一系列 UI 功能
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
using CustomEvent;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace UI
|
||||
{
|
||||
public class DisplayItem : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private IconArea _iconArea;
|
||||
|
||||
[SerializeField] private RectTransform _itemRect;
|
||||
|
||||
private DisplayItemContext _context;
|
||||
|
||||
private int _index = -1;
|
||||
|
||||
public void OnInit(DisplayItemContext context, int index)
|
||||
{
|
||||
if (context == null) return;
|
||||
|
||||
_context = context;
|
||||
_index = index;
|
||||
|
||||
if (_iconArea != null)
|
||||
{
|
||||
string iconName = _context.IconAssetName;
|
||||
GameEntry.SpriteCache.GetSprite(iconName, sprite => { _iconArea.OnInit(sprite, context.Rarity); });
|
||||
}
|
||||
}
|
||||
|
||||
public void OnReset()
|
||||
{
|
||||
_context = null;
|
||||
_index = -1;
|
||||
|
||||
if (_iconArea != null)
|
||||
{
|
||||
_iconArea.OnReset();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnItemInfoShow()
|
||||
{
|
||||
if (_index < 0) return;
|
||||
Vector3 targetPos = _itemRect.position + new Vector3(0, _itemRect.sizeDelta.y / 2, 0);
|
||||
GameEntry.Event.Fire(this, DisplayItemShowEventArgs.Create(_index, _context.IsWeapon, targetPos));
|
||||
}
|
||||
|
||||
public void SetIndex(int index)
|
||||
{
|
||||
_index = index;
|
||||
}
|
||||
|
||||
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,191 @@
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace UI
|
||||
{
|
||||
public class DisplayItemInfoForm : UGuiForm
|
||||
{
|
||||
[SerializeField] private RectTransform _content;
|
||||
|
||||
[SerializeField] private IconArea _iconArea;
|
||||
|
||||
[SerializeField] private TMP_Text _itemTitle;
|
||||
|
||||
[SerializeField] private TMP_Text _itemTypeText;
|
||||
|
||||
[SerializeField] private TMP_Text _itemDescription;
|
||||
|
||||
[SerializeField] private CommonButton _recycleButton;
|
||||
|
||||
[SerializeField] private TMP_Text _itemPrice;
|
||||
|
||||
[SerializeField] private bool _autoResizeHeight = true;
|
||||
|
||||
[SerializeField] private float _fixedTopHeight = 150f;
|
||||
|
||||
[SerializeField] private float _fixedBottomHeight = 150f;
|
||||
|
||||
[SerializeField] private float _fixedWidth = 300f;
|
||||
|
||||
[SerializeField] private float _screenEdgePadding = 0f;
|
||||
|
||||
private DisplayItemInfoFormContext _context;
|
||||
private Vector3 _targetPos;
|
||||
|
||||
public void RefreshUI(DisplayItemInfoFormContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
Log.Warning("DisplayItemInfoForm context is invalid.");
|
||||
return;
|
||||
}
|
||||
|
||||
_context = context;
|
||||
|
||||
_targetPos = ConvertWorldToAnchored(_context.TargetPos);
|
||||
|
||||
if (_content != null)
|
||||
{
|
||||
_content.anchoredPosition3D = _targetPos;
|
||||
}
|
||||
|
||||
if (_itemTitle != null) _itemTitle.text = _context.Title ?? string.Empty;
|
||||
if (_itemTypeText != null) _itemTypeText.text = _context.TypeText ?? string.Empty;
|
||||
if (_itemDescription != null) _itemDescription.text = _context.Description ?? string.Empty;
|
||||
if (_itemPrice != null) _itemPrice.text = $"{_context.Price} <sprite name=\"coin\" index=0>";
|
||||
|
||||
if (_recycleButton != null)
|
||||
{
|
||||
_recycleButton.gameObject.SetActive(_context.IsWeapon);
|
||||
}
|
||||
|
||||
if (_iconArea != null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_context.IconAssetName))
|
||||
{
|
||||
string iconAssetName = _context.IconAssetName;
|
||||
GameEntry.SpriteCache.GetSprite(iconAssetName, sprite =>
|
||||
{
|
||||
if (_context != null && _context.IconAssetName == iconAssetName)
|
||||
{
|
||||
_iconArea.OnInit(sprite, context.Rarity);
|
||||
ResizeToFitContent();
|
||||
ApplyClampedTargetPos();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ResizeToFitContent();
|
||||
ApplyClampedTargetPos();
|
||||
}
|
||||
|
||||
protected override void OnOpen(object userData)
|
||||
{
|
||||
base.OnOpen(userData);
|
||||
|
||||
if (!(userData is DisplayItemInfoFormContext context))
|
||||
{
|
||||
Log.Error("DisplayItemInfoFormContext is invalid.");
|
||||
return;
|
||||
}
|
||||
|
||||
RefreshUI(context);
|
||||
}
|
||||
|
||||
protected override void OnClose(bool isShutdown, object userData)
|
||||
{
|
||||
_context = null;
|
||||
base.OnClose(isShutdown, userData);
|
||||
}
|
||||
|
||||
private void ResizeToFitContent()
|
||||
{
|
||||
if (!_autoResizeHeight || _content == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_itemDescription == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_itemDescription.ForceMeshUpdate();
|
||||
var descriptionSize = _itemDescription.rectTransform.sizeDelta;
|
||||
descriptionSize.y = _itemDescription.preferredHeight;
|
||||
_itemDescription.rectTransform.sizeDelta = descriptionSize;
|
||||
float descriptionHeight = Mathf.Max(0f, _itemDescription.preferredHeight);
|
||||
float targetHeight = _fixedTopHeight + descriptionHeight + _fixedBottomHeight;
|
||||
|
||||
Vector2 size = new Vector2(_fixedWidth, targetHeight);
|
||||
_content.sizeDelta = size;
|
||||
}
|
||||
|
||||
private void ApplyClampedTargetPos()
|
||||
{
|
||||
if (_content == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RectTransform parent = _content.parent as RectTransform;
|
||||
if (parent == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Vector2 size = _content.sizeDelta;
|
||||
if (size.x <= 0f || size.y <= 0f)
|
||||
{
|
||||
size = _content.rect.size;
|
||||
}
|
||||
|
||||
Vector2 pivot = _content.pivot;
|
||||
float halfWidth = parent.rect.width * 0.5f;
|
||||
float halfHeight = parent.rect.height * 0.5f;
|
||||
|
||||
float minX = -halfWidth + size.x * pivot.x + _screenEdgePadding;
|
||||
float maxX = halfWidth - size.x * (1f - pivot.x) - _screenEdgePadding;
|
||||
float minY = -halfHeight + size.y * pivot.y + _screenEdgePadding;
|
||||
float maxY = halfHeight - size.y * (1f - pivot.y) - _screenEdgePadding;
|
||||
|
||||
float clampedX = minX <= maxX ? Mathf.Clamp(_targetPos.x, minX, maxX) : 0f;
|
||||
float clampedY = minY <= maxY ? Mathf.Clamp(_targetPos.y, minY, maxY) : 0f;
|
||||
|
||||
_content.anchoredPosition3D = new Vector3(clampedX, clampedY, _targetPos.z);
|
||||
}
|
||||
|
||||
private Vector3 ConvertWorldToAnchored(Vector3 worldPos)
|
||||
{
|
||||
if (_content == null)
|
||||
{
|
||||
return worldPos;
|
||||
}
|
||||
|
||||
RectTransform parent = _content.parent as RectTransform;
|
||||
if (parent == null)
|
||||
{
|
||||
return worldPos;
|
||||
}
|
||||
|
||||
Canvas canvas = _content.GetComponentInParent<Canvas>();
|
||||
Camera uiCamera = null;
|
||||
if (canvas != null && canvas.renderMode != RenderMode.ScreenSpaceOverlay)
|
||||
{
|
||||
uiCamera = canvas.worldCamera;
|
||||
}
|
||||
|
||||
Vector2 screenPoint = RectTransformUtility.WorldToScreenPoint(uiCamera, worldPos);
|
||||
if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(parent, screenPoint, uiCamera, out Vector2 localPoint))
|
||||
{
|
||||
return worldPos;
|
||||
}
|
||||
|
||||
float z = _content.anchoredPosition3D.z;
|
||||
return new Vector3(localPoint.x, localPoint.y, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 32469245f20597240a3c51d2cd1fb0c5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,27 @@
|
||||
using GameFramework;
|
||||
using GameFramework.ObjectPool;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UI
|
||||
{
|
||||
public class DisplayItemObject : ObjectBase
|
||||
{
|
||||
public static DisplayItemObject Create(object target)
|
||||
{
|
||||
DisplayItemObject displayItemObject = ReferencePool.Acquire<DisplayItemObject>();
|
||||
displayItemObject.Initialize(target);
|
||||
return displayItemObject;
|
||||
}
|
||||
|
||||
protected override void Release(bool isShutdown)
|
||||
{
|
||||
DisplayItem item = (DisplayItem)Target;
|
||||
if (item == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Object.Destroy(item.gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f21ab051c635de04998b80804d9bafe5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,278 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using GameFramework.ObjectPool;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace UI
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
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:
|
||||
@@ -13,32 +13,47 @@ namespace UI
|
||||
[SerializeField] private float _sliderFadeDuration = 0.5f;
|
||||
|
||||
[SerializeField] private Slider _hpSlider;
|
||||
|
||||
[SerializeField] private TMP_Text _hpText;
|
||||
|
||||
private float _currentHpPercent = 0;
|
||||
private Coroutine _hpSliderFadeCoroutine;
|
||||
|
||||
[SerializeField] private Slider _expSlider;
|
||||
|
||||
private float _currentExpPercent = 0;
|
||||
private Coroutine _expSliderFadeCoroutine;
|
||||
|
||||
[SerializeField] private TMP_Text _expText;
|
||||
|
||||
private int _playerCurrentLevel = 1;
|
||||
|
||||
[SerializeField] private TMP_Text _coinText;
|
||||
private int _currentCoin = 0;
|
||||
|
||||
public void RefreshUI(HudFormContext hudFormContext)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#region FSM
|
||||
|
||||
protected override void OnInit(object userData)
|
||||
{
|
||||
base.OnInit(userData);
|
||||
|
||||
GameEntry.Event.Subscribe(PlayerHealthChangeEventArgs.EventId, OnPlayerHpChange);
|
||||
GameEntry.Event.Subscribe(PlayerExpChangeEventArgs.EventId, OnPlayerExpChange);
|
||||
GameEntry.Event.Subscribe(PlayerCoinChangeEventArgs.EventId, OnPlayerCoinChange);
|
||||
GameEntry.Event.Subscribe(PlayerHealthChangeEventArgs.EventId, PlayerHpChange);
|
||||
GameEntry.Event.Subscribe(PlayerExpChangeEventArgs.EventId, PlayerExpChange);
|
||||
GameEntry.Event.Subscribe(PlayerCoinChangeEventArgs.EventId, PlayerCoinChange);
|
||||
GameEntry.Event.Subscribe(PlayerLevelUpEventArgs.EventId, PlayerLevelUp);
|
||||
}
|
||||
|
||||
protected override void OnClose(bool isShutdown, object userData)
|
||||
{
|
||||
GameEntry.Event.Unsubscribe(PlayerHealthChangeEventArgs.EventId, OnPlayerHpChange);
|
||||
GameEntry.Event.Unsubscribe(PlayerExpChangeEventArgs.EventId, OnPlayerExpChange);
|
||||
GameEntry.Event.Unsubscribe(PlayerCoinChangeEventArgs.EventId, OnPlayerCoinChange);
|
||||
GameEntry.Event.Unsubscribe(PlayerLevelUpEventArgs.EventId, PlayerLevelUp);
|
||||
GameEntry.Event.Unsubscribe(PlayerHealthChangeEventArgs.EventId, PlayerHpChange);
|
||||
GameEntry.Event.Unsubscribe(PlayerExpChangeEventArgs.EventId, PlayerExpChange);
|
||||
GameEntry.Event.Unsubscribe(PlayerCoinChangeEventArgs.EventId, PlayerCoinChange);
|
||||
|
||||
base.OnClose(isShutdown, userData);
|
||||
}
|
||||
@@ -47,9 +62,12 @@ namespace UI
|
||||
|
||||
#region Event Handlers
|
||||
|
||||
private void OnPlayerHpChange(object sender, GameEventArgs e)
|
||||
private void PlayerHpChange(object sender, GameEventArgs e)
|
||||
{
|
||||
if (!(e is PlayerHealthChangeEventArgs args)) return;
|
||||
|
||||
_hpText.text = $"{args.CurrentHealth}/{args.MaxHealth}";
|
||||
|
||||
float percent = (float)args.CurrentHealth / args.MaxHealth;
|
||||
if (Mathf.Approximately(_currentHpPercent, percent)) return;
|
||||
|
||||
@@ -59,7 +77,7 @@ namespace UI
|
||||
_hpSliderFadeCoroutine = StartCoroutine(_hpSlider.SmoothValue(percent, _sliderFadeDuration));
|
||||
}
|
||||
|
||||
private void OnPlayerExpChange(object sender, GameEventArgs e)
|
||||
private void PlayerExpChange(object sender, GameEventArgs e)
|
||||
{
|
||||
if (!(e is PlayerExpChangeEventArgs args)) return;
|
||||
float percent = (float)args.CurrentExp / args.MaxExp;
|
||||
@@ -71,12 +89,20 @@ namespace UI
|
||||
_expSliderFadeCoroutine = StartCoroutine(_expSlider.SmoothValue(percent, _sliderFadeDuration));
|
||||
}
|
||||
|
||||
private void OnPlayerCoinChange(object sender, GameEventArgs e)
|
||||
private void PlayerLevelUp(object sender, GameEventArgs e)
|
||||
{
|
||||
if (!(e is PlayerLevelUpEventArgs)) return;
|
||||
|
||||
_playerCurrentLevel++;
|
||||
_expText.text = $"LV.{_playerCurrentLevel}";
|
||||
}
|
||||
|
||||
private void PlayerCoinChange(object sender, GameEventArgs e)
|
||||
{
|
||||
if (!(e is PlayerCoinChangeEventArgs args)) return;
|
||||
if (_currentCoin == args.CoinCount) return;
|
||||
|
||||
_coinText.text = args.CoinCount.ToString();
|
||||
_coinText.text = $"<sprite name=\"coin\" index=0> {args.CoinCount}";
|
||||
_currentCoin = args.CoinCount;
|
||||
}
|
||||
|
||||
@@ -100,4 +126,4 @@ namespace UI
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,21 +7,16 @@ namespace UI
|
||||
{
|
||||
public class LevelUpForm : UGuiForm
|
||||
{
|
||||
[SerializeField] private TMP_Text _titleText;
|
||||
[SerializeField] private LevelUpRewardItem[] _propItems;
|
||||
|
||||
[SerializeField] private LevelUpPropItem[] _propItems;
|
||||
[SerializeField] private TMP_Text _refreshButtonText;
|
||||
|
||||
private LevelUpFormContext _context;
|
||||
|
||||
public void RefreshUI(LevelUpFormContext context)
|
||||
{
|
||||
_context = context;
|
||||
|
||||
if (_titleText != null)
|
||||
{
|
||||
_titleText.text = $"Level Up (Lv.{_context.Level})";
|
||||
}
|
||||
|
||||
|
||||
foreach (var propItem in _propItems)
|
||||
{
|
||||
propItem.gameObject.SetActive(false);
|
||||
@@ -33,6 +28,9 @@ namespace UI
|
||||
_propItems[i].gameObject.SetActive(true);
|
||||
_propItems[i].Init(_context.Props[i]);
|
||||
}
|
||||
|
||||
//刷新 -20 <sprite name="coin" index=0>
|
||||
_refreshButtonText.text = $"刷新 -{context.RefreshPrice} <sprite name=\"coin\" index=0>";
|
||||
}
|
||||
|
||||
#region FSM
|
||||
@@ -58,9 +56,14 @@ namespace UI
|
||||
|
||||
#endregion
|
||||
|
||||
private void OnSelectProp(int index)
|
||||
public void OnSelectButtonClick(int index)
|
||||
{
|
||||
GameEntry.Event.Fire(this, LevelUpPropSelectedEventArgs.Create(index));
|
||||
}
|
||||
|
||||
public void OnRefreshButtonClick()
|
||||
{
|
||||
GameEntry.Event.Fire(this, RefreshEventArgs.Create(_context.RefreshPrice));
|
||||
}
|
||||
}
|
||||
}
|
||||
+7
-10
@@ -1,15 +1,13 @@
|
||||
using System;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.UI;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace UI
|
||||
{
|
||||
public class LevelUpPropItem : MonoBehaviour
|
||||
public class LevelUpRewardItem : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private Image _iconImage;
|
||||
[SerializeField] private IconArea _iconArea;
|
||||
|
||||
[SerializeField] private TMP_Text _titleText;
|
||||
|
||||
@@ -17,10 +15,10 @@ namespace UI
|
||||
|
||||
[SerializeField] private TMP_Text _descriptionText;
|
||||
|
||||
private LevelUpPropContext _context;
|
||||
private LevelUpRewardItemContext _context;
|
||||
|
||||
|
||||
public void Init(LevelUpPropContext context)
|
||||
public void Init(LevelUpRewardItemContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
@@ -31,20 +29,19 @@ namespace UI
|
||||
_context = context;
|
||||
|
||||
if (_titleText != null) _titleText.text = context.Title;
|
||||
if (_typeText != null) _typeText.text = context.Type;
|
||||
if (_descriptionText != null) _descriptionText.text = context.Description;
|
||||
if (_iconImage != null) _iconImage.sprite = context.Icon;
|
||||
if (_iconArea != null) _iconArea.SetIcon(context.Icon);
|
||||
|
||||
LoadIcon(_context.IconAssetName);
|
||||
}
|
||||
|
||||
private void LoadIcon(string iconAssetName)
|
||||
{
|
||||
if (_iconImage == null) return;
|
||||
if (_iconArea == null) return;
|
||||
|
||||
if (string.IsNullOrEmpty(iconAssetName)) return;
|
||||
|
||||
GameEntry.SpriteCache.GetSprite(iconAssetName, sprite => _iconImage.sprite = sprite);
|
||||
GameEntry.SpriteCache.GetSprite(iconAssetName, sprite => _iconArea.SetIcon(sprite));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
using CustomEvent;
|
||||
using GameFramework.Event;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace UI
|
||||
{
|
||||
@@ -18,7 +20,11 @@ namespace UI
|
||||
[SerializeField] private TMP_Text _playerCoinText;
|
||||
private int _currentCoin;
|
||||
|
||||
[SerializeField] private GoodsItem[] _goodsItems;
|
||||
[SerializeField] private ShopGoodsItem[] _goodsItems;
|
||||
|
||||
[SerializeField] private DisplayListArea _propListArea;
|
||||
|
||||
[SerializeField] private DisplayListArea _weaponListArea;
|
||||
|
||||
private ShopFormContext _context;
|
||||
|
||||
@@ -31,22 +37,89 @@ namespace UI
|
||||
|
||||
_titleText.text = $"商店 (Lv.{context.CurrentLevel})";
|
||||
_continueButtonText.text = $"继续 (Lv.{context.CurrentLevel + 1})";
|
||||
_refreshPriceText.text = $"-{context.RefreshPrice}";
|
||||
_playerCoinText.text = context.PlayerCoin.ToString();
|
||||
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 (_context.GoodsItems == null) return;
|
||||
for (int i = 0; i < _context.GoodsItems.Count; i++)
|
||||
if (goodsItems == null)
|
||||
{
|
||||
_goodsItems[i].Init(context.GoodsItems[i]);
|
||||
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);
|
||||
}
|
||||
|
||||
#region ButtonClick
|
||||
|
||||
public void OnContinueButtonClick()
|
||||
@@ -61,7 +134,7 @@ namespace UI
|
||||
|
||||
public void OnRefreshButtonClick()
|
||||
{
|
||||
GameEntry.Event.Fire(this, ShopRefreshEventArgs.Create(_context.RefreshPrice));
|
||||
GameEntry.Event.Fire(this, RefreshEventArgs.Create(_context.RefreshPrice));
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -80,7 +153,7 @@ namespace UI
|
||||
return;
|
||||
}
|
||||
|
||||
UnityGameFramework.Runtime.Log.Warning("ShopForm requires ShopFormContext as userData.");
|
||||
Log.Warning("ShopForm requires ShopFormContext as userData.");
|
||||
}
|
||||
|
||||
protected override void OnClose(bool isShutdown, object userData)
|
||||
@@ -89,6 +162,9 @@ namespace UI
|
||||
|
||||
GameEntry.Event.Unsubscribe(PlayerCoinChangeEventArgs.EventId, OnPlayerCoinChange);
|
||||
|
||||
_propListArea?.OnReset();
|
||||
_weaponListArea?.OnReset();
|
||||
|
||||
base.OnClose(isShutdown, userData);
|
||||
}
|
||||
|
||||
@@ -102,9 +178,9 @@ namespace UI
|
||||
if (args.CoinCount == _currentCoin) return;
|
||||
|
||||
_currentCoin = args.CoinCount;
|
||||
_playerCoinText.text = _currentCoin.ToString();
|
||||
_playerCoinText.text = $"<sprite name=\"coin\" index=0> {_currentCoin}";
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-6
@@ -4,9 +4,9 @@ using UnityEngine.UI;
|
||||
|
||||
namespace UI
|
||||
{
|
||||
public class GoodsItem : MonoBehaviour
|
||||
public class ShopGoodsItem : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private Image _iconImage;
|
||||
[SerializeField] private IconArea _iconArea;
|
||||
|
||||
[SerializeField] private TMP_Text _titleText;
|
||||
|
||||
@@ -15,18 +15,16 @@ namespace UI
|
||||
[SerializeField] private TMP_Text _descriptionText;
|
||||
|
||||
[SerializeField] private TMP_Text _costText;
|
||||
|
||||
[SerializeField] private CommonButton _purchaseButton;
|
||||
|
||||
#region Init
|
||||
|
||||
public void Init(GoodsItemContext data)
|
||||
{
|
||||
_iconImage.sprite = data.Icon;
|
||||
_iconArea.OnInit(data.Icon, data.Rarity);
|
||||
_titleText.text = data.Title;
|
||||
_typeText.text = data.Type;
|
||||
_descriptionText.text = data.Description;
|
||||
_costText.text = data.Price.ToString();
|
||||
_costText.text = $"{data.Price} <sprite name=\"coin\" index=0>";
|
||||
}
|
||||
|
||||
#endregion
|
||||
Reference in New Issue
Block a user