zzzUI

用 React 搭建游戏设置菜单

游戏设置菜单通常由几个标签页组成,每页包含若干设置行:左侧为设置名称,右侧为对应的控件,底部是应用更改的操作栏。本文介绍如何用 React、shadcn 和 zzzUI 搭建这样一个设置菜单;如需直接使用,也可以用一条命令安装现成的代码块。

直接安装现成代码块

最快的做法是安装 Settings menu 代码块:它只有一个文件,位于 components/blocks/,下文介绍的各个部分都已组装完成。安装时会一并安装它用到的组件。

terminal
pnpm dlx shadcn@latest registry add @zzzui=https://zzzui.catforest.dev/r/{name}.jsonpnpm dlx shadcn@latest add @zzzui/settings-menu

本页其余部分说明这个代码块是如何组装的,便于按需修改,或自行实现一个。

每项设置占一行

玩家浏览设置菜单时,会沿左侧向下扫读名称,再到右侧操作控件。因此每项设置占一行,分为两列:左列是名称,可附一行说明;右列是控件,靠右对齐。在手机上,两列改为上下排列。

row.tsx
function Row({ name, line, children }) {  return (    <div className="grid items-center gap-x-8 gap-y-3 py-4 sm:grid-cols-[1fr_minmax(0,16rem)]">      <div className="grid gap-0.5">        <span className="font-bold font-zero italic">{name}</span>        {line ? <span className="text-zero-sm text-zero-text-3">{line}</span> : null}      </div>      <div className="flex items-center sm:justify-end">{children}</div>    </div>  );}

一项设置值不值得放进菜单,往往取决于这行说明:「消除画面撕裂;略微增加延迟」告诉玩家开启垂直同步的代价,这是仅凭名称看不出来的。

按类别划分标签页

按设置影响的方面分组(画面、音频、操控、玩法),每组置于一个 Tabs 面板。音量用 Slider,开关项用 Switch,从列表中选一项用 Select,三选一用 Radio group。每个控件都要设置 aria-label:行里显示的名称并不会自动成为控件的无障碍标签。

settings.tsx
<Tabs defaultValue="display">  <TabsList>    <TabsTab value="display">Display</TabsTab>    <TabsTab value="audio">Audio</TabsTab>    <TabsTab value="controls">Controls</TabsTab>  </TabsList>  <TabsPanel value="audio">    <Row name="Music">      <Slider value={music} onValueChange={(v) => setMusic(v as number)} aria-label="Music volume" />    </Row>    <Row name="Subtitles">      <Switch checked={subtitles} onCheckedChange={setSubtitles} aria-label="Subtitles" />    </Row>  </TabsPanel></Tabs>

按键绑定最好直接显示按键本身:每个操作占一行,按键用 Kbd 呈现。

有改动时才可应用

保留两份状态:界面上当前的值,以及上次应用的值。两者一致时,Apply 按钮保持禁用,玩家一眼就能看出有没有尚未应用的更改。

settings.tsx
const [settings, setSettings] = useState(defaults);const [saved, setSaved] = useState(defaults);const changed = JSON.stringify(settings) !== JSON.stringify(saved);<Button disabled={!changed} onClick={() => setSaved(settings)}>  {changed ? "Apply" : "Applied"}</Button>

恢复默认需按住确认

恢复默认会清除玩家调整过的全部设置,不应因一次误点而触发。Hold button 需要一直按住,直到填充条铺满整个按钮;提前松开,填充条会逐渐退回。鼠标或触控、Enter 或空格键、快捷键均可操作,读屏软件会提示用户按住。

settings.tsx
<HoldButton size="sm" onConfirm={() => setSettings(defaults)}>  Hold to restore defaults</HoldButton>

延伸阅读

游戏的其他界面可参考代码块:Mission briefing、Loadout、Leaderboard 与 HUD。若要一次性改变它们的整体外观,请阅读《用设计 token 定制 zzzUI 主题》。