원클릭으로
phaser-gamedev
// Build 2D browser games with Phaser 3 (JS/TS): scenes, sprites, physics (Arcade/Matter), tilemaps (Tiled), animations, input. Trigger: 'Phaser scene', 'Arcade physics', 'tilemap', 'Phaser 3 game'.
// Build 2D browser games with Phaser 3 (JS/TS): scenes, sprites, physics (Arcade/Matter), tilemaps (Tiled), animations, input. Trigger: 'Phaser scene', 'Arcade physics', 'tilemap', 'Phaser 3 game'.
Write player-focused, SEO-optimized blog posts for PixelDen. Use for any new article in content/blog/. Hard rule: NO technical/devlog content (no tech stack, code, internals, file paths, framework names). Audience is players, not developers.
Generate game assets via Google Gemini API and process them into final sprite files. Use when creating new game sprites, generating concept art, or running the asset pipeline (Gemini image generation, sprite sheets, Imagen).
Generate pixel-art tilesets and sprites via PixelLab MCP. Use when creating sidescroller platform tiles, top-down Wang tilesets, or other pixel-art assets through the PixelLab MCP server.
Use when the user wants to clean halo fragments or imperfect alpha from native-resolution pixel art sprites (PixelLab, Aseprite, or handmade). Invokes the Node/TypeScript CLI at scripts/pixelart-cleanup. Do NOT trigger for upscaled AI pixel art where one logical pixel spans many real pixels — that needs grid detection instead.
Post code review verdicts to Jira. Use after a code review is done and the user wants to sync the result to Jira. Triggers on: "post to Jira", "update the ticket", "sync review to Jira", or when a review is complete and Jira context is available (project key, issue number, or Atlassian MCP connected). Posts a concise verdict comment, flags blockers, and suggests workflow transitions.
Sandwich-structured code review for PRs, MRs, diffs, or pasted code. Triggers on: "review this", "check my PR/MR", "what do you think of this code", "is this OK to merge", or any paste of code/diff where the user wants feedback. Produces motivating reviews that clearly separate blockers from nitpicks. Always use this skill — even for quick reviews. The structure matters.
| name | phaser-gamedev |
| description | Build 2D browser games with Phaser 3 (JS/TS): scenes, sprites, physics (Arcade/Matter), tilemaps (Tiled), animations, input. Trigger: 'Phaser scene', 'Arcade physics', 'tilemap', 'Phaser 3 game'. |
Build 2D browser games using Phaser 3's scene-based architecture and physics systems.
Read read-before-implement.md first. Three-layer reading order (skill docs → target game → shared helpers) and the pre-implementation output checklist. Skipping this is the #1 source of invented APIs and wrong assumptions.
Before merging to main, run the relevant checks in pre-release-audit.md — 7 reactive error signatures + 7 proactive consistency checks (asset keys, scene registration, reloadDocument on game links, touch-controls cleanup, viewport restart guard).
When reaching for abstraction, consult game-patterns.md first — it catalogs composition patterns (BehaviorManager, FSM, TurnManager, WaveManager, CardManager, DialogueManager) with explicit "when to extract" vs "stay ad-hoc" signals. Prevents premature abstraction.
Read spritesheets-nineslice.md FIRST.
Spritesheet loading is fragile—a few pixels off causes silent corruption that compounds into broken visuals. The reference file contains the mandatory inspection protocol.
Quick rules (details in reference):
spacing: N in loader configimageWidth = (frameWidth × cols) + (spacing × (cols - 1))Read these BEFORE working on the relevant feature:
| When working on... | Read first |
|---|---|
| Starting a new game, or editing a scene you haven't touched recently | read-before-implement.md |
| Pre-merge / pre-release audit, or diagnosing a runtime error | pre-release-audit.md |
| Deciding whether to extract an FSM / BehaviorManager / TurnManager / WaveManager / CardManager / DialogueManager | game-patterns.md |
| Loading ANY spritesheet | spritesheets-nineslice.md |
| Nine-slice UI panels | spritesheets-nineslice.md |
| Config, scenes, objects, input, animations | core-patterns.md |
| Tiled tilemaps, collision layers | tilemaps.md |
| Physics tuning, groups, pooling | arcade-physics.md |
| Scrolling platforms, custom body sizes | arcade-physics.md (Static vs Dynamic section) |
| Performance issues, object pooling | performance.md |
| Keyboard input, SPACE key, canvas focus | keyboard-input.md |
| Raycasting (Wolf3D-style FPS), doors, transparent gates, billboard sprites, enemy collision, secret walls, wall-mounted decals, torch lighting, fisheye fix, texture clipping, weapon sprites, enemy animation, level data integrity | raycasting.md |
| Procedural dungeon generation, BSP splitting, corridor carving, door placement, wall-door-wall, level validation | procedural-dungeon-gen.md |
Before building, decide:
| System | Use When |
|---|---|
| Arcade | Platformers, shooters, most 2D games. Fast AABB collisions |
| Matter | Physics puzzles, ragdolls, realistic collisions. Slower, more accurate |
| None | Menu scenes, visual novels, card games |
delta for motion and timers; avoid frame countingWhen textures are larger than display size (e.g. AI-generated assets), body.setSize() is multiplied by sprite scale!
WRONG — body becomes tiny (5px instead of 80px):
sprite.setDisplaySize(80, 12); // scale = 80/1200 = 0.067
sprite.body.setSize(80, 12); // actual body = 80 * 0.067 = 5px!
CORRECT — use FRAME (texture) size, scale applies automatically:
sprite.setDisplaySize(80, 12);
const body = sprite.body as Phaser.Physics.Arcade.Body;
body.setSize(sprite.frame.width, sprite.frame.height); // body = 1200 * 0.067 = 80px ✓
For static bodies (bricks, platforms), also call updateFromGameObject():
brick.setDisplaySize(40, 16);
const body = brick.body as Phaser.Physics.Arcade.StaticBody;
body.setSize(brick.frame.width, brick.frame.height);
body.updateFromGameObject(); // REQUIRED for static bodies!
setAngle() / setRotation() rotates the VISUAL sprite only. The physics body stays axis-aligned (AABB). This means rotated wall sprites have wrong collision shapes.
WRONG — body stays 64×32 horizontal even though sprite is rotated 90°:
wall.setAngle(90);
wall.body.setSize(64, 32); // still horizontal!
CORRECT — separate visual from collision:
// Visual only (no physics)
this.add.image(x, y, "wall").setAngle(90);
// Invisible collision zone
const zone = this.add.zone(x, y, 16, span);
this.physics.add.existing(zone, true); // static body
wallGroup.add(zone);
Split obstacles into separate StaticGroups when different entities need different collision rules:
this.walls = this.physics.add.staticGroup(); // invisible zones
this.trees = this.physics.add.staticGroup(); // visible + physics
// Walls block player only (enemies walk through to enter arena)
this.physics.add.collider(this.player, this.walls);
// Trees block everyone
this.physics.add.collider(this.player, this.trees);
this.physics.add.collider(this.enemyPool, this.trees);
Read keyboard-input.md for the full solution.
Phaser's keyboard only works when canvas has focus. When embedded in a React page, canvas loses focus constantly (user clicks tabs, buttons, scrolls). keyboard.target: window alone does NOT fix this.
Required: Window-level capture-phase listener that re-focuses canvas + re-dispatches the event. Plus addCapture(SPACE) in each scene that uses SPACE.
When drawing Graphics effects (brake lights, nitro flames, etc.) on top of sprite-based game objects:
AI-generated sprites have transparent padding — the visible content is smaller than the PNG canvas. Always crop sprites first (process_sprite with auto-crop) so the PNG edges match the pixel art edges. Otherwise, effects positioned at halfW/halfH will float outside the visible car.
After g.translateCanvas(x, y) + g.rotateCanvas(angle), the local coordinate origin is the sprite center. Position effects relative to the display size (which maps to cropped sprite edges):
const halfW = CAR_DRAW_W / 2;
const halfH = CAR_DRAW_H / 2;
// Brake lights at rear corners (car body is ~55% of canvas width)
const sideX = halfW * 0.38; // actual car edge, NOT halfW
const rearY = halfH - 4; // just inside rear edge
If a sprite faces south but angle=0 means north: add Math.PI to sprite rotation, but keep Graphics rotateCanvas(car.angle) as-is (Graphics follow physics convention, sprite gets the visual flip).
| Anti-Pattern | Problem | Solution |
|---|---|---|
Global state on window | Scene transitions break state | Use scene data, registries |
Loading in create() | Assets not ready when referenced | Load in preload(), use Boot scene |
| Frame counting | Game speed varies with FPS | Use delta / 1000 |
| Matter for simple collisions | Unnecessary complexity | Arcade handles most 2D games |
| One giant scene | Hard to extend | Separate gameplay/UI/menus |
| Magic numbers | Impossible to balance | Config objects, constants |
| No object pooling | GC stutters | Groups with setActive(false) |
body.setSize(displayW, displayH) on scaled sprites | Body is multiplied by scale → tiny | Use body.setSize(frame.width, frame.height) |
StaticBody + setSize() + updateFromGameObject() | updateFromGameObject() overwrites custom size every frame | Use immovable dynamic body (setImmovable(true)) — see arcade-physics.md |
setAngle() on physics sprites | Visual rotates but body stays axis-aligned (AABB) | Use invisible zones for collision, sprites for visual only |
Constant setAlpha(0.5) for invincibility | Character looks broken/ghostly | Use blink tween (alpha 1↔0.3, duration 80, yoyo) |
Loading non-existent sprites in preload() | Silent 404 errors on every game load | Only load files that exist; use !textures.exists() + programmatic fallback in create() |
2500 individual add.image() for tile grid | 2500 game objects = slow | Use RenderTexture — draw all tiles onto one texture |
Outputs should vary based on:
Phaser provides powerful primitives—scenes, sprites, physics, input—but architecture is your responsibility.
Think in systems: define the scenes, define the entities, define their interactions—then implement.
Codex can build complete, polished Phaser games. These guidelines illuminate the path—they don't fence it.