LogoPixi’VN

Minigames

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

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") and navigate to it from the story. More information is available here.

Here are some tips:

  • Use components provided by the @drincs/pixi-vn/pixi.js submodule, such as Graphics, Sprite, and Text, to create your minigame. @drincs/pixi-vn/pixi.js is the PixiJS import, so you can use all its features.
  • Use PixiJS Layers to create a separate layer for your minigame. This allows you to manage the minigame independently from the main visual novel interface.
  • Use the Ticker class to manage the game loop and update the game state.
  • Use window.addEventListener to handle user input, such as keyboard or mouse events.
  • Use HTML elements to create the UI, such as buttons, score displays, and game over messages.
  • If you use PixiJS Layers, remember that Pixi’VN does not save the state of layers, so you need to manage the minigame state yourself. Save the state in a variable and restore it when the minigame restarts.

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.

For example:

import { Layer } from "@drincs/pixi-vn";
import { Graphics, Ticker } from "@drincs/pixi-vn/pixi.js";
import { useCallback, useMemo, useRef, useState } from "react";
import useMinigame from "../hooks/useMinigame";

export default function MiniGame() {
    const ticker = useMemo(() => new Ticker(), []);
    const [displayScore, setDisplayScore] = useState(0);
    const [gameOver, setGameOver] = useState(false);

    const onKeyDown = useCallback((e: KeyboardEvent) => {
        // Handle key down events for game controls
    }, []);

    const game = useCallback(
        (layer: Layer) => {
            const endGame = () => {
                ticker.stop();
                setGameOver(true);
            };

            window.addEventListener("keydown", onKeyDown);

            ticker.add(({ deltaMS }) => {
                // Update game logic here
            });
            ticker.start();
        },
        [ticker] // They must not be changed during the game otherwise the game will restart
    );

    const options = useMemo(
        () => ({
            onExit() {
                ticker.stop();
                ticker.destroy();
                window.removeEventListener("keydown", onKeyDown);
            },
        }),
        [ticker, onKeyDown] // 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>
            )}
        </>
    );
}

Here are some examples:

The Pixi’VN Team welcomes new proposals and contributions to make this library even more complete. You can create a discussion to share or propose your minigame implementations.