---
name: randallliuxin/project-scaffold
source: https://app.decimal.ai/s/randallliuxin-project-scaffold@1/SKILL.md
source_sha256: a6bfce3f423c
---

# Project Scaffold

$ARGUMENTS

Generate a new Godot game project with GodotMaker's ECS architecture.
Read templates from this skill's `templates/` directory, fill `{{placeholders}}`
with values from the game plan or user input, and write results to the target.

## Step 1 — Gather Variables

If a confirmed Game Plan exists in the conversation (from game-planner), extract
variables from it. Otherwise, ask the user for **game name** and **genre** at
minimum — use genre defaults for everything else.

| Variable | Source | Default |
|----------|--------|---------|
| `{{game_name}}` | user input, snake_case, used as directory name | *required* |
| `{{game_title}}` | user input or Title Case of game_name | *required* |
| `{{genre}}` | Game Plan "Genre" or user | `"platformer"` |
| `{{perspective}}` | Fixed framework target | `"2D"` |
| `{{viewport_width}}` | genre defaults table | `1280` |
| `{{viewport_height}}` | genre defaults table | `720` |
| `{{rendering_method}}` | fixed 2D renderer | `"gl_compatibility"` |
| `{{root_node_type}}` | fixed 2D root | `"Node2D"` |
| `{{camera_type}}` | fixed 2D camera | `"Camera2D"` |
| `{{game_description}}` | Game Plan summary or one-line from user | genre name + " game" |

**Genre defaults:**

| Genre | Viewport | Gravity | Input Actions |
|-------|----------|---------|---------------|
| Platformer | 1280x720 | 980.0 | move_left, move_right, jump |
| Top-down | 1280x720 | none | move_up, move_down, move_left, move_right |
| Puzzle | 1280x720 | none | select, confirm, cancel |
| Endless runner | 720x1280 | 980.0 | jump |

## Step 2 — Create Directory Structure

Create the project root and all subdirectories:

```
{{game_name}}/
├── project.godot
├── CLAUDE.md
├── .gitignore
├── src/
│   ├── components/          # C_ prefixed component scripts
│   ├── systems/             # NameSystem scripts
│   ├── entities/            # Entity scene definitions
│   └── ui/                  # UI scenes and scripts
├── scenes/
│   ├── main.tscn            # Entry point scene
│   └── game_world.tscn      # Gameplay scene with camera
├── test/
│   └── test_example.gd      # gdUnit4 test template
├── e2e/
│   └── conftest.py           # E2E test config (GODOT_PROJECT = "..")
├── assets/
│   ├── sprites/
│   ├── audio/
│   ├── fonts/
│   └── ui/
├── references/              # Scene reference images (generated by /gm-asset)
└── addons/                  # gecs + gdUnit4 + godot_e2e installed here
```

## .tscn Generation Rules

When writing `.tscn` files (from templates or manually), follow these rules strictly:

1. **No UID references** — use `res://path/to/script.gd` paths, never `uid://xxx`. UID references fail in headless/CI environments because `uid_cache.bin` cannot be rebuilt.
2. **load_steps formula** — `load_steps = ext_resource_count + sub_resource_count + 1`. Count carefully; mismatch causes parse errors.
3. **parent attribute required** — only the root `[node]` omits `parent`. Every other node MUST have `parent="."` (direct child of root) or `parent="path/to/parent"`.
4. **World node setup** — always set `system_nodes_root = NodePath(".")` and `entity_nodes_root = NodePath(".")` when using SystemGroups.

## Step 3 — Fill Templates

Read each template from `templates/`, replace all `{{placeholders}}`, write output.
Remove any template comments (lines starting with `; TEMPLATE:`) from the output.

| Template | Output Path | Notes |
|----------|------------|-------|
| `project.godot.tmpl` | `project.godot` | Must be valid Godot ConfigFile |
| `claude.md.tmpl` | `CLAUDE.md` | Fill game info + ECS reference |
| `gitignore.tmpl` | `.gitignore` | Use as-is, no placeholders |
| `main_scene.tmpl` | `scenes/main.tscn` | Use a 2D root node |
| `world_scene.tmpl` | `scenes/game_world.tscn` | Use 2D node + camera types; gameplay scene gets a World child node added during `/gm-build` |
| `test_example.tmpl` | `test/test_example.gd` | Replace `{{GameNamePascal}}` |
| `component.tmpl` | `src/components/c_example.gd` | Example stub |
| `system.tmpl` | `src/systems/example_system.gd` | Example stub |

### gecs World setup

gecs v7.1.0 ships `class_name World` in `addons/gecs/ecs/world.gd`. The
canonical scene-node pattern:

- Add a `Node` child to your gameplay scene with
  `script = res://addons/gecs/ecs/world.gd`, named `World`, with
  `system_nodes_root = NodePath("Systems")` and
  `entity_nodes_root = NodePath("Entities")`.
- The main scene script wires it up:
  `@onready var world: World = $World` then `ECS.world = world` in `_ready()`.
- Drive systems via `world.process(delta, "gameplay")` /
  `world.process(delta, "physics")` from `_process` / `_physics_process`.

Scaffold leaves the World node out — gameplay scene structure is filled in
during `/gm-build`. See the gecs skill for the full World API.

### Game Plan ECS stubs

If the Game Plan includes an ECS Architecture section with components and systems:

- **Components**: for each planned component (e.g., `C_Velocity`), create a file in
  `src/components/` based on `component.tmpl`. Fill the class name and add `@export`
  vars from the plan.
- **Systems**: for each planned system (e.g., `MovementSystem`), create a file in
  `src/systems/` based on `system.tmpl`. Fill the class name and `query()` with
  the required components.
- **World**: register systems by adding them as children of the gameplay
  scene's `World` node — see "gecs World setup" above for the full pattern.

## Step 4 — Genre Adaptations

After writing base template files, apply genre-specific modifications to `project.godot`:

**Platformer / Endless runner** (gravity games):
- Add `[physics]` section: `2d/default_gravity=980.0`
- Add `[input]` section with move + jump actions

**Top-down**:
- No `[physics]` section
- Add `[input]` section with 4-directional movement

For input action serialization format, read `references/project_settings.md`
— it has the exact Object() syntax and key code table.

## Step 5 — Post-Scaffold Steps

After all files are written, tell the user what to do next:

1. **Deploy GodotMaker skills** to the new project:
   ```bash
   bash <godotmaker_repo>/shell/publish.sh {{game_name}}/
   ```
   This copies skills to `.claude/skills/`, creates `godotmaker.yaml`
   (prompts for Godot executable path on first run), and creates
   `.godotmaker/config.yaml` with default project settings.

2. **Install addons** — pick the row matching your Godot version from the
   active runtime's `config/addon_versions.json` (the source of truth for repo,
   tag, and install path), then install addon-only directories:
   - gecs -> `addons/gecs/`
   - gdUnit4 -> `addons/gdUnit4/`
   - godot_e2e -> `addons/godot_e2e/`

   `tools/check_project.py --e2e` and the gm-* skills expect those
   exact paths (note `gdUnit4/` is capital U — matches the upstream
   repo layout). See `references/addons.md` for the step-by-step
   installation contract.

3. **Register godot-mcp** for runtime debugging:
   ```bash
   claude mcp add godot -e GODOT_PATH="<godot_path>" -- npx @coding-solo/godot-mcp
   ```

4. **Verify** with headless-build: `godot --headless --quit 2>&1`

## Report

After scaffold completes, output:
- Project path (absolute)
- File tree listing
- Checklist of pending post-scaffold steps

## What This Skill Does NOT Do

- Does not implement game logic — only creates the starting structure
- Does not install addons — provides instructions in references/addons.md
- Does not run game-planner — expects a plan or minimal user input already
- Does not deploy skills — that is publish.sh's job