LogoPixi’VN

Minigames

Guide to integrating minigames into Pixi’VN, including best practices, lifecycle management, and example implementations using PixiJS and React.

AI

You can use the button above to create your own minigame, with AI.

The beauty of an interactive story compared to normal text is that you can take a break and interact with minigames related to the story.

Minigames in visual novels are typically launched during the narrative. To switch between the narrative UI and the minigame UI, it is recommended to link the minigame to a route (e.g., "/minigame/snake") and navigate to it from the story. More information is available here.

Here are some tips:

  • Use PixiJS directly — either the pixi.js package itself or the @drincs/pixi-vn/pixi.js submodule that re-exports it — with components such as Graphics, Sprite, and Text, combined with PixiJS Layers to create a separate layer for your minigame, managed independently from the main visual novel interface.
  • Use hotkeys to handle keyboard input for your minigame controls. See Hotkeys to learn how to set them up.
  • It is always recommended not to use PixiJS to build the UI (buttons, score displays, game over messages, ...); use a framework such as React or Vue instead.
  • Saving and restoring the minigame's current state is the developer's responsibility.
  • Since a minigame is its own route, a project usually ends up with more than one of them under the same /minigame path (e.g. /minigame/snake, /minigame/quiz); use TanStack Router's file-based routing to share logic between them instead of duplicating it in every minigame (see below).
  • Starting a minigame is nothing more than navigating to its route from the story; if it needs a parameter (a difficulty, a level id, ...), pass it through that same route (see below).

Vorlagen

useMinigame is a custom hook that helps you manage the lifecycle of a minigame, including starting, updating, and cleaning up resources. It is present in all templates.

Zum Beispiel:

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>
            )}
        </>
    );
}

Examples

The Pixi’VN Team welcomes new proposals and contributions to make this library even more complete. Feel free to share or propose your minigame implementations in the chat below!

Auf dieser Seite