본문으로 건너뛰기
cccskills
무료GitHub에 공개

pixi-vn-characters

Use when defining visual novel characters for a @drincs/pixi-vn game — creating a character with a name, color, icon and other props, registering it so it can be looked up by id, and referencing it from dialogue/narration. Load this before writing any `CharacterBaseModel` or `RegisteredCharacters` code.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

설치 방법 보기

포함된 파일(1)

  • SKILL.md12.2 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

Pixi'VN Characters

When to use this skill

Use this skill whenever a task involves creating the characters that appear in a Pixi'VN game (name, surname, age, color, icon, and any custom fields), storing them so the engine and narration code can find them by id, or hooking a character into a narration.dialogue line. It does not cover how dialogue/text itself is displayed (see the narration skill) or how variables are generally saved (see the storage skill).

import { CharacterBaseModel, RegisteredCharacters } from "@drincs/pixi-vn";

(Also available from the narrower @drincs/pixi-vn/characters subpath — see pixi-vn for when to prefer that.)

Full reference: https://pixi-vn.com/start/character

1. Defining a character

CharacterBaseModel is the built-in character class. Construct it with a unique string id and a props object (name, surname, age, icon, color — all optional):

import { CharacterBaseModel } from "@drincs/pixi-vn";

export const liam = new CharacterBaseModel("liam", {
  name: "Liam",
  surname: "Smith",
  age: 25,
  icon: "https://example.com/liam.png",
  color: "#9e2e12",
});

export const emma = new CharacterBaseModel("emma", {
  name: "Emma",
  surname: "Johnson",
  age: 23,
  icon: "https://example.com/emma.png",
  color: "#c23b7f",
});

The id is the value used everywhere else in the game (dialogue, lookups, history) to reference this character, so it must be unique and stable across saves.

name/surname/age are actually get/set accessors backed by the game's storage (see CharacterStoredClass, which CharacterBaseModel extends, and the stored classes doc): the value passed in the constructor becomes the default, but setting liam.name = "..." at runtime persists an override in storage that survives save/load and takes priority over the default. icon and color are plain readonly fields, not stored/overridable at runtime.

import { liam } from "@/content/characters";

console.log(liam.name); // "Liam"
liam.name = "Liam Smith";
console.log(liam.name); // "Liam Smith" — now persisted in game storage

If a character's id is ever changed between game versions, the engine does not migrate the storage data from the old id to the new one — treat ids as permanent once shipped.

Custom character classes with extra fields

If you need fields beyond name/surname/age/icon/color (e.g. a sprite id or a relationship stat), this is the recommended, docs-endorsed approach (project templates already ship it as models/Character.ts):

  1. Create a class Character extends CharacterStoredClass implements CharacterInterface.
  2. Augment the (otherwise empty) CharacterInterface in a pixi-vn.d.ts file via declare module "@drincs/pixi-vn" so every CharacterInterface consumer in the project — including RegisteredCharacters.get() — sees the richer, project-specific type.
  3. For any field that must persist across saves, back it with a getter/setter that calls this.getStorageProperty() / this.setStorageProperty() (inherited from CharacterStoredClass), the same pattern CharacterBaseModel uses internally for name/surname/age.

This is exactly what the official pixi-vn-react-template (the "TS narration

  • React" project npm create pixi-vn@latest scaffolds) ships as src/models/Character.ts — stored getters/setters for name, surname and age, and plain (non-stored) fields for icon/color:
// models/Character.ts
import { type CharacterInterface, CharacterStoredClass } from "@drincs/pixi-vn";

export default class Character
  extends CharacterStoredClass
  implements CharacterInterface
{
  constructor(
    id: string | { id: string; emotion: string },
    props: CharacterProps,
  ) {
    super(
      typeof id === "string" ? id : id.id,
      typeof id === "string" ? "" : id.emotion,
    );
    this.defaultName = props.name;
    this.defaultSurname = props.surname;
    this.defaultAge = props.age;
    this.icon = props.icon;
    this.color = props.color;
  }

  // stored — persists across save/load, same pattern as CharacterBaseModel
  private defaultName?: string;
  get name(): string {
    return (
      this.getStorageProperty<string>("name") || this.defaultName || this.id
    );
  }
  set name(value: string | undefined) {
    this.setStorageProperty<string>("name", value);
  }

  private defaultSurname?: string;
  get surname(): string | undefined {
    return this.getStorageProperty<string>("surname") || this.defaultSurname;
  }
  set surname(value: string | undefined) {
    this.setStorageProperty<string>("surname", value);
  }

  private defaultAge?: number;
  get age(): number | undefined {
    return this.getStorageProperty<number>("age") || this.defaultAge;
  }
  set age(value: number | undefined) {
    this.setStorageProperty<number>("age", value);
  }

  // not stored
  readonly icon?: string;
  readonly color?: string;
}

interface CharacterProps {
  name?: string;
  surname?: string;
  age?: number;
  icon?: string;
  color?: string;
}
// pixi-vn.d.ts
declare module "@drincs/pixi-vn" {
  interface CharacterInterface {
    name: string;
    surname?: string;
    age?: number;
    readonly icon?: string;
    readonly color?: string;
  }
}

2. Registering and looking up characters

Characters are looked up at runtime by id through the RegisteredCharacters registry (a namespace, not a decorator despite its file name). Calling RegisteredCharacters.add(...) is required — a CharacterBaseModel (or custom class) instance that is only exported/constructed but never passed to add will not be findable by id and will not work in dialogue lookups after a save/load. Register every character you define once, at module load time (recommended: import them all at project startup), then reference it by id elsewhere:

import { RegisteredCharacters } from "@drincs/pixi-vn";

RegisteredCharacters.add(liam, emma);
// or with arrays:
RegisteredCharacters.add([liam, emma]);

Other RegisteredCharacters functions:

  • RegisteredCharacters.get(id) — returns the character (or undefined, logging a warning if it was never registered).
  • RegisteredCharacters.has(id) — checks whether an id is registered.
  • RegisteredCharacters.values() — returns all registered characters.
  • RegisteredCharacters.keys() — returns all registered ids.
  • RegisteredCharacters.clear() — removes all registered characters (mainly useful in tests).
const liam = RegisteredCharacters.get("liam");

Re-adding an id that's already registered overwrites the previous character (a log message notes this), so registration order matters if two files define the same id.

Real-world project convention (official React template)

The official pixi-vn-react-template (what npm create pixi-vn@latest scaffolds for the "TS narration + React" setup) defines its characters as export const instances in src/content/characters.ts, then registers all of them with a single RegisteredCharacters.add([...]) call at module scope:

// src/content/characters.ts
import Character from "@/models/Character";
import { RegisteredCharacters } from "@drincs/pixi-vn";

export const mc = new Character("mc", { name: "Me" });
export const james = new Character("james", {
  name: "James",
  color: "#0084ac",
});
export const steph = new Character("steph", {
  name: "Steph",
  color: "#ac5900",
});
export const sly = new Character("sly", { name: "Sly", color: "#6d00ac" });

RegisteredCharacters.add([mc, james, steph, sly]);

This works with no manual import list to maintain because src/content/index.ts uses import.meta.glob to import every file under content/ for its side effects at app startup — so the module-scope RegisteredCharacters.add(...) call above always runs. That means you never need to remember to import each new character file yourself (dropping it anywhere under content/ is enough), but conversely, a character defined (and .added) in a file outside content/ that nothing else imports never gets its add call executed, and stays silently unregistered.

This is the official template's convention, not a hard requirement of the library — RegisteredCharacters.add can be called from any file, as long as it's guaranteed to run before the character is looked up. Projects without this glob-import setup must import each character file explicitly instead (e.g. from a top-level content/index.ts or the app's entry point).

Typed ids (optional)

Character ids are typed as CharacterIdType, which is string by default. If a project augments the empty PixivnCharacterIds interface (typically auto-generated by the vitePluginPixivn Vite plugin's typeFilePath option), CharacterIdType narrows to a union of known ids and typos become compile errors. Don't assume this augmentation exists unless you see a generated pixi-vn.gen.d.ts/similar file in the project — if not, treat ids as plain strings.

3. Emotions

A character id may optionally be paired with an emotion by passing an object instead of a plain string as the constructor's first argument:

export const alice = new CharacterBaseModel("alice", {
  name: "Alice",
  icon: "https://example.com/alice.png",
  color: "#9e2e12",
});

export const angryAlice = new CharacterBaseModel(
  { id: "alice", emotion: "angry" },
  { icon: "https://example.com/angryAlice.png" },
);

RegisteredCharacters.add([alice, angryAlice]);

Internally this stores the character's name/surname/age under a separate storage key ("<id>@<emotion>") while still falling back to the base id's stored/default values if an emotion-specific value isn't set — the base id and its emotion variants share the same underlying character data except where the emotion variant overrides it:

console.log(alice.name); // "Alice"

alice.name = "Eleonora";
console.log(alice.name); // "Eleonora"
console.log(angryAlice.name); // "Eleonora" — falls back to the base id's value

angryAlice.name = "Angry Eleonora";
console.log(alice.name); // "Eleonora" — unaffected
console.log(angryAlice.name); // "Angry Eleonora" — its own override now set

Use this only if you need per-emotion overrides of a character's stored fields; for swapping the character's on-screen sprite/image per emotion, that's driven by the canvas/image APIs, not this module.

4. Using a character in dialogue

Once registered, pass the character instance (or its id string) as the character field of a dialogue, e.g. inside a label step:

narration.dialogue = {
  character: liam,
  text: "Which test do you want to perform?",
};
// or by id:
narration.dialogue = { character: "liam", text: "..." };

Which form to prefer depends on whether the project's @drincs/pixi-vn/vite plugin generates typed character ids (the typeFilePath option covered in pixi-vn, which narrows CharacterIdType from plain string to a union of the actually-registered ids):

  • Without it, prefer passing the instance (character: liam) — a bare string id has no compile-time typo protection, while the instance is a real variable reference the compiler already checks.
  • With it, prefer the string-id form (character: "liam") — it now gets the same compile-time safety (a typo like "liem" fails to typecheck against the generated CharacterIdType union) without needing to import the character module wherever a dialogue line is written, which keeps label files decoupled from character-definition files.

DialogueInterface.character accepts CharacterInterface | string; when a string id is stored, the engine resolves it back to the registered character object (via RegisteredCharacters.get) when the dialogue is read back — so a character must already be registered by the time its dialogue line runs. Reading/rendering the resolved dialogue is covered by the narration skill.

Related skills

pixi-vn, pixi-vn-narration, pixi-vn-storage

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

pixi-vn

무료

Index and entry point of the Pixi'VN skill set — start here for any task on a @drincs/pixi-vn project, then follow the skill map to the right skill (narration, canvas, characters, assets, sound, storage, saves, history, ui, minigames, testing, migration). Use when setting up a new or existing project, wiring the main.ts entry point, or calling the top-level Game API (Game.init, Game.start, Game.onEnd, Game.addOnError, Game.onNavigate, Game.clear) — every Pixi'VN project needs this before touching canvas, narration, sound, storage, or history.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

DRincs-Productions/pixi-vn1522026년 10월 15일 업데이트

Use when adding, organizing, registering, or loading non-code files (images, audio, video) in a Pixi'VN project — local assets processed by PixiJS AssetPack vs. online/remote-hosted assets, the AssetsManifest/bundle/alias system, where to edit the manifest for each case, and when to load bundles (at startup, in the background, per-label, per-route). Load this before writing any code that references an image/sound/video path directly, or before adding entries to a manifest.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

DRincs-Productions/pixi-vn1522026년 10월 15일 업데이트

Use when adding, moving, or removing images, sprites, text, or video on the Pixi'VN game canvas, or when applying or creating transitions (dissolve, fade, move, zoom, push, wipe, iris, split, flash, blur, pixelate, or a custom one built on `canvas.animate`/`filters.animate`), shake/animation effects, or ticker-based animations built on PixiJS. Covers the `canvas` singleton exported from `@drincs/pixi-vn`. For UI layers (HTML or PixiJS) mounted on top of the canvas, see `pixi-vn-ui` instead.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

DRincs-Productions/pixi-vn1522026년 10월 15일 업데이트

Use when implementing "go back" / "rewind" / step-navigation features in a Pixi'VN game, or when you need to read the player-facing dialogue/choice history (e.g. for a backlog or "history log" UI) — covers the History module's `stepHistory` API and the "step" vs "paragraph" goBackMode.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

DRincs-Productions/pixi-vn1522026년 10월 15일 업데이트

Use when upgrading an existing @drincs/pixi-vn project to a newer version, or when a runtime/type error matches a known breaking change. Covers every breaking change between versions with before/after code.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

DRincs-Productions/pixi-vn1522026년 10월 15일 업데이트

Use when adding a minigame (snake, quiz, puzzle, timing/reaction game, ...) to a Pixi'VN visual novel — the `useMinigame` lifecycle hook pattern, using PixiJS directly on its own layer instead of `gameLayer`, hotkeys for controls, linking the minigame to a route, and why saving/restoring minigame state is the developer's own responsibility.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

DRincs-Productions/pixi-vn1522026년 10월 15일 업데이트

DRincs-Productions의 스킬 모두 보기

이 스킬의 문제 신고