UI相关优化补充
This commit is contained in:
66
Assets/_Game/Scripts/UI/Utility/ColorblindApplier.cs
Normal file
66
Assets/_Game/Scripts/UI/Utility/ColorblindApplier.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
using UnityEngine;
|
||||
using BaseGames.Core;
|
||||
|
||||
namespace BaseGames.UI.Utility
|
||||
{
|
||||
/// <summary>
|
||||
/// 色盲滤镜接入点(架构 10_UIModule §可访问性)。
|
||||
/// 默认实现把 <see cref="ColorblindMode"/> 写入两个全局 Shader 参数:
|
||||
/// _GlobalColorblindMode (int) :0=None, 1=Protanopia, 2=Deuteranopia, 3=Tritanopia
|
||||
/// _GlobalColorblindStrength (float):固定 1.0,预留给后续过渡淡入
|
||||
///
|
||||
/// 项目接入方式:
|
||||
/// - URP:在 RendererFeature / PostProcess 中读取上述全局参数做 LUT 切换或 Daltonization 计算。
|
||||
/// - 内置管线:自定义 Image Effect 读取这两个参数。
|
||||
///
|
||||
/// 不依赖任何后处理包,挂在持久 GameObject 即可(DontDestroyOnLoad)。
|
||||
/// 业务方可实现 <see cref="IColorblindApplier"/> 并通过 ServiceLocator 替换默认行为。
|
||||
/// </summary>
|
||||
[DefaultExecutionOrder(-700)]
|
||||
public class ColorblindApplier : MonoBehaviour, IColorblindApplier
|
||||
{
|
||||
private static readonly int ModeId = Shader.PropertyToID("_GlobalColorblindMode");
|
||||
private static readonly int StrengthId = Shader.PropertyToID("_GlobalColorblindStrength");
|
||||
|
||||
[Tooltip("打开后会写入 Shader 全局变量,但不会强制后处理生效;后处理流程需自行读取。")]
|
||||
[SerializeField] private bool _writeShaderGlobals = true;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
ServiceLocator.RegisterIfAbsent<IColorblindApplier>(this);
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
SettingsManager.SettingsChanged += OnSettingsChanged;
|
||||
// 启动时若已加载,主动同步一次
|
||||
var svc = ServiceLocator.GetOrDefault<ISettingsService>();
|
||||
if (svc?.Current != null) ApplyMode(svc.Current.ColorblindMode);
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
SettingsManager.SettingsChanged -= OnSettingsChanged;
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
ServiceLocator.Unregister<IColorblindApplier>(this);
|
||||
}
|
||||
|
||||
private void OnSettingsChanged(GlobalSettingsData data) => ApplyMode(data.ColorblindMode);
|
||||
|
||||
public void ApplyMode(ColorblindMode mode)
|
||||
{
|
||||
if (!_writeShaderGlobals) return;
|
||||
Shader.SetGlobalInt(ModeId, (int)mode);
|
||||
Shader.SetGlobalFloat(StrengthId, mode == ColorblindMode.None ? 0f : 1f);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>色盲滤镜接入接口;由 ColorblindApplier 默认实现。</summary>
|
||||
public interface IColorblindApplier
|
||||
{
|
||||
void ApplyMode(ColorblindMode mode);
|
||||
}
|
||||
}
|
||||
11
Assets/_Game/Scripts/UI/Utility/ColorblindApplier.cs.meta
Normal file
11
Assets/_Game/Scripts/UI/Utility/ColorblindApplier.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7c8f1d9c11cc5cb43bdf3177b8028532
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
67
Assets/_Game/Scripts/UI/Utility/UIScaleApplier.cs
Normal file
67
Assets/_Game/Scripts/UI/Utility/UIScaleApplier.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using BaseGames.Core;
|
||||
|
||||
namespace BaseGames.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// 将 <see cref="ISettingsService"/>.<c>Current.UIScale</c> 应用到挂载的
|
||||
/// <see cref="CanvasScaler"/>。挂在每个根 Canvas 上即可。
|
||||
///
|
||||
/// 实现要点:
|
||||
/// · 记录 Inspector 初始的 <c>scaleFactor</c> / <c>referenceResolution</c> 作为基准值,
|
||||
/// 避免反复缩放导致的累积漂移;
|
||||
/// · 订阅 <see cref="SettingsManager.SettingsChanged"/> 在玩家调整后即时刷新;
|
||||
/// · 兼容 <see cref="CanvasScaler.ScaleMode.ScaleWithScreenSize"/> 与
|
||||
/// <see cref="CanvasScaler.ScaleMode.ConstantPixelSize"/> 两种模式。
|
||||
/// </summary>
|
||||
[DisallowMultipleComponent]
|
||||
[RequireComponent(typeof(CanvasScaler))]
|
||||
public class UIScaleApplier : MonoBehaviour
|
||||
{
|
||||
private CanvasScaler _scaler;
|
||||
private float _baseScaleFactor;
|
||||
private Vector2 _baseReferenceResolution;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_scaler = GetComponent<CanvasScaler>();
|
||||
_baseScaleFactor = _scaler.scaleFactor;
|
||||
_baseReferenceResolution = _scaler.referenceResolution;
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
SettingsManager.SettingsChanged += OnSettingsChanged;
|
||||
Apply();
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
SettingsManager.SettingsChanged -= OnSettingsChanged;
|
||||
}
|
||||
|
||||
private void OnSettingsChanged(GlobalSettingsData _) => Apply();
|
||||
|
||||
private void Apply()
|
||||
{
|
||||
var svc = ServiceLocator.GetOrDefault<ISettingsService>();
|
||||
float ui = svc?.Current?.UIScale ?? 1f;
|
||||
if (ui <= 0f) ui = 1f;
|
||||
|
||||
switch (_scaler.uiScaleMode)
|
||||
{
|
||||
case CanvasScaler.ScaleMode.ConstantPixelSize:
|
||||
_scaler.scaleFactor = _baseScaleFactor * ui;
|
||||
break;
|
||||
case CanvasScaler.ScaleMode.ScaleWithScreenSize:
|
||||
// 缩小参考分辨率 → 等价于放大 UI。
|
||||
_scaler.referenceResolution = _baseReferenceResolution / ui;
|
||||
break;
|
||||
default:
|
||||
_scaler.scaleFactor = _baseScaleFactor * ui;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/_Game/Scripts/UI/Utility/UIScaleApplier.cs.meta
Normal file
11
Assets/_Game/Scripts/UI/Utility/UIScaleApplier.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9eae5e71f8a9b5b4582bfa1f0b41ac52
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
131
Assets/_Game/Scripts/UI/Utility/UITween.cs
Normal file
131
Assets/_Game/Scripts/UI/Utility/UITween.cs
Normal file
@@ -0,0 +1,131 @@
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace BaseGames.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// UI 协程补间静态库。
|
||||
///
|
||||
/// 设计动机:消除 <see cref="BossHPBar"/>、<see cref="ToastNotification"/>、
|
||||
/// <see cref="FloatingDamageText"/> 等组件中重复出现的 Lerp + WaitForEndOfFrame 样板,
|
||||
/// 集中维护时间步与回调约定。所有协程默认使用 <see cref="Time.unscaledDeltaTime"/>,
|
||||
/// 以便 UI 动画在游戏暂停(<see cref="Time.timeScale"/> = 0)时仍能播放。
|
||||
///
|
||||
/// 性能特征:
|
||||
/// · 无堆分配(除协程对象本身);
|
||||
/// · 提前返回保护无效目标;
|
||||
/// · <paramref name="duration"/> <= 0 时立即吸附到终态并退出(一帧 yield 用于保持
|
||||
/// 与正常协程一致的栈语义)。
|
||||
/// </summary>
|
||||
public static class UITween
|
||||
{
|
||||
// ── 位置 ─────────────────────────────────────────────────────────────
|
||||
|
||||
/// <summary>将 RectTransform 的 anchoredPosition 平滑过渡到目标值。</summary>
|
||||
public static IEnumerator MoveAnchored(RectTransform rect,
|
||||
Vector2 target,
|
||||
float duration,
|
||||
bool unscaled = true)
|
||||
{
|
||||
if (rect == null) yield break;
|
||||
if (duration <= 0f)
|
||||
{
|
||||
rect.anchoredPosition = target;
|
||||
yield break;
|
||||
}
|
||||
|
||||
Vector2 start = rect.anchoredPosition;
|
||||
float t = 0f;
|
||||
while (t < duration)
|
||||
{
|
||||
rect.anchoredPosition = Vector2.Lerp(start, target, t / duration);
|
||||
t += unscaled ? Time.unscaledDeltaTime : Time.deltaTime;
|
||||
yield return null;
|
||||
}
|
||||
rect.anchoredPosition = target;
|
||||
}
|
||||
|
||||
// ── 透明度 ───────────────────────────────────────────────────────────
|
||||
|
||||
/// <summary>将 CanvasGroup.alpha 平滑过渡到目标值。</summary>
|
||||
public static IEnumerator FadeCanvasGroup(CanvasGroup cg,
|
||||
float target,
|
||||
float duration,
|
||||
bool unscaled = true)
|
||||
{
|
||||
if (cg == null) yield break;
|
||||
if (duration <= 0f)
|
||||
{
|
||||
cg.alpha = target;
|
||||
yield break;
|
||||
}
|
||||
|
||||
float start = cg.alpha;
|
||||
float t = 0f;
|
||||
while (t < duration)
|
||||
{
|
||||
cg.alpha = Mathf.Lerp(start, target, t / duration);
|
||||
t += unscaled ? Time.unscaledDeltaTime : Time.deltaTime;
|
||||
yield return null;
|
||||
}
|
||||
cg.alpha = target;
|
||||
}
|
||||
|
||||
/// <summary>将 Graphic(Image / TMP_Text 等)的颜色 Alpha 通道平滑过渡到目标值。</summary>
|
||||
public static IEnumerator FadeGraphic(Graphic graphic,
|
||||
float targetAlpha,
|
||||
float duration,
|
||||
bool unscaled = true)
|
||||
{
|
||||
if (graphic == null) yield break;
|
||||
if (duration <= 0f)
|
||||
{
|
||||
var c = graphic.color;
|
||||
c.a = targetAlpha;
|
||||
graphic.color = c;
|
||||
yield break;
|
||||
}
|
||||
|
||||
float startAlpha = graphic.color.a;
|
||||
float t = 0f;
|
||||
while (t < duration)
|
||||
{
|
||||
var c = graphic.color;
|
||||
c.a = Mathf.Lerp(startAlpha, targetAlpha, t / duration);
|
||||
graphic.color = c;
|
||||
t += unscaled ? Time.unscaledDeltaTime : Time.deltaTime;
|
||||
yield return null;
|
||||
}
|
||||
var fc = graphic.color;
|
||||
fc.a = targetAlpha;
|
||||
graphic.color = fc;
|
||||
}
|
||||
|
||||
// ── 缩放 ─────────────────────────────────────────────────────────────
|
||||
|
||||
/// <summary>将 Transform 的 localScale 等比平滑过渡到目标值。</summary>
|
||||
public static IEnumerator Scale(Transform tr,
|
||||
Vector3 target,
|
||||
float duration,
|
||||
bool unscaled = true)
|
||||
{
|
||||
if (tr == null) yield break;
|
||||
if (duration <= 0f)
|
||||
{
|
||||
tr.localScale = target;
|
||||
yield break;
|
||||
}
|
||||
|
||||
Vector3 start = tr.localScale;
|
||||
float t = 0f;
|
||||
while (t < duration)
|
||||
{
|
||||
tr.localScale = Vector3.Lerp(start, target, t / duration);
|
||||
t += unscaled ? Time.unscaledDeltaTime : Time.deltaTime;
|
||||
yield return null;
|
||||
}
|
||||
tr.localScale = target;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/_Game/Scripts/UI/Utility/UITween.cs.meta
Normal file
11
Assets/_Game/Scripts/UI/Utility/UITween.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 96fd9d373d6d2784c8d04d8e56d0bb3e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user