优化 UI 效果

This commit is contained in:
2026-10-03 18:30:31 +08:00
parent e0488cc790
commit 0077e3449d
89 changed files with 58194 additions and 23636 deletions
@@ -42,6 +42,8 @@ namespace SepCore.Entity
public IReadOnlyList<StatModifierConfig> Modifiers => Config.StatModifierConfigs;
public IReadOnlyList<WeaponTag> Tags => Config.Tags;
public float GetParamFloat(ParamType paramType)
{
if (Config.Parameters.TryGetValue(paramType, out float val))
@@ -9,33 +9,14 @@ namespace SepCore.UI
{
[SerializeField] private Image _background = null;
private Color _originalBackgroundColor;
[SerializeField] private UnityEvent _onPointerEnterAction = new();
[SerializeField] private UnityEvent _onPointerExitAction = new();
protected override void Awake()
{
base.Awake();
_originalBackgroundColor = _background.color;
}
protected override void OnEnable()
{
base.OnEnable();
_background.color = _originalBackgroundColor;
}
protected override void OnDisable()
{
base.OnDisable();
_background.color = new Color(0.3f, 0.3f, 0.3f, 0.3f);
}
public override void OnPointerEnter(PointerEventData eventData)
{
if (eventData.button != PointerEventData.InputButton.Left)
base.OnPointerEnter(eventData);
if (eventData.button != PointerEventData.InputButton.Left || !IsActive() || !IsInteractable())
{
return;
}
@@ -45,6 +26,7 @@ namespace SepCore.UI
public override void OnPointerExit(PointerEventData eventData)
{
base.OnPointerExit(eventData);
if (eventData.button != PointerEventData.InputButton.Left)
{
return;
@@ -55,16 +37,33 @@ namespace SepCore.UI
public override void OnSelect(BaseEventData eventData)
{
_background.color = new Color(1f, 0.9f, 0.3f);
base.OnSelect(eventData);
if (!IsActive() || !IsInteractable())
{
return;
}
EnterHoverState();
}
public override void OnDeselect(BaseEventData eventData)
{
_background.color = _originalBackgroundColor;
base.OnDeselect(eventData);
ExitHoverState();
}
protected override void DoStateTransition(SelectionState state, bool instant)
{
base.DoStateTransition(state, instant);
if (transition == Transition.SpriteSwap)
{
_background.canvasRenderer.SetColor(
state == SelectionState.Highlighted || state == SelectionState.Selected
? new Color(1f, 0.96f, 0.8f, 1f)
: Color.white);
}
}
private void EnterHoverState()
{
if (EventSystem.current.currentSelectedGameObject != gameObject)
@@ -10,6 +10,7 @@ namespace SepCore.UI
public string IconAssetName;
public string Title;
public ItemType ItemType;
public string TypeName;
public Rarity Rarity;
public string Description;
public int Price;
@@ -21,6 +22,7 @@ namespace SepCore.UI
Index = -1;
IconAssetName = string.Empty;
Title = string.Empty;
TypeName = string.Empty;
ItemType = ItemType.None;
Rarity = Rarity.White;
Description = string.Empty;
@@ -43,6 +45,7 @@ namespace SepCore.UI
var data = rawData.WeaponData;
IconAssetName = data.Sprite;
Title = data.Title;
TypeName = ItemDescUtility.FormatWeaponTags(data);
Description = ItemDescUtility.CreateWeaponDescription(data);
}
else if (ItemType == ItemType.Prop)
@@ -50,6 +53,7 @@ namespace SepCore.UI
var data = rawData.PropData;
IconAssetName = data.IconAssetName;
Title = data.Title;
TypeName = "道具";
Description = ItemDescUtility.CreatePropDescription(data);
}
}
@@ -36,7 +36,7 @@ namespace SepCore.UI
View.contentRectTransform.anchoredPosition3D = _targetPos;
View.titleText.text = _context.Title;
View.typeText.text = BuildTypeText(_context.ItemType);
View.typeText.text = _context.TypeName;
View.infoText.text = _context.Description;
View.textFormatText.Set(_context.Price);
@@ -173,16 +173,5 @@ namespace SepCore.UI
float z = View.contentRectTransform.anchoredPosition3D.z;
return new Vector3(localPoint.x, localPoint.y, z);
}
private string BuildTypeText(ItemType type)
{
return type switch
{
ItemType.None => "error",
ItemType.Weapon => "武器",
ItemType.Prop => "道具",
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
};
}
}
}
@@ -8,7 +8,8 @@
"GUID:47a82ffa13c291447ab895cd0bc251cd",
"GUID:436e23dbdc31e7d4fb5c3f804548b2df",
"GUID:d54b9488b03814a44ab937f0aeb738b1",
"GUID:3bb549212964b4192b427888ac9382e7"
"GUID:3bb549212964b4192b427888ac9382e7",
"GUID:75469ad4d38634e559750d17036d5f7c"
],
"includePlatforms": [],
"excludePlatforms": [],
@@ -19,4 +20,4 @@
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}
}
@@ -36,32 +36,23 @@ namespace SepCore.UI
if (ItemType == ItemType.Weapon)
{
DamageType damageType;
if (rawData.DataRow is WeaponConfig weaponConfig)
{
Name = weaponConfig.WeaponName;
Description = ItemDescUtility.CreateWeaponDescription(weaponConfig);
damageType = weaponConfig.DamageType;
TypeName = ItemDescUtility.FormatWeaponTags(weaponConfig);
}
else if (rawData.DataRow is WeaponData weaponData)
{
Name = weaponData.Title;
Description = ItemDescUtility.CreateWeaponDescription(weaponData);
damageType = weaponData.DamageType;
TypeName = ItemDescUtility.FormatWeaponTags(weaponData);
}
else
{
Log.Error("Weapon data row is invalid.");
return;
}
TypeName = damageType switch
{
DamageType.Melee => "近战武器",
DamageType.Ranged => "远程武器",
DamageType.Element => "元素武器",
_ => "武器"
};
}
else if (ItemType == ItemType.Prop)
{
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using SepCore.Event;
using UnityEngine;
using UnityGameFramework.Runtime;
namespace SepCore.UI
@@ -55,11 +56,19 @@ namespace SepCore.UI
View.statForm.OnInit(context.StatContext);
context.NeedRefreshStat = false;
}
bool weaponCapacityFull = context.WeaponListContext.CurrentCount >= context.WeaponListContext.MaxCount;
foreach (ShopGoodsItem item in _goodsItems)
{
item.RefreshAvailability(context.PlayerCoin, weaponCapacityFull);
}
View.refreshButton.interactable = context.PlayerCoin >= context.RefreshPrice;
}
protected override void OnOpen(object userData)
{
base.OnOpen(userData);
SetAttributesVisible(false);
if (userData is not ShopContext context)
{
@@ -72,6 +81,7 @@ namespace SepCore.UI
protected override void OnClose(bool isShutdown, object userData)
{
SetAttributesVisible(false);
_context = null;
for (int i = 0; i < _goodsItems.Count; i++)
@@ -96,6 +106,9 @@ namespace SepCore.UI
View.continueButton.onClick.AddListener(OnContinueButtonClick);
View.refreshButton.onClick.AddListener(OnRefreshButtonClick);
View.attributeToggleButton.onClick.AddListener(ShowAttributes);
View.attributeCloseButton.onClick.AddListener(HideAttributes);
View.attributeDismissButton.onClick.AddListener(HideAttributes);
}
private void RefreshGoodsItems(List<GoodsItemContext> goodsItems)
@@ -112,8 +125,16 @@ namespace SepCore.UI
}
else
{
item.OnReset();
item.gameObject.SetActive(false);
if (i < goodsItems.Count)
{
item.MarkPurchased();
item.gameObject.SetActive(true);
}
else
{
item.OnReset();
item.gameObject.SetActive(false);
}
}
}
}
@@ -136,5 +157,40 @@ namespace SepCore.UI
{
GameEntry.Event.Fire(this, ShopRefreshEventArgs.Create(_context.RefreshPrice));
}
private void ShowAttributes()
{
SetAttributesVisible(true);
}
private void HideAttributes()
{
SetAttributesVisible(false);
}
private void SetAttributesVisible(bool visible)
{
View.attributeDrawer.gameObject.SetActive(visible);
View.attributeToggleButton.gameObject.SetActive(!visible);
}
protected override void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
base.OnUpdate(elapseSeconds, realElapseSeconds);
if (!View.attributeDrawer.gameObject.activeSelf)
{
return;
}
#if ENABLE_INPUT_SYSTEM
var keyboard = UnityEngine.InputSystem.Keyboard.current;
if (keyboard != null && keyboard.escapeKey.wasPressedThisFrame)
#else
if (Input.GetKeyDown(KeyCode.Escape))
#endif
{
HideAttributes();
}
}
}
}
@@ -16,6 +16,11 @@ namespace SepCore.UI
[SerializeField] private TMP_Text _descriptionText;
[SerializeField] private TMP_Text _costText;
[SerializeField] private CommonButton _purchaseButton = null;
[SerializeField] private TMP_Text _purchaseLabel = null;
[SerializeField] private CanvasGroup _canvasGroup = null;
private GoodsItemContext _data;
private int _index = -1;
@@ -26,6 +31,9 @@ namespace SepCore.UI
EnsureListeners();
_index = index;
_data = data;
_canvasGroup.alpha = 1f;
_purchaseLabel.text = "购买";
_iconArea.OnInit(data.Icon, data.Rarity);
_titleText.text = data.Name;
@@ -37,6 +45,28 @@ namespace SepCore.UI
public void OnReset()
{
_index = -1;
_data = null;
}
public void MarkPurchased()
{
OnReset();
_canvasGroup.alpha = 0.5f;
_purchaseButton.interactable = false;
_purchaseLabel.text = "已购买";
}
public void RefreshAvailability(int playerCoin, bool weaponCapacityFull)
{
if (_index < 0)
{
return;
}
bool weaponFull = _data.ItemType == ItemType.Weapon && weaponCapacityFull;
bool affordable = playerCoin >= _data.Price;
_purchaseButton.interactable = affordable && !weaponFull;
_purchaseLabel.text = weaponFull ? "武器已满" : affordable ? "购买" : "金币不足";
}
private void EnsureListeners()
@@ -48,8 +78,7 @@ namespace SepCore.UI
_listenersBound = true;
CommonButton button = GetComponentInChildren<CommonButton>(true);
button.onClick.AddListener(OnPurchaseClick);
_purchaseButton.onClick.AddListener(OnPurchaseClick);
}
private void OnPurchaseClick()
@@ -8,12 +8,16 @@ namespace SepCore.UI
{
[SerializeField] public global::SepCore.UI.FormatTextUI titleFormatText;
[SerializeField] public global::SepCore.UI.FormatTextUI playerCoinFormatText;
[SerializeField] public global::SepCore.UI.CommonButton refreshButton;
[SerializeField] public global::SepCore.UI.FormatTextUI refreshButtonFormatText;
[SerializeField] public global::UnityEngine.RectTransform GoodsItemRoot;
[SerializeField] public global::SepCore.UI.ShopGoodsItem goodsItemTemplate;
[SerializeField] public global::SepCore.UI.CommonButton refreshButton;
[SerializeField] public global::SepCore.UI.FormatTextUI refreshButtonFormatText;
[SerializeField] public global::SepCore.UI.CommonButton continueButton;
[SerializeField] public global::SepCore.UI.FormatTextUI continueButtonFormatText;
[SerializeField] public global::SepCore.UI.CommonButton attributeToggleButton;
[SerializeField] public global::UnityEngine.RectTransform attributeDrawer;
[SerializeField] public global::UnityEngine.UI.Button attributeDismissButton;
[SerializeField] public global::SepCore.UI.CommonButton attributeCloseButton;
[SerializeField] public global::SepCore.UI.DisplayListForm weaponList;
[SerializeField] public global::SepCore.UI.DisplayListForm propList;
[SerializeField] public global::SepCore.UI.StatForm statForm;
@@ -28,7 +28,69 @@ namespace SepCore.CustomUtility
"收获", // Harvesting = 15
"幸运" // Luck = 16
};
private static readonly Dictionary<WeaponTag, string> _weaponTagNames = new()
{
{ WeaponTag.Blade, "刀剑" },
{ WeaponTag.Precise, "精准" },
{ WeaponTag.Gun, "枪械" },
{ WeaponTag.Engineering, "工程" },
{ WeaponTag.Blunt, "钝器" },
{ WeaponTag.Primitive, "原始" },
{ WeaponTag.Elemental, "元素" },
{ WeaponTag.Unarmed, "徒手" },
{ WeaponTag.Medical, "医疗" },
{ WeaponTag.Heavy, "重型" },
};
public static string GetWeaponTagName(WeaponTag tag)
{
return _weaponTagNames.TryGetValue(tag, out string name) ? name : tag.ToString();
}
public static string FormatWeaponTags(IReadOnlyList<WeaponTag> tags)
{
if (tags == null || tags.Count == 0)
{
return "武器";
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < tags.Count; i++)
{
WeaponTag tag = tags[i];
if (tag == WeaponTag.None)
{
continue;
}
if (sb.Length > 0)
{
sb.Append(" - ");
}
sb.Append(GetWeaponTagName(tag));
}
return sb.Length > 0 ? sb.ToString() : "武器";
}
public static string FormatWeaponTags(WeaponConfig config)
{
return FormatWeaponTags(config.Tags);
}
public static string FormatWeaponTags(WeaponData data)
{
if (data == null)
{
Log.Error("Weapon data is invalid.");
return "武器";
}
return FormatWeaponTags(data.Tags);
}
public static string Describe(StatModifierConfig statModifierConfig)
{
if (statModifierConfig == null)
@@ -31,9 +31,6 @@ namespace SepCore.Procedure
var useCase = new SelectRoleUseCase(this);
GameEntry.UIRouter.BindUIUseCase(UIFormType.SelectRoleForm, useCase);
QualitySettings.vSyncCount = 0;
Application.targetFrameRate = 120;
}
protected override void OnLeave(ProcedureOwner procedureOwner, bool isShutdown)