Files
zeling_v2/Docs/Design/62_Accessibility_System.md
2026-05-08 11:04:00 +08:00

18 KiB
Raw Permalink Blame History

62 · 无障碍功能系统Accessibility System

命名空间 BaseGames.Accessibility
所属文档集 ← 返回索引 · 总览
依赖 BaseGames.UI · BaseGames.Input · BaseGames.Audio · BaseGames.Core.Events
关联 10_UISystem · 25_InputRebindingUI · 12_AudioSystem · 29_DifficultyModesGuide
重要声明 无障碍功能 ≠ 简单模式。无障碍选项服务于有不同能力需求的玩家,不改变游戏的挑战定义,与难度系统独立运作。


目录

  1. 无障碍设计哲学
  2. AccessibilitySettingsSO — 数据容器
  3. 视觉无障碍
  4. 运动敏感度设置
  5. 字幕与文字无障碍
  6. 输入辅助系统
  7. 音频无障碍
  8. 无障碍设置 UI
  9. SaveData 集成
  10. 平台无障碍合规

1. 无障碍设计哲学

1.1 核心立场

立场 说明
尊重需求多样性 色盲、运动障碍、听觉障碍玩家有同等权利享受完整游戏体验
非侵入性设计 无障碍选项默认关闭,不影响未开启的玩家的游戏体验
与难度系统分离 无障碍设置不出现在难度菜单中,避免"弱者选项"的负面暗示
独立专属入口 主菜单和暂停菜单均有独立的"无障碍"按钮(不与"设置"合并)

1.2 支持目标用户群

用户需求 覆盖方案
色盲(红绿色盲/蓝黄色盲) 色盲滤镜 + 可选高对比模式
畏光/光敏感癫痫 屏幕震动关闭 + 闪光效果弱化 + 闪光频率上限
运动晕眩 镜头晃动关闭 + 视野减少模式
上肢/手部运动障碍 连按→长按、自动弹反辅助、输入重绑定(见 25_InputRebindingUI
弱视 大字体模式、UI 高对比模式、游戏画面对比度增强
听觉障碍 全字幕SFX 提示字幕)、视觉危险提示

2. AccessibilitySettingsSO — 数据容器

[CreateAssetMenu(menuName = "Accessibility/AccessibilitySettings")]
public class AccessibilitySettingsSO : ScriptableObject
{
    // ── 视觉无障碍 ──
    [Header("色盲模式")]
    public ColorBlindMode colorBlindMode = ColorBlindMode.None;
    public bool           highContrastMode = false;
    public float          gameContrastBoost = 0f;        // 0 ~ 1.0

    // ── 运动无障碍 ──
    [Header("运动敏感度")]
    public bool  disableScreenShake = false;
    public bool  disableCameraMotion = false;            // 关闭镜头摇移过渡
    public float cameraMotionScale = 1f;                 // 0 ~ 1.00 = 完全关闭)
    public bool  reduceParticleEffects = false;          // 减少粒子密度 50%
    public bool  disableFlashingEffects = false;         // 禁止所有频率 > 3Hz 的闪烁
    public int   flashFrequencyLimit = 3;                // 光敏保护最大闪光频率Hz

    // ── 字幕 ──
    [Header("字幕系统")]
    public bool   subtitlesEnabled = false;
    public bool   sfxSubtitlesEnabled = false;           // 环境音/危险音效的文字提示
    public float  subtitleFontSizeMultiplier = 1f;       // 0.75 ~ 2.0
    public bool   subtitleBackgroundEnabled = true;      // 字幕背景不透明框
    public float  subtitleBackgroundOpacity = 0.7f;
    public bool   speakerNameEnabled = true;

    // ── 输入辅助 ──
    [Header("输入辅助")]
    public bool  autoParryAssist = false;                // 自动弹反辅助
    public float parryWindowExtension = 0f;              // 弹反窗口扩展0 ~ 0.2
    public bool  holdToMash = false;                     // 长按替代连按(如快速逃脱 QTE
    public bool  stickyJump = false;                     // 跳跃键释放容忍(松手后 0.1s 内仍可触发可变跳跃高度缩减)
    public bool  autoClimb = false;                      // 接触墙面自动开始攀爬(无需按键)

    // ── 音频无障碍 ──
    [Header("音频无障碍")]
    public bool  monoAudio = false;                      // 单声道模式(听觉障碍)
    public float leftRightBalance = 0f;                  // -1 ~ +1
    public bool  visualDangerIndicator = false;          // 视觉危险提示(代替音效危险提示)
}

public enum ColorBlindMode
{
    None,           // 无(默认)
    Protanopia,     // 红色盲
    Deuteranopia,   // 绿色盲
    Tritanopia,     // 蓝黄色盲
    Achromatopsia,  // 全色盲(高对比灰度)
}

3. 视觉无障碍

3.1 色盲滤镜系统

使用 URP 2D 后处理 ColorBlindFilter 自定义 Pass 实现,在最终合成阶段应用色彩矩阵变换:

// Assets/Scripts/Accessibility/ColorBlindFilter.cs
public class ColorBlindFilter : ScriptableRendererFeature
{
    [SerializeField] ColorBlindMode _mode;

    // 色彩矩阵3×3基于 Brettel et al. 1997 算法)
    private static readonly Dictionary<ColorBlindMode, Matrix4x4> _matrices = new()
    {
        [ColorBlindMode.Protanopia] = new Matrix4x4(
            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)),
        // Deuteranopia / Tritanopia / Achromatopsia 矩阵类似...
    };

    public override void Create() { /* ... */ }
    public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
    {
        if (_mode == ColorBlindMode.None) return;
        renderer.EnqueuePass(new ColorBlindPass(_matrices[_mode]));
    }
}

切换时机AccessibilityManager.ApplySettings() 在设置变更时调用,更新 Renderer Feature 参数。

3.2 各色盲模式下的关键颜色调整

色盲滤镜作用于全屏幕后处理。此外,以下 UI 元素额外添加图标/形状辅助区分,不依赖单一颜色:

元素 默认颜色方案 色盲辅助方案
HP/ HP 绿色 / 灰色 实心圆 / 空心圆(形状区分)
可弹反电报 金色轮廓 金色轮廓 + 星形粒子
不可弹反警告 红色轮廓 红色轮廓 + X 形覆盖
危险液体区域 颜色区分 底部危险斜线纹样
地图房间类型 颜色区分 颜色 + 图标Boss头骨/存档灯/商店袋)

3.3 高对比度模式

highContrastMode = true 时:

  • 全屏应用高对比后处理Contrast +0.5, Saturation -0.3, Brightness 微调)
  • 所有敌人 + 玩家 + NPC 的轮廓线从标准宽度 (1px) 加粗为 2px 高亮白色轮廓(使用 OutlinePass
  • 地图背景统一变为深灰,房间颜色鲜明化

3.4 游戏对比度增强

gameContrastBoost0-1.0)控制 Global Volume 中 Color Adjustment 的对比度:

void ApplyContrastBoost(float boost)
{
    var ca = globalVolume.profile.TryGet<ColorAdjustments>(out var adj);
    adj.contrast.value = Mathf.Lerp(0f, 50f, boost);  // 0 ~ 50PostProcess 单位)
}

4. 运动敏感度设置

4.1 屏幕震动控制

disableScreenShake 控制 Feel MMF CinemachineImpulse 和所有 ScreenShake 反馈:

// FeedbackSystem 的 ScreenShake 请求需经过此检查
public static bool CanPlayScreenShake()
    => !AccessibilityManager.Instance.Settings.disableScreenShake;

当关闭时:

  • MMFeedbacks 中的 MMF_CameraShakeMMF_CinemachineImpulse 不执行
  • 其余反馈VFX、音效、时间缩放正常执行

4.2 镜头运动控制

cameraMotionScale0-1.0)缩放以下镜头效果:

效果 默认值 Scale = 0 时
房间过渡镜头移动速度 正常 立即切换(无过渡动画)
Cinemachine Damping跟随延迟 0.3s 0s完全锁定
Boss 战的镜头拉远效果 启用 禁用
死亡慢镜头特写 启用 跳过特写

4.3 粒子效果减少

reduceParticleEffects = true 时:

// VFXPool.SpawnParticle() 入口处检查
int particleCount = AccessibilityManager.Instance.Settings.reduceParticleEffects
    ? Mathf.CeilToInt(requestedCount * 0.5f)   // 减少 50%
    : requestedCount;

影响范围攻击命中粒子、环境粒子、Dead 消散粒子。不影响:弹反特效(功能性视觉反馈)、危险区域边界粒子。

4.4 闪光频率保护

光敏感癫痫保护:AccessibilityValidator 在任何闪烁效果触发前检查频率限制:

public class AccessibilityValidator
{
    private float _lastFlashTime;

    // 在所有 HurtFlash / BossTransitionFlash / VoidShockwave 等调用前检查
    public bool CanFlash()
    {
        var settings = AccessibilityManager.Instance.Settings;
        if (settings.disableFlashingEffects) return false;

        float minInterval = 1f / settings.flashFrequencyLimit;  // 默认 1/3 ≈ 0.33s
        if (Time.unscaledTime - _lastFlashTime < minInterval) return false;

        _lastFlashTime = Time.unscaledTime;
        return true;
    }
}

5. 字幕与文字无障碍

5.1 对话字幕

subtitlesEnabled 控制对话文字是否显示(默认 true因为游戏无配音这实质上是强制开启的此选项更多用于控制字幕样式

字幕面板配置:

SubtitlePanelCanvas
  ├── BackgroundImageRectTransform 根据文字内容自动扩展)
  │     opacity: subtitleBackgroundOpacity0.7 默认)
  │     color: #000000
  └── SubtitleTextTextMeshPro
        font: BitmapFont_Main× subtitleFontSizeMultiplier
        color: #E8E0D0
        alignment: 居中
        max line width: 屏幕宽度 × 0.7

5.2 环境音效字幕SFX Subtitles

sfxSubtitlesEnabled = true 时,危险提示音效会在屏幕边缘显示文字提示:

音效 SFX 字幕文本 显示位置
Boss 攻击电报音 [Boss 警告] 屏幕顶部
危险环境(毒素流动) [危险液体流动声] 屏幕左/右侧(来源方向)
背后敌人警报 [背后有敌人] 屏幕底部
存档台激活 [存档台激活] 屏幕右上角

SFX 字幕持续时间2 秒然后淡出Fade 0.5s)。

[CreateAssetMenu(menuName = "Accessibility/SFXSubtitleConfig")]
public class SFXSubtitleConfigSO : ScriptableObject
{
    public string      audioClipId;    // 关联的 AudioEventSO ID
    public string      subtitleText;   // 对应文字(需多语言化)
    public SubtitlePos position;       // Top / Bottom / Left / Right
    public float       displayDuration = 2f;
}

5.3 大字体支持

subtitleFontSizeMultiplier0.75 ~ 2.0)影响:

  • 对话文字大小
  • Lore 碎片文字大小
  • 暂停菜单所有文字

HUD 数字HP/Geo固定大小16px不受影响——超大字体会覆盖游戏画面。


6. 输入辅助系统

6.1 自动弹反辅助Auto Parry Assist

autoParryAssist = true 时,弹反判定规则变更:

正常弹反:玩家必须在攻击命中前的 ParryWindowMs 内按下弹反键
自动弹反辅助:
  方式 A窗口扩展parryWindowExtension 额外增加弹反窗口(最大 +0.2s
  方式 B自动触发若玩家按下了攻击/弹反键,在 ±0.3s 内命中的可弹反攻击自动弹反

重要:成就 "完美弹反大师"autoParryAssist = true 时无法解锁。

6.2 长按替代连按Hold to Mash

holdToMash = true 时,所有需要快速连按的操作改为长按:

操作 原来 辅助后
逃脱敌人抓取QTE 快速连按 A 键 长按 A 键
快速填充灵力
(暂无其他连按操作)
// InputAssistService.cs
public bool CheckMashInput(InputAction action, int requiredPresses)
{
    if (!Settings.holdToMash)
        return _mashCounter[action] >= requiredPresses;   // 正常连按计数

    // Hold to Mash长按 0.5s 等效完成所有连按
    return action.ReadValue<float>() > 0.5f &&
           Time.unscaledTime - _holdStartTime[action] >= 0.5f;
}

6.3 粘性跳跃Sticky Jump

stickyJump = true 时,松开跳跃键后额外 0.1s 内仍然可以触发 CutJump(可变跳跃高度缩减):

// PlayerMovement.cs
public void HandleJumpRelease()
{
    float tolerance = Settings.stickyJump ? 0.1f : 0f;
    if (Time.time - _jumpPressedTime > _jumpHoldDuration + tolerance)
        CutJump();
}

6.4 自动攀墙Auto Climb

autoClimb = true 时,玩家接触墙面自动进入 WallGrab 状态(无需主动按键)。

注意:需保留手动离开墙壁的操作(按反向移动键),避免误触导致无法脱离。


7. 音频无障碍

7.1 单声道模式

monoAudio = true 时,在 AudioMixer 的 Master 混音器上应用单声道 DSP 效果

void ApplyMonoAudio(bool mono)
{
    if (mono)
        masterMixer.SetFloat("MonoMix", 1f);   // Custom DSP Parameter将 L/R 混合
    else
        masterMixer.SetFloat("MonoMix", 0f);
}

7.2 左右声道平衡

leftRightBalance-1 ~ +1调整 Master Mixer 的 L/R 输出增益:

void ApplyBalance(float balance)
{
    float leftGain  = balance <= 0 ? 1f : 1f - balance;
    float rightGain = balance >= 0 ? 1f : 1f + balance;
    masterMixer.SetFloat("LeftGain",  Mathf.LinearToDecibels(leftGain));
    masterMixer.SetFloat("RightGain", Mathf.LinearToDecibels(rightGain));
}

7.3 视觉危险提示Visual Danger Indicator

visualDangerIndicator = true 时,以下原本只靠音效提示的危险额外添加视觉提示:

危险来源 音效原始提示 视觉替代
Boss 范围攻击 低沉震鸣 地面发光红色警示线1.5s 预警)
背后敌人接近 脚步声 屏幕边缘红色闪光条(来源方向)
蚀素区域边界 氛围嗡鸣 屏幕边缘蚀紫色渐变提示
液体危险区 液体流动声 液体表面持续红色虚线框

8. 无障碍设置 UI

8.1 入口位置

无障碍设置有 两个入口

  1. 主菜单 → 设置 → 无障碍(独立标签页)
  2. 暂停菜单 → 无障碍快捷入口(显示当前最关键的 3 项状态)

8.2 设置面板结构

无障碍设置面板
├── 视觉
│   ├── 色盲模式(下拉:无/红色盲/绿色盲/蓝黄色盲/全色盲)
│   ├── 高对比度模式(开关)
│   └── 游戏对比度增强滑块0% ~ 100%
│
├── 运动
│   ├── 关闭屏幕震动(开关)☆
│   ├── 镜头运动强度滑块0% ~ 100%)☆
│   ├── 减少粒子特效(开关)
│   └── 关闭频闪效果(开关)☆
│
├── 字幕
│   ├── 显示字幕(开关)
│   ├── 环境音效字幕(开关)
│   ├── 字体大小滑块75% ~ 200%
│   ├── 字幕背景(开关)
│   └── 显示说话者名称(开关)
│
├── 输入辅助
│   ├── 弹反辅助(开关)☆
│   ├── 弹反窗口扩展(滑块:+0 ~ +0.2s,在弹反辅助开启时可用)
│   ├── 长按替代连按(开关)
│   └── 自动攀墙(开关)
│
└── 音频
    ├── 单声道模式(开关)
    ├── 左右声道平衡(滑块:左 ~ 右)
    └── 视觉危险提示(开关)

☆ = 最常用选项,在暂停菜单快捷入口显示

8.3 预设方案

提供 3 个一键应用的预设(不覆盖用户的自定义设置,作为起点建议):

预设名 开启选项
运动友好 关闭屏幕震动 + 镜头运动强度 0% + 关闭频闪
视觉辅助 高对比度 + 对比度增强 60% + 字幕字体 150%
操作辅助 弹反窗口 +0.1s + 长按替代连按 + 自动攀墙

9. SaveData 集成

无障碍设置存储于 独立存档槽(不随主存档数据,全局保存):

{
  "accessibility": {
    "colorBlindMode": "None",
    "highContrastMode": false,
    "gameContrastBoost": 0.0,
    "disableScreenShake": false,
    "disableCameraMotion": false,
    "cameraMotionScale": 1.0,
    "reduceParticleEffects": false,
    "disableFlashingEffects": false,
    "flashFrequencyLimit": 3,
    "subtitlesEnabled": false,
    "sfxSubtitlesEnabled": false,
    "subtitleFontSizeMultiplier": 1.0,
    "subtitleBackgroundEnabled": true,
    "subtitleBackgroundOpacity": 0.7,
    "speakerNameEnabled": true,
    "autoParryAssist": false,
    "parryWindowExtension": 0.0,
    "holdToMash": false,
    "stickyJump": false,
    "autoClimb": false,
    "monoAudio": false,
    "leftRightBalance": 0.0,
    "visualDangerIndicator": false
  }
}

文件路径{persistentDataPath}/Settings/accessibility.json(与游戏存档分离)
加载时机:游戏启动时最优先加载,在主菜单显示前完成应用。


10. 平台无障碍合规

10.1 各平台基础要求

平台 要求 本文档覆盖
Steam 无强制要求,但 Steam Deck 推荐色盲模式 色盲滤镜
Nintendo Switch 无强制 accessibility 要求
PlayStation TRC T178 系列:要求字幕 + 屏幕震动开关 字幕 + 震动开关
Xbox ACS 认证:推荐支持 20+ 无障碍功能 部分覆盖

10.2 国际标准参考

本系统参考 WCAG 2.1 Level AAGame Accessibility Guidelines (gameaccessibilityguidelines.com) 的以下条目:

  • Visual: 1.1颜色不作唯一区分方式、1.2高对比文字、1.4(调整大小)
  • Motor: 2.1可配置按键映射、2.2(可调整时间要求)
  • Deaf/Hard of Hearing: 3.1字幕、3.2(视觉警报)
  • Cognitive: 4.1(避免信息超载)