AgentSkillsCN

scene

创建或修改游戏关卡,精心设计场景环境、装饰元素与道具陈设

SKILL.md
--- frontmatter
name: scene
description: Create or modify a game level with environment, decorations, and props
argument-hint: "[games/<slug>] <N> <description>" or "[games/<slug>] <N> modify <changes>"
user-invocable: true
allowed-tools: Read, Bash, Glob, Grep, Write, Edit
agent: scene-builder
context: fork

Scene Skill — Create/Edit Game Levels

You are the scene builder for Three.js + WebXR games.

CRITICAL: DO NOT STOP UNTIL THE JOB IS DONE

Complete ALL steps in a SINGLE run. NEVER use AskUserQuestion. NEVER stop early.

First Step: Read CLAUDE.md

Read the CLAUDE.md file in the repo root to understand project structure, conventions, and paths.

Parsing Arguments

Game Directory Detection

The first argument may be a game path (starts with games/):

  • With path: /scene games/my-game 2 una spiaggia tropicale
  • Without path: /scene 2 una spiaggia tropicale → auto-detect by finding the most recently modified games/*/GAME_DESIGN.md

After resolving the game directory, all file paths are relative to it.

Two syntaxes:

CREATE syntax (generates level from scratch, overwrites if exists):

code
/scene [games/<slug>] <N> <environment-description> [decoration1, decoration2, ...]

EDIT syntax (modifies existing level — keyword modify after number):

code
/scene [games/<slug>] <N> modify <changes-to-make>

How to decide mode

If the word modify appears right after the level number → EDIT MODE. Otherwise → CREATE MODE (even if the level already exists — it will be overwritten).

CREATE examples:

  • /scene games/my-game 1 una spiaggia [palme, mare, scogli, uccelli]
  • /scene 2 foresta incantata
  • /scene games/crystal-forest 3 grotta oscura [stalattiti, funghi, cristalli]
  • /scene 1 a serene mountain lake surrounded by pine trees

EDIT examples:

  • /scene games/my-game 2 modify make the sky a sunset
  • /scene 2 modify add more palm trees
  • /scene 1 modify cambia il cielo in un tramonto

Context Awareness

If GAME_DESIGN.md exists in the game directory, read it first to understand the game's art direction and this level's role in the overall design.

CREATE MODE

Step A1: Parse

Extract: level number N, environment description, optional [decorations].

Step A2: Discover models

Run ls <game-dir>/public/models/N/ for .glb files. If empty/missing, that's fine — level will have no GLB props.

Step A3: Circle layout for GLB props

radius = max(3, numberOfModels * 1.2). Indoor: min(radius, roomWidth/2 - 1).

Step A4: Design environment

Decide OUTDOOR or INDOOR:

  • INDOOR: house, apartment, room, interior, casa, interno, stanza, cave, grotto, grotta, tunnel, mine, spaceship, station, lab, office, temple, church, dungeon
  • OUTDOOR: beach, forest, desert, mountain, garden, field, snow, volcano, swamp, ocean, ruins, park, spiaggia, foresta, deserto, montagna

OUTDOOR: background (hex string) + sky + ground + directional + hemisphere + fog + particles INDOOR: enclosure + pointLights/spotLights + hemisphere + fog + particles

Sky shader fields: sky.topColor, sky.bottomColor (required). Optional: sky.offset (float, default 0) and sky.exponent (float, default 1).

Step A5: Design decorations

If user provided [...]: map each item to types. If not: use defaults for the environment.

typeRendersParams
palmTreePalm treecount, radius: [min,max], height: [min,max]
treeDeciduous treecount, radius: [min,max], height: [min,max], canopyColor
pineTreePine treecount, radius: [min,max], height: [min,max]
rockRockcount, radius: [min,max], scale: [min,max], color
waterWater planey, color, opacity, size
birdCircling birdcount, height: [min,max], speed
stalactiteCeiling conecount, length: [min,max], color
mushroomGlowing mushroomcount, radius: [min,max], color, glowColor
crystalCrystal clustercount, radius: [min,max], color, glowIntensity
coralCoral shapecount, radius: [min,max], color
vineHanging vinecount, length: [min,max], color
lanternFloating lanterncount, height: [min,max], color
columnColumncount, radius: [min,max], color

Natural language → type: palme→palmTree, alberi→tree, pini→pineTree, rocce/scogli→rock, mare/oceano/acqua→water, uccelli/gabbiani→bird, stalattiti→stalactite, funghi→mushroom, cristalli→crystal, coralli→coral, liane/viti→vine, lanterne/luci→lantern, colonne/pilastri→column, conchiglie→rock (small)

Defaults: Beach: palmTree,rock,water,bird | Forest: tree,rock,mushroom,bird | Cave: stalactite,crystal,mushroom,rock | Snow: pineTree,rock | Futuristic: column,crystal | Space: column,crystal,lantern | Underwater: coral,crystal,rock | Garden: tree,rock,bird,lantern | Temple: column,crystal,lantern,vine | Generic outdoor: tree,rock,bird | Generic indoor: column,crystal,lantern

ALWAYS 3-5+ decoration entries.

Step A6: Write level file

Write to <game-dir>/src/levels/levelN.js:

js
export default {
  id: N,
  name: 'Descriptive Name',
  environment: { /* from A4 */ },
  decorations: [ /* from A5 */ ],
  props: [ /* GLB models from A3 */ ],
  playerSpawn: { position: [0, 0, R], rotationY: Math.PI },
  exit: { position: [0, 0, -R], targetLevel: N+1, label: 'Next Level' }, // omit for last level
};

Exit field: If this is NOT the last level, add an exit to place a portal. The engine renders a glowing ring and triggers a fade transition on proximity (1.8m). Place the portal opposite to playerSpawn. For the final level, omit exit entirely. For branching paths, use an array of exits.

Step A7: Done

Print: Level N created! Open https://localhost:5173/?level=N and refresh.


EDIT MODE (level already exists)

Step B1: Read existing level

Read the full content of <game-dir>/src/levels/levelN.js.

Step B2: Understand the modification

Interpret the user's natural language request.

Step B3: Apply changes

Use Edit tool to modify ONLY the relevant parts of the level file. Do NOT rewrite the entire file.

IMPORTANT about prop scale: The LevelLoader auto-grounds props (computes bounding box, sets Y so bottom touches floor). Changing scale in the config is safe — no Y adjustment needed.

Step B4: Done

Print: Level N updated! Refresh https://localhost:5173/?level=N to see changes.


NEVER use AskUserQuestion. Just do the work and print the result.