多轮审查和修复

This commit is contained in:
2026-05-12 15:34:08 +08:00
parent f55d2a57c3
commit ebbbb7332e
805 changed files with 838724 additions and 1905 deletions

View File

@@ -0,0 +1,89 @@
using UnityEngine;
using BaseGames.Core.Events;
namespace BaseGames.Support.Accessibility
{
/// <summary>
/// 无障碍功能管理器(架构 16_SupportingModules §6.1)。
/// 响应设置变更,广播色盲模式事件;提供 CanPlayScreenShake() 供 FeedbackSystem 查询。
/// </summary>
public class AccessibilityManager : MonoBehaviour
{
[Header("设置资产")]
[SerializeField] private AccessibilitySettingsSO _settings;
[Header("事件频道(输出)")]
[SerializeField] private ColorblindModeEventChannelSO _onColorblindModeChanged;
[SerializeField] private BoolEventChannelSO _onScreenShakeChanged;
private static AccessibilityManager _instance;
// ── 静态查询接口(供 FeedbackSystem 使用) ───────────────────────────────
public static bool CanPlayScreenShake()
=> _instance == null || (_instance._settings != null && _instance._settings.ScreenShake);
private void Awake()
{
if (_instance != null && _instance != this)
{
Debug.LogWarning("[AccessibilityManager] 已存在实例,请确保本组件仅放置在 Persistent 场景中。", this);
Destroy(this);
return;
}
_instance = this;
if (_settings != null)
_settings.Load();
}
private void Start()
{
// 初始化时广播当前色盲模式
if (_settings != null)
_onColorblindModeChanged?.Raise(_settings.ColorblindMode);
}
private void OnDestroy()
{
if (_instance == this) _instance = null;
}
/// <summary>应用新的设置并持久化。</summary>
public void Apply(AccessibilitySettingsSO newSettings)
{
_settings.ScreenShake = newSettings.ScreenShake;
_settings.ReducedFlash = newSettings.ReducedFlash;
_settings.LargeText = newSettings.LargeText;
_settings.HighContrast = newSettings.HighContrast;
_settings.UIScale = newSettings.UIScale;
bool colorblindChanged = _settings.ColorblindMode != newSettings.ColorblindMode;
_settings.ColorblindMode = newSettings.ColorblindMode;
_settings.Save();
_onScreenShakeChanged?.Raise(_settings.ScreenShake);
if (colorblindChanged)
_onColorblindModeChanged?.Raise(_settings.ColorblindMode);
}
/// <summary>直接设置色盲模式。</summary>
public void SetColorblindMode(ColorblindMode mode)
{
if (_settings == null) return;
_settings.ColorblindMode = mode;
_settings.Save();
_onColorblindModeChanged?.Raise(mode);
}
/// <summary>直接设置屏幕抖动开关。</summary>
public void SetScreenShake(bool enabled)
{
if (_settings == null) return;
_settings.ScreenShake = enabled;
_settings.Save();
_onScreenShakeChanged?.Raise(enabled);
}
public AccessibilitySettingsSO Settings => _settings;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: db1f332f741502f4385b7d639582a8cf
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,46 @@
using UnityEngine;
using BaseGames.Core.Events;
/// <summary>
/// 无障碍设置数据 SO架构 16_SupportingModules §6
/// 包含屏幕抖动、闪光减弱、色盲模式等辅助功能开关。
/// </summary>
[CreateAssetMenu(menuName = "Settings/AccessibilitySettings", fileName = "SET_Accessibility")]
public class AccessibilitySettingsSO : ScriptableObject
{
private const string PrefsPrefix = "accessibility_";
[Header("屏幕体感")]
public bool ScreenShake = true;
public bool ReducedFlash = false;
[Header("色盲模式")]
public ColorblindMode ColorblindMode = ColorblindMode.None;
[Header("文字与 UI")]
public bool LargeText = false;
public bool HighContrast = false;
public float UIScale = 1f;
// ── 持久化 ──────────────────────────────────────────────────────────────
public void Save()
{
PlayerPrefs.SetInt (PrefsPrefix + "ScreenShake", ScreenShake ? 1 : 0);
PlayerPrefs.SetInt (PrefsPrefix + "ReducedFlash", ReducedFlash ? 1 : 0);
PlayerPrefs.SetInt (PrefsPrefix + "ColorblindMode",(int)ColorblindMode);
PlayerPrefs.SetInt (PrefsPrefix + "LargeText", LargeText ? 1 : 0);
PlayerPrefs.SetInt (PrefsPrefix + "HighContrast", HighContrast ? 1 : 0);
PlayerPrefs.SetFloat (PrefsPrefix + "UIScale", UIScale);
PlayerPrefs.Save();
}
public void Load()
{
ScreenShake = PlayerPrefs.GetInt (PrefsPrefix + "ScreenShake", 1) == 1;
ReducedFlash = PlayerPrefs.GetInt (PrefsPrefix + "ReducedFlash", 0) == 1;
ColorblindMode = (ColorblindMode)PlayerPrefs.GetInt(PrefsPrefix + "ColorblindMode", 0);
LargeText = PlayerPrefs.GetInt (PrefsPrefix + "LargeText", 0) == 1;
HighContrast = PlayerPrefs.GetInt (PrefsPrefix + "HighContrast", 0) == 1;
UIScale = PlayerPrefs.GetFloat(PrefsPrefix + "UIScale", 1f);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a1cbca9ad14985d469becb19e074f8a5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,137 @@
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
using BaseGames.Core.Events;
/// <summary>
/// 色盲滤镜 URP Renderer Feature架构 16_SupportingModules §6.2)。
/// 通过颜色矩阵将画面转换到对应色盲友好的色彩空间。
/// 配置:在 URP Renderer Asset 的 Renderer Features 列表中添加此 Feature。
/// 订阅 EVT_ColorBlindModeChanged 事件切换模式。
/// </summary>
public class ColorBlindFilter : ScriptableRendererFeature
{
[System.Serializable]
public class Settings
{
public ColorblindMode Mode = ColorblindMode.None;
public float Strength = 1f;
public RenderPassEvent PassEvent = RenderPassEvent.AfterRenderingPostProcessing;
}
[SerializeField] public Settings settings = new();
[Header("事件频道")]
[SerializeField] private ColorblindModeEventChannelSO _onColorblindModeChanged;
private ColorBlindRenderPass _pass;
private readonly CompositeDisposable _subs = new();
public override void Create()
{
_pass = new ColorBlindRenderPass(settings);
_pass.renderPassEvent = settings.PassEvent;
}
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{
if (settings.Mode == ColorblindMode.None) return;
renderer.EnqueuePass(_pass);
}
private void OnEnable() => _onColorblindModeChanged?.Subscribe(SetMode).AddTo(_subs);
private void OnDisable() => _subs.Clear();
/// <summary>切换色盲模式(由 AccessibilityManager 通过事件调用)。</summary>
public void SetMode(ColorblindMode mode)
{
settings.Mode = mode;
_pass?.UpdateSettings(settings);
}
// ── 内部 RenderPass ───────────────────────────────────────────────────────
private class ColorBlindRenderPass : ScriptableRenderPass
{
private Settings _settings;
private Material _material;
private static readonly int ColorMatrixId = Shader.PropertyToID("_ColorMatrix");
public ColorBlindRenderPass(Settings settings)
{
_settings = settings;
_material = CoreUtils.CreateEngineMaterial(Shader.Find("Hidden/ColorBlindFilter"));
}
public void UpdateSettings(Settings s)
{
_settings = s;
}
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
if (_material == null || _settings.Mode == ColorblindMode.None) return;
var matrix = GetColorMatrix(_settings.Mode, _settings.Strength);
_material.SetMatrix(ColorMatrixId, matrix);
CommandBuffer cmd = CommandBufferPool.Get("ColorBlindFilter");
// Blit with material applies the color matrix shader
Blit(cmd, ref renderingData, _material);
context.ExecuteCommandBuffer(cmd);
CommandBufferPool.Release(cmd);
}
/// <summary>返回指定色盲类型的颜色变换矩阵。</summary>
private static Matrix4x4 GetColorMatrix(ColorblindMode mode, float strength)
{
// 基于 Brettel/Viénot 等人的色盲模拟矩阵
Matrix4x4 identity = Matrix4x4.identity;
Matrix4x4 sim = mode switch
{
ColorblindMode.Protanopia => Protanopia,
ColorblindMode.Deuteranopia => Deuteranopia,
ColorblindMode.Tritanopia => Tritanopia,
ColorblindMode.Achromatopsia=> Achromatopsia,
_ => identity,
};
// 按强度插值
if (Mathf.Approximately(strength, 1f)) return sim;
return Lerp(identity, sim, strength);
}
private static Matrix4x4 Lerp(Matrix4x4 a, Matrix4x4 b, float t)
{
var result = new Matrix4x4();
for (int i = 0; i < 16; i++)
result[i] = Mathf.Lerp(a[i], b[i], t);
return result;
}
// ── 预定义色盲矩阵 ───────────────────────────────────────────────────
private static readonly Matrix4x4 Protanopia = new(
new Vector4(0.567f, 0.433f, 0.000f, 0),
new Vector4(0.558f, 0.442f, 0.000f, 0),
new Vector4(0.000f, 0.242f, 0.758f, 0),
new Vector4(0, 0, 0, 1));
private static readonly Matrix4x4 Deuteranopia = new(
new Vector4(0.625f, 0.375f, 0.000f, 0),
new Vector4(0.700f, 0.300f, 0.000f, 0),
new Vector4(0.000f, 0.300f, 0.700f, 0),
new Vector4(0, 0, 0, 1));
private static readonly Matrix4x4 Tritanopia = new(
new Vector4(0.950f, 0.050f, 0.000f, 0),
new Vector4(0.000f, 0.433f, 0.567f, 0),
new Vector4(0.000f, 0.475f, 0.525f, 0),
new Vector4(0, 0, 0, 1));
private static readonly Matrix4x4 Achromatopsia = new(
new Vector4(0.299f, 0.587f, 0.114f, 0),
new Vector4(0.299f, 0.587f, 0.114f, 0),
new Vector4(0.299f, 0.587f, 0.114f, 0),
new Vector4(0, 0, 0, 1));
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9fff57f1963a7e74e8000da706ddafce
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: