with one click
vue-phaserjs
// Esposter vue-phaserjs integration patterns — component inventory, markRaw for Phaser objects in Pinia stores, configuration Pick pattern. Apply when writing Phaser game objects, stores, or vue-phaserjs components.
// Esposter vue-phaserjs integration patterns — component inventory, markRaw for Phaser objects in Pinia stores, configuration Pick pattern. Apply when writing Phaser game objects, stores, or vue-phaserjs components.
[HINT] Download the complete skill directory including SKILL.md and all related files
| name | vue-phaserjs |
| description | Esposter vue-phaserjs integration patterns — component inventory, markRaw for Phaser objects in Pinia stores, configuration Pick pattern. Apply when writing Phaser game objects, stores, or vue-phaserjs components. |
All game object components follow the same 4-file pattern: {Name}Configuration.ts, {Name}EventEmitsOptions.ts, {Name}SetterMap.ts, {Name}.vue.
Sprites / Images: <Sprite>, <Image>, <NineSlice>, <TileSprite>, <PathFollower>
Text: <Text>, <BitmapText>
Shapes (all extend ShapeConfiguration): <Arc>, <Circle>, <Rectangle>, <Ellipse>, <Line>, <Triangle>, <Polygon>, <Star>, <Curve>, <IsoBox>, <IsoTriangle>
Effects / Rendering: <Graphics>, <RenderTexture>, <Particles>, <Video>
Structure: <Container>, <Zone>, <Tilemap>, <Scene>
Not implemented: <Group> (use v-for), <Layer> (incompatible with useInitializeGameObject), input composables (use onUpdate directly), useTimeline/physics/camera/tilemap-layer components.
Pick from Game Object TypesWhen a configuration interface re-declares properties that exist on the Phaser game object, use Pick<GameObjects.X, "prop1" | "prop2"> in extends instead of re-declaring each property individually:
// ✅ Correct
export interface ArcConfiguration
extends ShapeConfiguration, Pick<GameObjects.Arc, "closePath" | "endAngle" | "radius" | "startAngle"> {}
// ❌ Wrong — redundant re-declaration
export interface ArcConfiguration extends ShapeConfiguration {
closePath: GameObjects.Arc["closePath"];
endAngle: GameObjects.Arc["endAngle"];
// ...
}
Keep explicit declarations only for Parameters<GameObjects.X["method"]> tuples and plain primitives (number, string) that are constructor args without a matching readable property.
SetterMap types the inner setter function as returning void. When the setter body is a single method call that returns a value (Phaser fluent API), wrap it in braces — never use the void operator:
// ✅ Correct
x: (gameObject) => (value) => { gameObject.setX(value); },
// ❌ Wrong — void operator banned
x: (gameObject) => (value) => void gameObject.setX(value),
Multi-line setters already use braces naturally — no change needed.
Always use markRaw() when assigning a Phaser object to any reactive ref in a Pinia store.
Pinia devtools traverse store state via Vue's traverse. Phaser 3.85+ Frame.get glTexture() returns null before WebGL upload — crash in dev when traversed. markRaw(obj) sets __v_skip = true to skip traversal.
// ✅ Correct — traverse-safe
controls.value = markRaw(new KeyboardControls(scene));
controls.value = markRaw(new JoystickControls());
sprite.value = markRaw(newSprite);
pathFollower.value = markRaw(newPathFollower);
tilemap.value = markRaw(newTilemap);
volumeSlider.value = markRaw(useSlider(scene, rectangle, { ... }));
// ❌ Wrong — will crash in dev mode via traverse → Frame.get glTexture → null
controls.value = new KeyboardControls(scene);
sprite.value = newSprite;
Any Phaser class that chains to Scene → TextureManager → Texture → Frame → glTexture is a risk:
GameObjects.* (Sprite, Image, PathFollower, Container, Text, etc.)Tilemaps.Tilemap, Tilemaps.TilemapLayerInput.Keyboard.Key, Input.Keyboard.CursorKeys (via KeyboardPlugin → Scene)scene referenceProblem: vue-phaserjs/dist/index.js bundled phaser4-rex-plugins subpath imports (e.g. phaser4-rex-plugins/plugins/clickoutside.js). The bundled code accesses Phaser.Scene, Phaser.Game, etc. as globals at module-evaluation time, which fails in Node.js SSR.
Root cause: The external array used string literal "phaser4-rex-plugins" which only matches the root package name, not subpath imports like "phaser4-rex-plugins/plugins/clickoutside.js".
Fix (packages/vue-phaserjs/vite.config.js): Use a RegExp to match all subpaths:
rolldownOptions: {
external: ["phaser", /^phaser4-rex-plugins/, "pinia", "vue"],
},
Rule: In Rolldown external, always use /^package-name/ (regex) instead of "package-name" (string) when the package may be imported via subpaths. String literals only match exact module IDs.