summer-engine 1.3.0 → 1.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +67 -0
- package/README.md +7 -2
- package/dist/bin/summer.js +24 -1
- package/dist/commands/doctor.js +44 -20
- package/dist/commands/setup.js +77 -6
- package/dist/commands/skills.js +5 -3
- package/dist/lib/format.d.ts +23 -0
- package/dist/lib/format.js +60 -0
- package/dist/lib/format.test.d.ts +1 -0
- package/dist/lib/format.test.js +36 -0
- package/dist/lib/skills-registry.d.ts +16 -0
- package/dist/lib/skills-registry.js +35 -0
- package/package.json +5 -3
- package/skills/_meta/skill-test/SKILL.md +1 -1
- package/skills/_shared/summer-folder.md +44 -0
- package/skills/_tests/specs/art-direction.md +115 -0
- package/skills/_tests/specs/audio-direction.md +123 -0
- package/skills/_tests/specs/brainstorm-game.md +99 -0
- package/skills/_tests/specs/debug.md +94 -0
- package/skills/_tests/specs/design-level.md +113 -0
- package/skills/_tests/specs/design-mechanic.md +112 -0
- package/skills/_tests/specs/design-npc.md +112 -0
- package/skills/_tests/specs/export-and-ship.md +114 -0
- package/skills/_tests/specs/fps-controller.md +5 -5
- package/skills/_tests/specs/play.md +88 -0
- package/skills/_tests/specs/setup-multiplayer.md +106 -0
- package/skills/_tests/specs/tune-performance.md +104 -0
- package/skills/_tests/specs/vfx.md +98 -0
- package/skills/ai-and-npcs/design-npc/SKILL.md +343 -0
- package/skills/asset-pipeline/asset-strategy/SKILL.md +1 -0
- package/skills/audio/audio-direction/SKILL.md +258 -0
- package/skills/catalog.yaml +13 -3
- package/skills/character-controllers/fps-controller/SKILL.md +1 -0
- package/skills/debugging/debug/SKILL.md +137 -0
- package/skills/deployment/export-and-ship/SKILL.md +300 -0
- package/skills/gameplay-mechanics/design-mechanic/SKILL.md +265 -0
- package/skills/level-design/design-level/SKILL.md +266 -0
- package/skills/multiplayer-and-networking/setup-multiplayer/SKILL.md +328 -0
- package/skills/performance/tune-performance/SKILL.md +219 -0
- package/skills/rendering-and-lighting/3d-lighting/SKILL.md +1 -0
- package/skills/rendering-and-lighting/art-direction/SKILL.md +284 -0
- package/skills/scene-and-project/brainstorm-game/SKILL.md +210 -0
- package/skills/scene-and-project/make-game/SKILL.md +1 -0
- package/skills/scene-and-project/play/SKILL.md +108 -0
- package/skills/scene-and-project/scene-composition/SKILL.md +1 -0
- package/skills/scripting-patterns/gdscript-patterns/SKILL.md +1 -0
- package/skills/ui-and-ux/ui-basics/SKILL.md +1 -0
- package/skills/visual-effects/vfx/SKILL.md +280 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: brainstorm-game
|
|
3
|
+
description: Use when the user wants help deciding what game to make, scoping a new project, or turning a vague idea into a buildable plan. Walks through genre, scope, core loop, mechanics, and art direction, then writes a 1-page brief to `.summer/GameSoul.md`. Trigger on "brainstorm a game", "what should I make", "I want to make a game", "help me scope", "new game idea".
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: [Cursor, Claude Code, Windsurf, Codex]
|
|
6
|
+
category: scene-and-project
|
|
7
|
+
user-invocable: true
|
|
8
|
+
allowed-tools: Read Grep Glob Write Edit summer_get_project_context summer_get_scene_tree
|
|
9
|
+
paths: [".summer/**", "project.godot", "**/*.md"]
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# /brainstorm-game — Decide What Game to Build
|
|
13
|
+
|
|
14
|
+
## Overview
|
|
15
|
+
|
|
16
|
+
Most game projects fail because they were never scoped. This skill turns "I want to make a game" into a 1-page brief that names the pitch, the core loop, three mechanics max, the art direction, and the scope (jam / vertical slice / full game). The brief lands at `.summer/GameSoul.md` — the file Summer's onboarding pipeline and every future `summer:` skill reads on first turn.
|
|
17
|
+
|
|
18
|
+
**Core principle:** Constrain ruthlessly. A buildable bad idea beats an un-buildable great idea. Three mechanics, one art direction, one scope. Anything else gets parked in a "Later" list.
|
|
19
|
+
|
|
20
|
+
## Steps
|
|
21
|
+
|
|
22
|
+
### 1. Open with the one question
|
|
23
|
+
|
|
24
|
+
Ask exactly this and wait:
|
|
25
|
+
|
|
26
|
+
> One sentence — what kind of game do you want to make?
|
|
27
|
+
|
|
28
|
+
Don't pre-pitch genres. Don't list options. Read what they say. The first sentence is signal.
|
|
29
|
+
|
|
30
|
+
If they freeze ("I don't know"), offer 4 reference points only:
|
|
31
|
+
|
|
32
|
+
> No worries. Pick the one that pulls you most: a) something cozy and exploratory (Stardew, A Short Hike), b) something tight and skill-based (Celeste, Hades), c) something story-led (Disco Elysium, Citizen Sleeper), d) something silly and short (Goat Sim, Untitled Goose). Or describe a different vibe.
|
|
33
|
+
|
|
34
|
+
### 2. Branch on genre signal
|
|
35
|
+
|
|
36
|
+
Read their answer for a genre anchor and pick **one** branch. Don't ask multiple questions in parallel.
|
|
37
|
+
|
|
38
|
+
| Anchor word(s) | Branch |
|
|
39
|
+
|---|---|
|
|
40
|
+
| "shooter", "FPS", "TPS", "combat" | Action-Combat branch |
|
|
41
|
+
| "platformer", "metroidvania", "souls-like" | Skill-Platforming branch |
|
|
42
|
+
| "RPG", "story", "narrative", "dialogue" | Narrative branch |
|
|
43
|
+
| "roguelike", "deckbuilder", "auto-battler" | Run-Based branch |
|
|
44
|
+
| "sim", "cozy", "farming", "city-builder" | Sim branch |
|
|
45
|
+
| "puzzle", "physics", "sandbox" | Puzzle branch |
|
|
46
|
+
| "horror", "atmospheric" | Horror branch |
|
|
47
|
+
| "multiplayer", "PvP", "co-op" | Multiplayer branch (forces the multiplayer-or-not call before scope) |
|
|
48
|
+
|
|
49
|
+
If unclear, ask the smallest disambiguating question:
|
|
50
|
+
|
|
51
|
+
> Tighter on this — is it more "tense and dangerous" or "calm and exploratory"?
|
|
52
|
+
|
|
53
|
+
### 3. Pin the core loop
|
|
54
|
+
|
|
55
|
+
A core loop is what the player does for 30 seconds, repeated. State a candidate and ask them to refine. Examples per branch:
|
|
56
|
+
|
|
57
|
+
| Branch | Candidate core loop |
|
|
58
|
+
|---|---|
|
|
59
|
+
| Action-Combat | "Enter room, read enemy patterns, kill, loot, advance." |
|
|
60
|
+
| Skill-Platforming | "See obstacle, attempt, fail, learn, retry, clear, next." |
|
|
61
|
+
| Narrative | "Walk to character, choose dialogue, see consequence, walk to next." |
|
|
62
|
+
| Run-Based | "Pick a card/weapon/build, fight, die, unlock, repeat with edge." |
|
|
63
|
+
| Sim | "Plan day, execute, harvest reward, plan next day." |
|
|
64
|
+
| Puzzle | "See state, form theory, test, observe, solve or rethink." |
|
|
65
|
+
| Horror | "Hear cue, locate threat, hide or flee, advance space." |
|
|
66
|
+
|
|
67
|
+
State your candidate. Ask:
|
|
68
|
+
|
|
69
|
+
> The 30-second loop I'm hearing: <candidate>. Does that match what you have in your head, or is the actual moment-to-moment different?
|
|
70
|
+
|
|
71
|
+
### 4. Constrain to three mechanics maximum
|
|
72
|
+
|
|
73
|
+
Force the trade. State the rule:
|
|
74
|
+
|
|
75
|
+
> Three mechanics, no more. What are the three things this game has that other games in this genre don't?
|
|
76
|
+
|
|
77
|
+
If they pitch five, push back. Not "we'll add it later" — actually cut. Two examples for them to react to:
|
|
78
|
+
|
|
79
|
+
> Hades has three: dash-attack-cast, room reward choice, and run-end conversation. Everything else (mirror upgrades, weapon variants, bosses) is content built on those three. What are yours?
|
|
80
|
+
|
|
81
|
+
Common new-dev mistake: listing features ("inventory, crafting, quests") instead of mechanics ("a single resource that's both currency and ammo"). Push them toward verbs and decisions, not nouns and systems.
|
|
82
|
+
|
|
83
|
+
### 5. Pick an art direction in one phrase
|
|
84
|
+
|
|
85
|
+
Not a paragraph. Examples to anchor:
|
|
86
|
+
|
|
87
|
+
- "Lo-fi PS1 polygons + dithered shader"
|
|
88
|
+
- "Hand-drawn 2D, single bold outline, 4-color palette"
|
|
89
|
+
- "Toon-shaded Nintendo-bright, no realistic lighting"
|
|
90
|
+
- "Pixel art, 16x16 tiles, GameBoy 4-shade green"
|
|
91
|
+
|
|
92
|
+
Ask:
|
|
93
|
+
|
|
94
|
+
> One phrase for the look. If a stranger asked your screenshot's style in 6 words, what do you say?
|
|
95
|
+
|
|
96
|
+
Don't go deeper here — `/summer:art-direction` is where the bible gets built. This is one phrase to seed it.
|
|
97
|
+
|
|
98
|
+
### 6. Pin the scope
|
|
99
|
+
|
|
100
|
+
State the choices flat:
|
|
101
|
+
|
|
102
|
+
> Scope. Pick one: (a) a 4-hour jam-sized prototype — one mechanic, one level, one art direction, ships in a weekend. (b) a vertical slice — 30-60 minutes of polished play that proves the core loop, ships in 2-4 weeks. (c) a full game — 4-20 hours of content, ships in 6+ months.
|
|
103
|
+
|
|
104
|
+
If they flinch at (c), recommend (b). New devs almost always overestimate scope. Default recommendation if they're unsure: **vertical slice**.
|
|
105
|
+
|
|
106
|
+
### 7. Sanity-check the combination
|
|
107
|
+
|
|
108
|
+
Do this in your head, not out loud unless something fails:
|
|
109
|
+
|
|
110
|
+
- Is the core loop achievable with the three mechanics + the chosen art direction in the chosen scope? If not, name what has to flex (usually scope down or merge two mechanics into one).
|
|
111
|
+
- Is there a hidden multiplayer requirement? ("4-player" or "co-op" anywhere = stop, multiplayer is its own scope explosion.)
|
|
112
|
+
- Is the art direction generatable / sourceable for a solo dev? (Photoreal 3D for a 4-week vertical slice is a red flag.)
|
|
113
|
+
|
|
114
|
+
If you spot a problem, raise it once, plainly:
|
|
115
|
+
|
|
116
|
+
> One concern: <X>. Suggest <flex Y>. OK or do you want to push through anyway?
|
|
117
|
+
|
|
118
|
+
### 8. Draft the brief
|
|
119
|
+
|
|
120
|
+
Compose the 1-page brief. Format:
|
|
121
|
+
|
|
122
|
+
```markdown
|
|
123
|
+
# <Game Name or "Untitled">
|
|
124
|
+
|
|
125
|
+
**Pitch:** <One sentence the player would tell a friend.>
|
|
126
|
+
|
|
127
|
+
**Core loop (30s):** <What the player does, repeated.>
|
|
128
|
+
|
|
129
|
+
**Three mechanics:**
|
|
130
|
+
1. <Verb-led: "Parry to convert damage into mana" not "Combat system">
|
|
131
|
+
2. <…>
|
|
132
|
+
3. <…>
|
|
133
|
+
|
|
134
|
+
**Art direction (one phrase):** <…>
|
|
135
|
+
|
|
136
|
+
**Scope:** <Jam | Vertical slice | Full game>
|
|
137
|
+
|
|
138
|
+
**Win condition:** <How the player knows they succeeded — ending? Score? Unlock?>
|
|
139
|
+
|
|
140
|
+
**One thing this is NOT:** <Define by negation. Cuts scope creep later.>
|
|
141
|
+
|
|
142
|
+
**Inspirations:** <2-4 specific games, films, or images.>
|
|
143
|
+
|
|
144
|
+
**Parked for later:** <Things they pitched that didn't make the three.>
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### 9. Ask permission and write
|
|
148
|
+
|
|
149
|
+
**Preferred (Summer MCP + host file write):**
|
|
150
|
+
|
|
151
|
+
> May I create `.summer/GameSoul.md` with this brief?
|
|
152
|
+
|
|
153
|
+
On yes, use `Write` (host file tool) — `.summer/GameSoul.md` is plain markdown, no engine import needed:
|
|
154
|
+
|
|
155
|
+
```
|
|
156
|
+
Write .summer/GameSoul.md
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
If `.summer/GameSoul.md` already exists, read it first, show what would change, ask:
|
|
160
|
+
|
|
161
|
+
> `.summer/GameSoul.md` already exists. May I overwrite it, or merge into a "Revision 2" section below the existing brief?
|
|
162
|
+
|
|
163
|
+
**Fallback (no host write tool — agent is read-only):**
|
|
164
|
+
|
|
165
|
+
Print the full brief to the user with the explicit instruction:
|
|
166
|
+
|
|
167
|
+
> Save this as `.summer/GameSoul.md` in your project root. Every Summer skill reads it.
|
|
168
|
+
|
|
169
|
+
### 10. Route to the next step
|
|
170
|
+
|
|
171
|
+
End every successful brainstorm with a routing question:
|
|
172
|
+
|
|
173
|
+
> Brief saved. Next step:
|
|
174
|
+
> - `/summer:design-mechanic` — design the core loop in detail (recommended for vertical slice).
|
|
175
|
+
> - `/summer:design-level` — sketch level 1 / the tutorial.
|
|
176
|
+
> - `/summer:art-direction` — turn the one-phrase look into an art bible.
|
|
177
|
+
> - Or pick up `/summer:make-game` to start scaffolding the project.
|
|
178
|
+
|
|
179
|
+
Don't auto-pick. Let them decide.
|
|
180
|
+
|
|
181
|
+
## Anti-patterns (don't do these)
|
|
182
|
+
|
|
183
|
+
| Anti-pattern | Why it fails |
|
|
184
|
+
|---|---|
|
|
185
|
+
| Asking 6 questions in a row before any branching | User feels interviewed, not collaborated with. Branch after each answer. |
|
|
186
|
+
| Letting them list 5+ mechanics | Three is the constraint. Without the constraint, the brief is useless. |
|
|
187
|
+
| Skipping the "one thing this is NOT" line | This is the single best scope-protection in the brief. Future "let's add X" gets compared against it. |
|
|
188
|
+
| Writing the brief without showing it first | They have to react to it. They'll catch their own bad fits when they read it back. |
|
|
189
|
+
| Defaulting to "full game" scope | New devs overestimate. Default to vertical slice; let them upgrade if they push. |
|
|
190
|
+
| Pitching a genre they didn't ask for | The first sentence they typed is signal. Don't override it. |
|
|
191
|
+
| Including art direction details | One phrase. Bible-building is `/summer:art-direction`. |
|
|
192
|
+
| Saving without asking | Always ask "May I create `.summer/GameSoul.md`?" — see collaborative-protocol. |
|
|
193
|
+
|
|
194
|
+
## Collaborative protocol
|
|
195
|
+
|
|
196
|
+
This skill writes one file (`.summer/GameSoul.md`). Always ask before writing. Always show the brief inline before saving so the user can react. See `_shared/collaborative-protocol.md`.
|
|
197
|
+
|
|
198
|
+
## Want a working starter?
|
|
199
|
+
|
|
200
|
+
No template — this is a workflow that produces the brief that drives template selection later. Once `.summer/GameSoul.md` exists, `/summer:make-game` can match it against `_shared/template-registry.md`.
|
|
201
|
+
|
|
202
|
+
## See also
|
|
203
|
+
|
|
204
|
+
- `_shared/collaborative-protocol.md` — "May I write?" pattern
|
|
205
|
+
- `_shared/template-registry.md` — templates the brief will be matched against later
|
|
206
|
+
- `gameplay-mechanics/design-mechanic/SKILL.md` — design the core loop in detail
|
|
207
|
+
- `level-design/design-level/SKILL.md` — sketch level 1
|
|
208
|
+
- `rendering-and-lighting/art-direction/SKILL.md` — turn the one-phrase look into a full bible
|
|
209
|
+
- `audio/audio-direction/SKILL.md` — sonic identity
|
|
210
|
+
- `scene-and-project/make-game/SKILL.md` — scaffold the project once the brief is locked
|
|
@@ -4,6 +4,7 @@ description: End-to-end game creation workflow that orchestrates project setup,
|
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: [Cursor, Claude Code, Windsurf, Codex]
|
|
6
6
|
category: scene-and-project
|
|
7
|
+
user-invocable: false
|
|
7
8
|
allowed-tools: Read Grep Glob Write Edit summer_get_project_context summer_get_agent_playbook summer_create_scene summer_open_scene summer_save_scene summer_add_node summer_set_prop summer_set_resource_property summer_instantiate_scene summer_play summer_stop summer_get_diagnostics
|
|
8
9
|
paths: ["**/*.gd", "**/*.tscn", "**/project.godot"]
|
|
9
10
|
---
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: play
|
|
3
|
+
description: Use when the user says "play", "run it", "test it", "let me see it", or "start the game". Runs the project in Summer Engine, briefly waits, then reports what's happening — clean run, errors, or warnings.
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: [Cursor, Claude Code, Windsurf, Codex]
|
|
6
|
+
category: scene-and-project
|
|
7
|
+
user-invocable: true
|
|
8
|
+
allowed-tools: summer_is_running summer_play summer_stop summer_clear_console summer_get_console summer_get_debugger_errors summer_get_script_errors summer_get_diagnostics
|
|
9
|
+
paths: ["**/project.godot", "**/*.tscn"]
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# /play — Run the Game
|
|
13
|
+
|
|
14
|
+
## Overview
|
|
15
|
+
|
|
16
|
+
Press play. Watch what happens. Report it back.
|
|
17
|
+
|
|
18
|
+
## Steps
|
|
19
|
+
|
|
20
|
+
### 1. Check if it's already running
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
summer_is_running
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
If running:
|
|
27
|
+
> The game is already playing. Stop and restart, or leave it?
|
|
28
|
+
|
|
29
|
+
Wait for the user. Don't unilaterally stop.
|
|
30
|
+
|
|
31
|
+
If not running, continue.
|
|
32
|
+
|
|
33
|
+
### 2. Pre-check for compile errors
|
|
34
|
+
|
|
35
|
+
Cheapest call. If the script won't compile, play will fail with a noisy error — better to surface that clearly first.
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
summer_get_script_errors
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
If errors found:
|
|
42
|
+
> Won't run yet — there's a script error: `<one-line summary>`. Want me to /debug it?
|
|
43
|
+
|
|
44
|
+
Stop here on user's no.
|
|
45
|
+
|
|
46
|
+
### 3. Clear and play
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
summer_clear_console
|
|
50
|
+
summer_play
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### 4. Brief wait, then read state
|
|
54
|
+
|
|
55
|
+
Wait ~2 seconds (so the engine has time to process at least one frame). Then:
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
summer_get_diagnostics
|
|
59
|
+
summer_get_debugger_errors
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### 5. Report
|
|
63
|
+
|
|
64
|
+
Three outcomes:
|
|
65
|
+
|
|
66
|
+
**Clean:** `summer_get_diagnostics` shows zero errors and zero warnings.
|
|
67
|
+
|
|
68
|
+
> Running. No errors or warnings. Tell me what you see and I'll dig in if needed.
|
|
69
|
+
|
|
70
|
+
**Warnings only:**
|
|
71
|
+
|
|
72
|
+
> Running. <N> warnings — `<one-line summary of the most important one>`. Want me to look at them?
|
|
73
|
+
|
|
74
|
+
**Runtime error:**
|
|
75
|
+
|
|
76
|
+
> Crashed: `<error class>` at `<file>:<line>` — `<message>`. Want me to /debug?
|
|
77
|
+
|
|
78
|
+
### 6. Don't auto-stop
|
|
79
|
+
|
|
80
|
+
Leave the game running. The user wants to play it. Stop only on explicit ask.
|
|
81
|
+
|
|
82
|
+
## Common patterns
|
|
83
|
+
|
|
84
|
+
| Situation | Right move |
|
|
85
|
+
|---|---|
|
|
86
|
+
| User says "play" with no context | Steps 1–5 above |
|
|
87
|
+
| User says "play and watch it for me" | After step 5, set a timer / poll `summer_get_debugger_errors` every few seconds for the duration the user specifies |
|
|
88
|
+
| User says "play the boss fight scene" | Open the specific scene first (`summer_open_scene "scenes/boss_fight.tscn"`), then steps 1–5 |
|
|
89
|
+
| Game is already running and user says "play" again | Step 1's check covers it — ask before restarting |
|
|
90
|
+
|
|
91
|
+
## Anti-patterns
|
|
92
|
+
|
|
93
|
+
- **Don't** auto-stop a running game just because the user said "play". They probably mean "make sure it's playing", not "stop and restart".
|
|
94
|
+
- **Don't** call `summer_get_console` instead of `summer_get_debugger_errors` for runtime errors. Console has print() output and warnings; debugger errors has the stack traces.
|
|
95
|
+
- **Don't** report "no errors" before waiting for the engine to actually run a frame. A 0-frame snapshot is meaningless.
|
|
96
|
+
|
|
97
|
+
## Collaborative protocol
|
|
98
|
+
|
|
99
|
+
This skill mutates engine state (clearing console, starting play). It's a read-side workflow with one safe write (`summer_play`). No "May I" needed — this is what the user asked for. Do confirm before stopping a running game.
|
|
100
|
+
|
|
101
|
+
## Want a working starter?
|
|
102
|
+
|
|
103
|
+
No template — works against any Godot/Summer project that has a main scene set in `project.godot`.
|
|
104
|
+
|
|
105
|
+
## See also
|
|
106
|
+
|
|
107
|
+
- `_shared/mcp-tools-reference.md` — full tool list
|
|
108
|
+
- `debugging/debug/SKILL.md` — what to do when /play surfaces an error
|
|
@@ -4,6 +4,7 @@ description: How to structure scenes, when to use sub-scenes, node hierarchy con
|
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: [Cursor, Claude Code, Windsurf, Codex]
|
|
6
6
|
category: scene-and-project
|
|
7
|
+
user-invocable: false
|
|
7
8
|
allowed-tools: Read Grep summer_create_scene summer_open_scene summer_get_scene_tree summer_add_node summer_instantiate_scene summer_save_scene
|
|
8
9
|
paths: ["**/*.tscn"]
|
|
9
10
|
---
|
|
@@ -4,6 +4,7 @@ description: Common GDScript idioms — type hints, signals, exports, onready, l
|
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: [Cursor, Claude Code, Windsurf, Codex]
|
|
6
6
|
category: scripting-patterns
|
|
7
|
+
user-invocable: false
|
|
7
8
|
allowed-tools: Read Grep summer_get_script_errors
|
|
8
9
|
paths: ["**/*.gd"]
|
|
9
10
|
---
|
|
@@ -4,6 +4,7 @@ description: HUD, menus, health bars, progress bars, and Control-based UI layout
|
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: [Cursor, Claude Code, Windsurf, Codex]
|
|
6
6
|
category: ui-and-ux
|
|
7
|
+
user-invocable: false
|
|
7
8
|
allowed-tools: Read Grep summer_add_node summer_set_prop summer_connect_signal summer_save_scene
|
|
8
9
|
paths: ["**/*.tscn", "**/*.gd"]
|
|
9
10
|
---
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: vfx
|
|
3
|
+
description: Use when the user says the game "feels flat", "needs juice", "lacks impact", or asks for particles, hit feedback, screen shake, camera juice, muzzle flashes, explosions, or pickup effects. Walks the user through a guided 30-minute polish pass that adds visible feedback to combat, movement, pickups, damage, and casting. Trigger on "vfx", "juice", "feels flat", "polish", "particles", "screen shake", "hit flash", "feedback".
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: [Cursor, Claude Code, Windsurf, Codex]
|
|
6
|
+
category: visual-effects
|
|
7
|
+
user-invocable: true
|
|
8
|
+
allowed-tools: Read Grep Edit Write summer_get_scene_tree summer_inspect_node summer_inspect_resource summer_add_node summer_set_prop summer_set_resource_property summer_connect_signal summer_save_scene summer_get_script_errors summer_play summer_stop
|
|
9
|
+
paths: ["**/*.gd", "**/*.tscn", "**/*.tres"]
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# /vfx — Visible Polish in 30 Minutes
|
|
13
|
+
|
|
14
|
+
A game with bad VFX feels like a tech demo. A game with good VFX feels alive even before the mechanics are deep. This skill is a guided polish pass: you ask the user *what feels flat*, then deliver one or two tight recipes that add immediately-visible feedback. No vague "add particles". Specific node, specific values, specific signal hookup, verified result.
|
|
15
|
+
|
|
16
|
+
## Steps
|
|
17
|
+
|
|
18
|
+
### 1. Ask which thing feels flat
|
|
19
|
+
|
|
20
|
+
Open with one focused question. Wait for the answer.
|
|
21
|
+
|
|
22
|
+
> What feels flat? Pick one to start: combat hits / movement / pickups / damage taken / explosions / spell casts / something else.
|
|
23
|
+
|
|
24
|
+
If the user says "all of it", anchor on **combat hits** — it gives the loudest perceived improvement per minute spent.
|
|
25
|
+
|
|
26
|
+
### 2. Read the current scene state
|
|
27
|
+
|
|
28
|
+
Before adding anything, see what's there.
|
|
29
|
+
|
|
30
|
+
**Preferred (Summer MCP):**
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
summer_get_scene_tree
|
|
34
|
+
summer_inspect_node "./World/Player" # or whichever node owns the action
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
You're looking for: where the trigger lives (e.g. `body_entered` signal on a Hurtbox), whether a Camera3D exists for shake, whether the target has a MeshInstance3D for a flash material override.
|
|
38
|
+
|
|
39
|
+
**Fallback (no MCP):** ask the user to paste the relevant `.tscn` snippet or describe the parent of the actor.
|
|
40
|
+
|
|
41
|
+
### 3. Pick the recipe and propose it explicitly
|
|
42
|
+
|
|
43
|
+
Each recipe is a tight bundle of (node + property + signal + script). Don't mix recipes — add one, see it land, stack later.
|
|
44
|
+
|
|
45
|
+
> I want to add a hit flash + 0.04s camera shake bound to the `damaged` signal on `./World/Enemy`. About 6 lines of GDScript and one Camera3D shake node. May I proceed?
|
|
46
|
+
|
|
47
|
+
### 4. Apply the recipe
|
|
48
|
+
|
|
49
|
+
See **Recipes** below. Each is end-to-end: node tree, property values, signal hookup, GDScript stub.
|
|
50
|
+
|
|
51
|
+
### 5. Verify
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
summer_save_scene
|
|
55
|
+
summer_get_script_errors
|
|
56
|
+
summer_play
|
|
57
|
+
# user reproduces the trigger
|
|
58
|
+
summer_stop
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
If it doesn't read on screen, the values are wrong (flash too dim, shake too short, particle lifetime too long). Tune one knob at a time.
|
|
62
|
+
|
|
63
|
+
## Recipes
|
|
64
|
+
|
|
65
|
+
### Recipe A — Hit flash (mesh albedo flicker)
|
|
66
|
+
|
|
67
|
+
**Goal:** when an enemy takes damage, the mesh flashes white for 80 ms. Single biggest-bang-for-buck VFX. Read instantly even at 60 fps.
|
|
68
|
+
|
|
69
|
+
**Approach:** swap to a flash material via `material_override`, then back. Use Tween for the timing.
|
|
70
|
+
|
|
71
|
+
**MCP path:**
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
summer_add_node(parent="./World/Enemy", type="Node", name="HitFlash")
|
|
75
|
+
# attach script (see below) — script holds reference to MeshInstance3D and a flash StandardMaterial3D
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Script (`scripts/hit_flash.gd`):
|
|
79
|
+
|
|
80
|
+
```gdscript
|
|
81
|
+
class_name HitFlash
|
|
82
|
+
extends Node
|
|
83
|
+
|
|
84
|
+
@export var mesh: MeshInstance3D
|
|
85
|
+
@export var flash_color: Color = Color(1.0, 1.0, 1.0)
|
|
86
|
+
@export var duration: float = 0.08
|
|
87
|
+
|
|
88
|
+
var _flash_mat: StandardMaterial3D
|
|
89
|
+
var _orig_mat: Material
|
|
90
|
+
|
|
91
|
+
func _ready() -> void:
|
|
92
|
+
if mesh == null:
|
|
93
|
+
push_warning("HitFlash: mesh export not assigned")
|
|
94
|
+
return
|
|
95
|
+
_orig_mat = mesh.get_surface_override_material(0)
|
|
96
|
+
_flash_mat = StandardMaterial3D.new()
|
|
97
|
+
_flash_mat.albedo_color = flash_color
|
|
98
|
+
_flash_mat.emission_enabled = true
|
|
99
|
+
_flash_mat.emission = flash_color
|
|
100
|
+
_flash_mat.emission_energy_multiplier = 2.0
|
|
101
|
+
|
|
102
|
+
func flash() -> void:
|
|
103
|
+
if mesh == null:
|
|
104
|
+
return
|
|
105
|
+
mesh.set_surface_override_material(0, _flash_mat)
|
|
106
|
+
var tween := create_tween()
|
|
107
|
+
tween.tween_interval(duration)
|
|
108
|
+
tween.tween_callback(func(): mesh.set_surface_override_material(0, _orig_mat))
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Wire the trigger:
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
summer_connect_signal(from="./World/Enemy", signal="damaged", to="./World/Enemy/HitFlash", method="flash")
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**Tunable knobs:** `duration` (0.06–0.12 sec — under 0.06 reads as a flicker, over 0.12 looks slow), `emission_energy_multiplier` (1.5–3.0).
|
|
118
|
+
|
|
119
|
+
> CRITICAL: do NOT inline the StandardMaterial3D as a sub_resource via `summer_set_prop`. Build it in script (as above) or save it to `materials/flash.tres` and load. Inline sub_resources break `summer_set_resource_property` silently — see `_shared/mcp-tools-reference.md` § "Trap".
|
|
120
|
+
|
|
121
|
+
### Recipe B — Screen shake (Camera3D wobble)
|
|
122
|
+
|
|
123
|
+
**Goal:** punchy 40–80 ms camera shake on impact. Ties hits to the player's body.
|
|
124
|
+
|
|
125
|
+
**Approach:** attach a shake script to the existing Camera3D. Don't spawn a new camera; reuse the player's.
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
summer_inspect_node "./World/Player/Camera3D" # confirm the path
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Script (`scripts/camera_shake.gd`):
|
|
132
|
+
|
|
133
|
+
```gdscript
|
|
134
|
+
class_name CameraShake
|
|
135
|
+
extends Camera3D
|
|
136
|
+
|
|
137
|
+
@export var trauma_decay: float = 5.0
|
|
138
|
+
@export var max_offset: Vector2 = Vector2(0.15, 0.15)
|
|
139
|
+
@export var max_roll: float = 0.05
|
|
140
|
+
|
|
141
|
+
var _trauma: float = 0.0
|
|
142
|
+
var _base_position: Vector3
|
|
143
|
+
|
|
144
|
+
func _ready() -> void:
|
|
145
|
+
_base_position = position
|
|
146
|
+
|
|
147
|
+
func add_trauma(amount: float) -> void:
|
|
148
|
+
_trauma = clamp(_trauma + amount, 0.0, 1.0)
|
|
149
|
+
|
|
150
|
+
func _process(delta: float) -> void:
|
|
151
|
+
if _trauma <= 0.0:
|
|
152
|
+
position = _base_position
|
|
153
|
+
rotation.z = 0.0
|
|
154
|
+
return
|
|
155
|
+
var shake := _trauma * _trauma # quadratic — feels punchy, not noisy
|
|
156
|
+
position = _base_position + Vector3(
|
|
157
|
+
randf_range(-max_offset.x, max_offset.x) * shake,
|
|
158
|
+
randf_range(-max_offset.y, max_offset.y) * shake,
|
|
159
|
+
0.0
|
|
160
|
+
)
|
|
161
|
+
rotation.z = randf_range(-max_roll, max_roll) * shake
|
|
162
|
+
_trauma = max(_trauma - trauma_decay * delta, 0.0)
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Trigger from the player or any damage handler:
|
|
166
|
+
|
|
167
|
+
```gdscript
|
|
168
|
+
$Camera3D.add_trauma(0.5) # 0.3 light hit, 0.5 normal, 0.8 heavy
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
**Tunable knobs:** `max_offset` (0.05 subtle, 0.2 punchy, 0.4 cinematic), `trauma_decay` (3 long, 5 standard, 8 snappy). Quadratic falloff is the trick — linear feels twitchy.
|
|
172
|
+
|
|
173
|
+
### Recipe C — Pickup sparkle (GPUParticles3D one-shot)
|
|
174
|
+
|
|
175
|
+
**Goal:** when a pickup is collected, emit a quick burst of particles where it disappeared.
|
|
176
|
+
|
|
177
|
+
**Approach:** GPUParticles3D set to `one_shot = true`, `emitting = true` triggered from script. Particle material **must** be a standalone `.tres` (inline sub_resource trap).
|
|
178
|
+
|
|
179
|
+
```
|
|
180
|
+
summer_add_node(parent="./World/Pickup", type="GPUParticles3D", name="Sparkle")
|
|
181
|
+
summer_set_prop(path="./World/Pickup/Sparkle", key="one_shot", value="true")
|
|
182
|
+
summer_set_prop(path="./World/Pickup/Sparkle", key="amount", value="24")
|
|
183
|
+
summer_set_prop(path="./World/Pickup/Sparkle", key="lifetime", value="0.5")
|
|
184
|
+
summer_set_prop(path="./World/Pickup/Sparkle", key="explosiveness", value="0.95")
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
For the `process_material`, save a `ParticleProcessMaterial.tres` first (see `asset-pipeline/asset-strategy/SKILL.md` for the pattern), then:
|
|
188
|
+
|
|
189
|
+
```
|
|
190
|
+
summer_set_prop(path="./World/Pickup/Sparkle", key="process_material", value="res://materials/sparkle_particles.tres")
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
In the pickup's collection handler:
|
|
194
|
+
|
|
195
|
+
```gdscript
|
|
196
|
+
func _on_picked_up() -> void:
|
|
197
|
+
$Sparkle.restart()
|
|
198
|
+
$Sparkle.emitting = true
|
|
199
|
+
$Mesh.visible = false
|
|
200
|
+
await get_tree().create_timer(0.6).timeout
|
|
201
|
+
queue_free()
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
`explosiveness = 0.95` is the difference between a continuous trickle and a satisfying *poof*. `0.0` = constant emission, `1.0` = all particles spawn at frame zero.
|
|
205
|
+
|
|
206
|
+
### Recipe D — Muzzle flash (light + sprite + sound)
|
|
207
|
+
|
|
208
|
+
**Goal:** when a weapon fires, a brief OmniLight3D flash + Sprite3D billboard + audio cue. Sells the shot more than the projectile does.
|
|
209
|
+
|
|
210
|
+
```
|
|
211
|
+
summer_add_node(parent="./World/Player/Weapon", type="OmniLight3D", name="MuzzleLight")
|
|
212
|
+
summer_set_prop(path="./World/Player/Weapon/MuzzleLight", key="light_color", value="Color(1.0, 0.85, 0.4)")
|
|
213
|
+
summer_set_prop(path="./World/Player/Weapon/MuzzleLight", key="light_energy", value="0.0")
|
|
214
|
+
summer_set_prop(path="./World/Player/Weapon/MuzzleLight", key="omni_range", value="2.5")
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
Trigger from the fire function:
|
|
218
|
+
|
|
219
|
+
```gdscript
|
|
220
|
+
func fire() -> void:
|
|
221
|
+
var light: OmniLight3D = $MuzzleLight
|
|
222
|
+
light.light_energy = 4.0
|
|
223
|
+
var tween := create_tween()
|
|
224
|
+
tween.tween_property(light, "light_energy", 0.0, 0.06)
|
|
225
|
+
$MuzzleSound.play()
|
|
226
|
+
$Camera3D.add_trauma(0.3) # if camera shake recipe applied
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Do NOT keep the light always-on at low energy — that costs a real-time light slot. Tween from 4.0 to 0.0 over 60 ms.
|
|
230
|
+
|
|
231
|
+
### Recipe E — Explosion (5-element stack)
|
|
232
|
+
|
|
233
|
+
**Goal:** one Big Boom, all senses. Stack five small effects on the same trigger.
|
|
234
|
+
|
|
235
|
+
1. **Light flash:** OmniLight3D, `light_energy` 0 → 8 → 0 over 200 ms
|
|
236
|
+
2. **Particles:** GPUParticles3D one-shot, `amount` 64, `lifetime` 0.8, `explosiveness` 1.0
|
|
237
|
+
3. **Camera shake:** `add_trauma(0.8)`
|
|
238
|
+
4. **Audio:** AudioStreamPlayer3D, layered explosion
|
|
239
|
+
5. **Hit-stop:** `Engine.time_scale = 0.05` for 60 ms, then 1.0
|
|
240
|
+
|
|
241
|
+
Hit-stop is the secret weapon. 60 ms of 0.05 time scale = the explosion lands. Use sparingly; once per "big" event. Reset with:
|
|
242
|
+
|
|
243
|
+
```gdscript
|
|
244
|
+
Engine.time_scale = 0.05
|
|
245
|
+
await get_tree().create_timer(0.06).timeout
|
|
246
|
+
Engine.time_scale = 1.0
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## Anti-patterns
|
|
250
|
+
|
|
251
|
+
| Don't | Do | Why |
|
|
252
|
+
|---|---|---|
|
|
253
|
+
| Inline sub_resource for ParticleProcessMaterial via `summer_set_prop` | Save standalone `.tres`, reference by path | `summer_set_resource_property` silently fails on inline sub_resources |
|
|
254
|
+
| Hit flash for 0.3 sec | Hit flash for 0.06–0.12 sec | Anything over ~120 ms reads as a "stay" not a flash |
|
|
255
|
+
| Camera shake with linear falloff | Quadratic falloff (`_trauma * _trauma`) | Linear feels jittery; quadratic feels weighty |
|
|
256
|
+
| `OmniLight3D` always on at low energy | Tween from off → on → off in 60–80 ms | Real-time lights have hard count limits per frame |
|
|
257
|
+
| GPUParticles3D `one_shot = false` for pickups | `one_shot = true`, `restart()` to fire | Otherwise particles trickle continuously and look wrong |
|
|
258
|
+
| Tween every property at once | Tween one or two; let the others snap | Over-animated polish reads as noise |
|
|
259
|
+
| `Engine.time_scale` < 0.1 for > 100 ms | `time_scale = 0.05` for 40–80 ms | Longer hit-stop feels broken, not satisfying |
|
|
260
|
+
| Particles with `lifetime > 1.0` for hits | `lifetime = 0.3–0.6` | Hit feedback should resolve before the next swing |
|
|
261
|
+
|
|
262
|
+
## Collaborative protocol
|
|
263
|
+
|
|
264
|
+
This skill writes scripts and adds nodes. Always ask before each recipe is applied. Group related writes into one ask: "I'm about to add HitFlash + CameraShake + connect 2 signals. OK?" See `_shared/collaborative-protocol.md`.
|
|
265
|
+
|
|
266
|
+
## Want a working starter?
|
|
267
|
+
|
|
268
|
+
No template — this is a workflow that bolts onto any existing scene. For a project that already has these recipes pre-wired, see `template-3d-fps` (hit flash + muzzle flash) and `template-3d-platformer` (pickup sparkle).
|
|
269
|
+
|
|
270
|
+
## See also
|
|
271
|
+
|
|
272
|
+
- `_shared/mcp-tools-reference.md` — full MCP tool list, especially the inline-sub_resource trap
|
|
273
|
+
- `_shared/godot-version.md` — Godot 4.5 API notes
|
|
274
|
+
- `_shared/collaborative-protocol.md` — "May I write" pattern
|
|
275
|
+
- `_shared/gd-style.md` — typed GDScript conventions
|
|
276
|
+
- `post-processing/screen-shake/SKILL.md` — deeper camera-shake techniques (Perlin noise, trauma stacks)
|
|
277
|
+
- `visual-effects/gpuparticles-3d-basics/SKILL.md` — particle setup details
|
|
278
|
+
- `visual-effects/hit-impact-flashes/SKILL.md` — flash material variations
|
|
279
|
+
- `visual-effects/muzzle-flashes/SKILL.md` — muzzle flash deep dive
|
|
280
|
+
- `audio/sfx-pipeline/SKILL.md` — pairing audio with VFX
|