Pixi’VN

小游戏

将小游戏集成到 Pixi’VN 中的指南,包括最佳实践、生命周期管理,以及使用 PixiJS 和 React 的示例实现。

AI

你可以使用上方的按钮,通过 AI 创建你自己的小游戏。

与普通文本相比,交互式故事的魅力在于,你可以稍作休息,与故事相关的小游戏进行互动。

视觉小说中的小游戏通常会在叙事过程中启动。为了在叙事 UI 与小游戏 UI 之间切换,建议将小游戏关联到一个路由(例如 "/minigame/snake"),并从故事中导航至该路由。更多信息请参见此处。

以下是一些提示:

  • 直接使用 PixiJS——可以是 pixi.js 包本身,也可以是重新导出它的 @drincs/pixi-vn/pixi.js 子模块——搭配 Graphics、Sprite、Text 等组件,并结合PixiJS 图层,为你的小游戏创建一个独立于主视觉小说界面进行管理的单独图层。
  • 使用快捷键来处理小游戏控制的键盘输入。请参阅Hotkeys了解如何设置它们。
  • 始终建议不要使用 PixiJS 来构建 UI(按钮、分数显示、游戏结束提示等);而应使用诸如React或Vue之类的框架。
  • 保存和恢复小游戏的当前状态是开发者的责任。
  • 由于小游戏本身就是一个路由,一个项目通常最终会在同一个 /minigame 路径下拥有不止一个小游戏(例如 /minigame/snake、/minigame/quiz);请使用TanStack Router的基于文件的路由机制,在它们之间共享逻辑,而不是在每个小游戏中重复编写(见下文)。
  • 启动一个小游戏,无非就是从故事中导航到它的路由;如果它需要参数(例如难度、关卡 ID 等),则通过同一个路由传递该参数(见下文)。

模板

useMinigame 是一个自定义 Hook,可帮助你管理小游戏的生命周期,包括启动、更新和清理资源。它包含在所有模板中。

例如:

src/routes/minigame/example.tsx
import { Layer } from "@drincs/pixi-vn";
import { Graphics, Ticker } from "@drincs/pixi-vn/pixi.js";
import { createFileRoute } from "@tanstack/react-router";
import { useCallback, useMemo, useState } from "react";
import { useHotkey } from "@tanstack/react-hotkeys";
import useMinigame from "@/lib/hooks/minigame-hooks";

export const Route = createFileRoute("/minigame/example")({
    component: MiniGame,
});

function MiniGame() {
    const [displayScore, setDisplayScore] = useState(0);
    const [gameOver, setGameOver] = useState(false);

    const ticker = useMemo(() => {
        const ticker = new Ticker();

        const endGame = () => {
            ticker.stop();
            setGameOver(true);
        };

        ticker.add(({ deltaMS }) => {
            // Update game logic here
        });

        return ticker;
    }, []);

    useHotkey(
        "ArrowUp",
        () => {
            // Handle key down events for game controls
        },
        { enabled: !gameOver },
    );

    const game = useCallback(
        (layer: Layer) => {
            ticker.start();
        },
        [ticker], // They must not be changed during the game otherwise the game will restart
    );

    const options = useMemo(
        () => ({
            onExit() {
                ticker.stop();
                ticker.destroy();
            },
        }),
        [ticker], // They must not be changed during the game otherwise the game will restart
    );

    const { loading } = useMinigame(game, options);

    return (
        <>
            <div
                style={{
                    position: "absolute",
                    top: 10,
                    left: 10,
                    color: "white",
                    fontSize: "24px",
                    background: "rgba(0,0,0,0.5)",
                    padding: "5px 10px",
                    borderRadius: "5px",
                }}
            >
                Score: {displayScore}
            </div>

            {gameOver && (
                <div
                    style={{
                        position: "absolute",
                        top: "50%",
                        left: "50%",
                        transform: "translate(-50%, -50%)",
                        color: "red",
                        fontSize: "48px",
                        background: "rgba(0,0,0,0.7)",
                        padding: "20px 40px",
                        borderRadius: "10px",
                    }}
                >
                    GAME OVER
                </div>
            )}
        </>
    );
}

示例

Pixi’VN 团队欢迎大家提出新的建议并做出贡献,让这个库变得更加完善。欢迎在下方的聊天区分享或提出你的小游戏实现方案!

本页内容