自定义 Menu(游戏内选项)
用 GameMenu 与 Item 替换游戏内选项菜单,并用 Item 的 bindKey 绑定快捷键
作用
游戏内选项(Menu)用于在故事中展示选择分支,玩家点击或按快捷键选择后执行对应动作。通过 game.configure 的 menu 配置项,可替换默认的 Menu 组件,实现自定义的选项样式和布局
使用 GameMenu 作为选项容器,Item 渲染单个选项。Item.bindKey 用于绑定快捷键
1. 创建自定义 Menu 组件
自定义菜单组件接收 items(选项索引数组),使用 GameMenu 作为容器,Item 渲染每个选项。Item 会自动从上下文中获取对应选项的文本和点击逻辑
import { GameMenu, Item } from "narraleaf-react";
function CustomMenu({ items }: { items: number[] }) {
return (
<GameMenu
className="absolute flex flex-col items-center justify-center min-w-full w-full h-full bg-black/50"
// GameMenu:容器,负责宽高比缩放
>
{items.map((index) => (
<Item
key={index}
className="bg-white/90 px-6 py-3 rounded-lg mt-3 w-64 text-center hover:bg-white transition-colors"
defaultColor="black"
// Item:渲染一个选项,索引来自 items 数组
/>
))}
</GameMenu>
);
}
2. 为选项绑定快捷键
Item 支持 bindKey 属性,按下对应按键时可直接选中该选项。键值参考 Key_Values
function CustomMenu({ items }: { items: number[] }) {
return (
<GameMenu className="absolute flex flex-col items-center justify-center min-w-full w-full h-full">
{items.map((index) => (
<Item
key={index}
className="bg-white p-2 mt-2 w-1/2"
defaultColor="black"
bindKey={String(index + 1)}
// bindKey:"1"、"2"、"3" 等——按下对应按键即可选中该项
/>
))}
</GameMenu>
);
}3. 只让选项区域接收点击
外层使用 pointer-events-none,避免全屏容器阻挡点击;内层使用 pointer-events-auto,只让选项区域可交互:
import { GameMenu, Item } from "narraleaf-react";
function CustomMenu({ items }: { items: number[] }) {
return (
<GameMenu
className="absolute inset-0 flex items-center justify-center w-full h-full pointer-events-none"
// pointer-events-none:菜单区域外的点击不被阻挡
>
<div className="relative w-full max-w-5xl pointer-events-auto px-4 md:px-8">
{/* pointer-events-auto:只有菜单区域可交互 */}
<div className="px-10 py-8 space-y-4">
{items.map((index) => (
<Item
key={index}
className="block md:w-3/4 lg:w-2/3 mx-auto text-center py-3 px-6
hover:bg-white/10 transition-all duration-300 transform hover:-translate-y-1
hover:[filter:drop-shadow(8px_8px_0_rgba(0,0,0,0.8))] disabled:opacity-50 disabled:cursor-not-allowed"
defaultColor="white"
fontSize={18}
fontWeight={700}
style={{
backgroundColor: "rgba(64,168,197,0.9)",
clipPath: "polygon(0 0,100% 0,97% 100%,3% 100%)", // 平行四边形
filter: "drop-shadow(4px 4px 0 rgba(0,0,0,0.6))",
}}
/>
))}
</div>
</div>
</GameMenu>
);
}菜单选项文字由自定义菜单组件控制。可以向 Item 传入 defaultColor、fontSize、fontFamily、fontWeight、fontWeightBold,也可以让文字从 CSS 继承
4. 横向布局示例
function HorizontalMenu({ items }: { items: number[] }) {
return (
<GameMenu className="absolute flex flex-row items-center justify-center gap-4 bottom-20 left-0 right-0">
{items.map((index) => (
<Item
key={index}
className="bg-amber-500/80 px-4 py-2 rounded hover:bg-amber-400"
defaultColor="black"
/>
))}
</GameMenu>
);
}5. 在脚本中定义选项
选项在 Menu 元素中通过 choose 定义,支持 hideIf、disableIf 等条件控制:
// 在剧情脚本中:
Menu.prompt("你要怎么做?")
.choose("往左走", [character.say("我往左走了")])
.choose("往右走", [character.say("我往右走了")])
.choose("原地不动", [character.say("我选择等待")])
.hideIf(persis.isTrue("alreadyMoved")); // 条件满足时隐藏6. 注册到游戏
import { Game, GameProviders, Player } from "narraleaf-react";
const game = new Game({ menu: CustomMenu });
function App() {
return (
<GameProviders game={game}>
<Player story={story} onReady={({ liveGame }) => liveGame.newGame()} />
</GameProviders>
);
}参考
- Menu - 菜单组件 API
- Menu 元素 - 脚本中的 Menu 用法
- game.configure - 游戏配置