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

- 结构上迁移文件,将脚本分成 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,8 @@
fileFormatVersion: 2
guid: 32bf4a12bd0ac2847b19bbb76b7c3dd5
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,11 @@
using SepCore.Definition;
namespace UI
{
public class DisplayItemContext : UIContext
{
public string IconAssetName;
public ItemRarity Rarity;
public bool 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,14 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace 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,15 @@
using SepCore.Definition;
using UnityEngine;
namespace UI
{
public class GoodsItemContext : UIContext
{
public string Title;
public ItemRarity Rarity;
public string Type;
public Sprite Icon;
public string Description;
public int Price;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c9d0b321b02be5f41a27cc0a8bf84e34
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,14 @@
using SepCore.Definition;
using UnityEngine;
namespace UI
{
public class LevelUpRewardItemContext : UIContext
{
public string Title;
public Sprite Icon;
public ItemRarity ItemRarity;
public string Description;
public string IconAssetName;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 424ceeba7cae4fa4faaeef9035f31e31
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6fba1ddc3747c38488ae739d08785dd1
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,205 @@
using CustomEvent;
using TMPro;
using UnityEngine;
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);
Log.Info($"{_context.TargetPos}->{_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>();
Canvas rootCanvas = canvas != null ? canvas.rootCanvas : null;
Camera uiCamera = null;
if (rootCanvas != null && rootCanvas.renderMode != RenderMode.ScreenSpaceOverlay)
{
uiCamera = rootCanvas.worldCamera != null ? rootCanvas.worldCamera : Camera.main;
}
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);
}
public void OnRecycleButtonClick()
{
if (_context == null || !_context.IsWeapon) return;
GameEntry.Event.Fire(this, ShopWeaponRecycleEventArgs.Create(_context.Index, _context.Price));
}
public void OnCancelButtonClick()
{
GameEntry.Event.Fire(this, DisplayItemInfoHideEventArgs.Create(true));
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 32469245f20597240a3c51d2cd1fb0c5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,18 @@
using SepCore.Definition;
using UnityEngine;
namespace UI
{
public class DisplayItemInfoFormContext : UIContext
{
public int Index;
public string IconAssetName;
public string Title;
public string TypeText;
public ItemRarity Rarity;
public string Description;
public int Price;
public bool IsWeapon;
public Vector3 TargetPos;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cdcef9502b7fa944ca95cb839fdc60bc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,158 @@
using CustomEvent;
using SepCore.Definition;
using GameFramework.Event;
using UnityEngine;
using UnityGameFramework.Runtime;
namespace UI
{
public class DisplayItemInfoFormController : UIFormControllerCommonBase<
DisplayItemInfoFormContext, DisplayItemInfoForm>
{
protected override UIFormType UIFormTypeId => UIFormType.DisplayItemInfoForm;
private bool _locked = false;
protected override void SubscribeCustomEvents()
{
GameEntry.Event.Subscribe(DisplayItemInfoLockEventArgs.EventId, DisplayItemInfoLock);
GameEntry.Event.Subscribe(DisplayItemInfoHideEventArgs.EventId, DisplayItemInfoHide);
}
protected override void UnsubscribeCustomEvents()
{
GameEntry.Event.Unsubscribe(DisplayItemInfoLockEventArgs.EventId, DisplayItemInfoLock);
GameEntry.Event.Unsubscribe(DisplayItemInfoHideEventArgs.EventId, DisplayItemInfoHide);
}
protected override void RefreshUI(DisplayItemInfoForm form, DisplayItemInfoFormContext context)
{
form.RefreshUI(context);
}
protected override void CloseLoadedFormDirect(DisplayItemInfoForm form)
{
GameEntry.UI.CloseUIForm(form);
}
private static DisplayItemInfoFormContext BuildContext(DisplayItemInfoFormRawData rawData)
{
if (rawData == null)
{
Log.Error("DisplayItemInfoFormController.BuildContext() rawData is null.");
return null;
}
return new DisplayItemInfoFormContext
{
Index = rawData.Index,
IconAssetName = rawData.IconAssetName,
Title = rawData.Title,
Rarity = rawData.Rarity,
TypeText = rawData.TypeText,
Description = rawData.Description,
Price = rawData.Price,
IsWeapon = rawData.IsWeapon,
TargetPos = rawData.TargetPos
};
}
public int? OpenUI(DisplayItemInfoFormRawData rawData)
{
_locked = false;
DisplayItemInfoFormContext context = BuildContext(rawData);
return OpenUIInternal(context);
}
public override int? OpenUI(object userData = null)
{
_locked = false;
if (userData is DisplayItemInfoFormContext context)
{
return OpenUIInternal(context);
}
if (userData is DisplayItemInfoFormRawData rawData)
{
return OpenUI(rawData);
}
if (userData != null)
{
Log.Warning("DisplayItemInfoFormController.OpenUI() userData type is invalid.");
return null;
}
return OpenUIInternal(Context);
}
public override void BindUseCase(IUIUseCase useCase)
{
if (!(useCase is DisplayItemInfoFormUseCase))
{
Log.Error("DisplayItemInfoForm.BindUseCase() useCase is invalid.");
}
}
private bool IsCurrentFormSender(object sender)
{
if (sender is DisplayItemInfoForm displayItemInfoForm)
{
return displayItemInfoForm == Form;
}
if (sender is Component component && Form != null)
{
return component.transform.IsChildOf(Form.transform);
}
return false;
}
#region Event Handlers
private void DisplayItemInfoLock(object sender, GameEventArgs e)
{
if (!(e is DisplayItemInfoLockEventArgs))
{
return;
}
if (Context == null)
{
Log.Error("DisplayItemInfoFormController.DisplayItemInfoLock() Context is null.");
return;
}
if (Form == null)
{
Log.Error("DisplayItemInfoFormController.DisplayItemInfoLock() Form is null.");
return;
}
_locked = true;
}
private void DisplayItemInfoHide(object sender, GameEventArgs e)
{
if (!(e is DisplayItemInfoHideEventArgs args))
{
return;
}
if (args.Force)
{
GameEntry.UIRouter.CloseUI(UIFormType.DisplayItemInfoForm);
_locked = false;
return;
}
if (_locked && !args.Force) return;
GameEntry.UIRouter.CloseUI(UIFormType.DisplayItemInfoForm);
_locked = false;
}
#endregion
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8a9b5acbc476ae44a8259a5aea1c0cf2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5d21ab27094da2548a00119313150780
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,142 @@
using CustomComponent;
using CustomEvent;
using GameFramework.Event;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
namespace UI
{
public class HudForm : UGuiForm
{
[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;
[SerializeField] private TMP_Text _levelTimeLeftText;
private int _currentTimeLeft = 0;
[SerializeField] private TMP_Text _enemyCountText;
private EnemyManagerComponent _enemy;
public void RefreshUI(HudFormContext hudFormContext)
{
}
#region FSM
protected override void OnInit(object userData)
{
base.OnInit(userData);
GameEntry.Event.Subscribe(PlayerHealthChangeEventArgs.EventId, PlayerHpChange);
GameEntry.Event.Subscribe(PlayerExpChangeEventArgs.EventId, PlayerExpChange);
GameEntry.Event.Subscribe(PlayerCoinChangeEventArgs.EventId, PlayerCoinChange);
GameEntry.Event.Subscribe(PlayerLevelUpEventArgs.EventId, PlayerLevelUp);
GameEntry.Event.Subscribe(LevelProcessEventArgs.EventId, LevelProcess);
_enemy = GameEntry.EnemyManager;
}
protected override void OnClose(bool isShutdown, object userData)
{
GameEntry.Event.Unsubscribe(PlayerLevelUpEventArgs.EventId, PlayerLevelUp);
GameEntry.Event.Unsubscribe(PlayerHealthChangeEventArgs.EventId, PlayerHpChange);
GameEntry.Event.Unsubscribe(PlayerExpChangeEventArgs.EventId, PlayerExpChange);
GameEntry.Event.Unsubscribe(PlayerCoinChangeEventArgs.EventId, PlayerCoinChange);
GameEntry.Event.Unsubscribe(PlayerLevelUpEventArgs.EventId, PlayerLevelUp);
base.OnClose(isShutdown, userData);
}
protected override void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
base.OnUpdate(elapseSeconds, realElapseSeconds);
if (_enemy == null)
{
_enemy = GameEntry.EnemyManager;
}
if (_enemy != null)
{
_enemyCountText.text = $"Enemy: {_enemy.CurrentEnemyCount}";
}
}
#endregion
#region Event Handlers
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;
_currentHpPercent = percent;
if (_hpSliderFadeCoroutine != null) StopCoroutine(_hpSliderFadeCoroutine);
_hpSliderFadeCoroutine = StartCoroutine(_hpSlider.SmoothValue(percent, _sliderFadeDuration));
}
private void PlayerExpChange(object sender, GameEventArgs e)
{
if (!(e is PlayerExpChangeEventArgs args)) return;
float percent = (float)args.CurrentExp / args.MaxExp;
if (Mathf.Approximately(_currentExpPercent, percent)) return;
_currentExpPercent = percent;
if (_expSliderFadeCoroutine != null) StopCoroutine(_expSliderFadeCoroutine);
_expSliderFadeCoroutine = StartCoroutine(_expSlider.SmoothValue(percent, _sliderFadeDuration));
}
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 = $"<sprite name=\"coin\" index=0> {args.CoinCount}";
_currentCoin = args.CoinCount;
}
private void LevelProcess(object sender, GameEventArgs e)
{
if (!(e is LevelProcessEventArgs args)) return;
if (_currentTimeLeft == args.LevelTimeLeft) return;
_currentTimeLeft = args.LevelTimeLeft;
_levelTimeLeftText.text = _currentTimeLeft.ToString();
}
#endregion
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bd4c4d6e8d4b0f54c830e2cfef46848e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,10 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace UI
{
public class HudFormContext : UIContext
{
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3f8d476720436b84483bd1c29da4d558
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,46 @@
using SepCore.Definition;
using UnityGameFramework.Runtime;
namespace UI
{
public class HudFormController : UIFormControllerCommonBase<HudFormContext, HudForm>
{
protected override UIFormType UIFormTypeId => UIFormType.HudForm;
protected override void RefreshUI(HudForm form, HudFormContext context)
{
form.RefreshUI(context);
}
private static HudFormContext BuildHudFormContext()
{
return new HudFormContext();
}
public override int? OpenUI(object userData = null)
{
if (userData is HudFormContext context)
{
return OpenUIInternal(context);
}
if (userData != null)
{
Log.Warning("HudFormController.OpenUI() userData type is invalid.");
return null;
}
return OpenUIInternal(BuildHudFormContext());
}
public int? OpenUI(HudFormContext context)
{
return OpenUIInternal(context);
}
public override void BindUseCase(IUIUseCase useCase)
{
Log.Info("HudFormController doesn't need UseCase");
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b7360b01093fa984d86105e2dd024dd5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c65843511cb3d6e43af724cbde56bb83
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,70 @@
using CustomEvent;
using TMPro;
using UnityEngine;
using UnityGameFramework.Runtime;
namespace UI
{
public class LevelUpForm : UGuiForm
{
[SerializeField] private LevelUpRewardItem[] _propItems;
[SerializeField] private TMP_Text _refreshButtonText;
private LevelUpFormContext _context;
public void RefreshUI(LevelUpFormContext context)
{
_context = context;
foreach (var propItem in _propItems)
{
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++)
{
_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
protected override void OnOpen(object userData)
{
base.OnOpen(userData);
if (userData is LevelUpFormContext context)
{
RefreshUI(context);
return;
}
Log.Warning("LevelUpForm requires LevelUpFormContext as userData.");
}
protected override void OnClose(bool isShutdown, object userData)
{
_context = null;
base.OnClose(isShutdown, userData);
}
#endregion
public void OnSelectButtonClick(int index)
{
GameEntry.Event.Fire(this, LevelUpPropSelectedEventArgs.Create(index));
}
public void OnRefreshButtonClick()
{
GameEntry.Event.Fire(this, RefreshEventArgs.Create(_context.RefreshPrice));
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 73f52c8a6d8b8ec42ad8cd3c00e0254d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,10 @@
using System.Collections.Generic;
namespace UI
{
public class LevelUpFormContext : UIContext
{
public List<LevelUpRewardItemContext> Props;
public int RefreshPrice;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1874c6c7ef990c04ba726a2581cd23ce
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,181 @@
using System.Collections.Generic;
using CustomEvent;
using SepCore.Definition;
using CustomUtility;
using GameFramework.Event;
using UnityGameFramework.Runtime;
namespace UI
{
public class LevelUpFormController : UIFormControllerCommonBase<LevelUpFormContext, LevelUpForm>
{
private LevelUpFormUseCase _useCase;
protected override UIFormType UIFormTypeId => UIFormType.LevelUpForm;
protected override void RefreshUI(LevelUpForm form, LevelUpFormContext context)
{
form.RefreshUI(context);
}
protected override void SubscribeCustomEvents()
{
GameEntry.Event.Subscribe(RefreshEventArgs.EventId, OnRefresh);
GameEntry.Event.Subscribe(LevelUpPropSelectedEventArgs.EventId, OnLevelUpPropSelected);
}
protected override void UnsubscribeCustomEvents()
{
GameEntry.Event.Unsubscribe(RefreshEventArgs.EventId, OnRefresh);
GameEntry.Event.Unsubscribe(LevelUpPropSelectedEventArgs.EventId, OnLevelUpPropSelected);
}
private static LevelUpFormContext BuildContext(LevelUpFormRawData rawData)
{
if (rawData == null)
{
Log.Error("LevelUpFormController.BuildContext() rawData is null.");
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,
Icon = null,
ItemRarity = reward.Rarity,
Description = ItemDescUtility.CreatePropDescription(reward.Modifiers),
IconAssetName = reward.IconAssetName
});
}
return new LevelUpFormContext
{
RefreshPrice = rawData.RefreshPrice,
Props = props
};
}
public override int? OpenUI(object userData = null)
{
if (userData is LevelUpFormContext context)
{
return OpenUIInternal(context);
}
if (userData is LevelUpFormRawData rawDataFromUserData)
{
return OpenUI(rawDataFromUserData);
}
if (userData != null)
{
Log.Warning("LevelUpFormController.OpenUI() userData type is invalid.");
return null;
}
if (_useCase == null)
{
Log.Error("LevelUpFormController.OpenUI() useCase is null.");
return null;
}
LevelUpFormRawData rawData = _useCase.CreateInitialModel();
return OpenUI(rawData);
}
public int? OpenUI(LevelUpFormRawData rawData)
{
LevelUpFormContext context = BuildContext(rawData);
return OpenUIInternal(context);
}
public override void BindUseCase(IUIUseCase useCase)
{
if (!(useCase is LevelUpFormUseCase levelUpFormUseCase))
{
Log.Error("LevelUpForm.BindUseCase() useCase is invalid.");
return;
}
_useCase = levelUpFormUseCase;
}
private void SelectReward(int selectedIndex)
{
if (_useCase == null)
{
Log.Error("LevelUpFormController.OpenUI() useCase is null.");
return;
}
LevelUpFormRawData rawData = _useCase.SelectReward(selectedIndex);
if (rawData == null)
{
return;
}
OpenUI(rawData);
}
private void RefreshRewardList(int refreshCost)
{
if (_useCase == null)
{
Log.Error("LevelUpFormController.OpenUI() useCase is null.");
return;
}
LevelUpFormRawData rawData = _useCase.TryRefresh(refreshCost);
if (rawData == null)
{
return;
}
OpenUI(rawData);
}
private void OnRefresh(object sender, GameEventArgs e)
{
if ((LevelUpForm)sender != Form)
{
return;
}
if (!(e is RefreshEventArgs args))
{
return;
}
RefreshRewardList(args.Cost);
}
private void OnLevelUpPropSelected(object sender, GameEventArgs e)
{
if ((LevelUpForm)sender != Form)
{
return;
}
if (!(e is LevelUpPropSelectedEventArgs args))
{
return;
}
SelectReward(args.SelectedId);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 89d85bbc4f7cf8c438c7ee693eaf67cf
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5d6a681c3d133314c869b07c7d94856e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -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:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7af0b9398035e254cb6de37a6c301552
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,71 @@
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 SetIndex(int index)
{
_index = index;
}
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;
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, DisplayItemShowEventArgs.Create(_index, _context.IsWeapon, targetPos));
}
public void OnItemInfoLock()
{
GameEntry.Event.Fire(this, DisplayItemInfoLockEventArgs.Create());
}
public void OnItemInfoHide()
{
GameEntry.Event.Fire(this, DisplayItemInfoHideEventArgs.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,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,285 @@
using System;
using System.Collections.Generic;
using GameFramework.ObjectPool;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using UnityGameFramework.Runtime;
namespace 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);
}
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:
@@ -0,0 +1,47 @@
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using UnityGameFramework.Runtime;
namespace UI
{
public class LevelUpRewardItem : MonoBehaviour
{
[SerializeField] private IconArea _iconArea;
[SerializeField] private TMP_Text _titleText;
[SerializeField] private TMP_Text _typeText;
[SerializeField] private TMP_Text _descriptionText;
private LevelUpRewardItemContext _context;
public void Init(LevelUpRewardItemContext context)
{
if (context == null)
{
Log.Error("LevelUpPropContext context is invalid.");
return;
}
_context = context;
if (_titleText != null) _titleText.text = context.Title;
if (_descriptionText != null) _descriptionText.text = context.Description;
if (_iconArea != null) _iconArea.OnInit(context.Icon, context.ItemRarity);
LoadIcon(_context.IconAssetName);
}
private void LoadIcon(string iconAssetName)
{
if (_iconArea == null) return;
if (string.IsNullOrEmpty(iconAssetName)) return;
GameEntry.SpriteCache.GetSprite(iconAssetName, sprite => _iconArea.SetIcon(sprite));
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 237d64392d274c14f9f5c81b54122a93
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,32 @@
using TMPro;
using UnityEngine;
using UnityEngine.UI;
namespace 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.Type;
_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: