summer-engine 2.0.0 → 2.3.0
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/{marketplace.json → .claude-plugin/marketplace.json} +3 -3
- package/.claude-plugin/plugin.json +37 -4
- package/.codex-plugin/plugin.json +38 -5
- package/.cursor-plugin/plugin.json +37 -4
- package/.factory-plugin/plugin.json +20 -0
- package/.opencode/INSTALL.md +9 -3
- package/.opencode/plugins/summer.js +0 -2
- package/AGENTS.md +51 -0
- package/CLAUDE.md +5 -0
- package/GEMINI.md +56 -0
- package/README.md +211 -61
- package/_persona/summer/SKILL.md +113 -0
- package/dist/commands/install.js +2 -2
- package/dist/commands/setup.js +4 -0
- package/dist/commands/skills.js +115 -2
- package/dist/lib/agent-config.d.ts +2 -2
- package/dist/lib/agent-config.js +171 -5
- package/dist/lib/skills-registry.d.ts +290 -26
- package/dist/lib/skills-registry.js +503 -1
- package/dist/mcp/tools/generate-tools.js +101 -0
- package/gemini-extension.json +2 -2
- package/package.json +9 -3
- package/skills/2d-assets/character-portrait/SKILL.md +196 -0
- package/skills/2d-assets/concept-art/SKILL.md +152 -0
- package/skills/2d-assets/pixel-art/SKILL.md +180 -0
- package/skills/2d-assets/skybox-panorama/SKILL.md +207 -0
- package/skills/2d-assets/sprite-sheet/SKILL.md +239 -0
- package/skills/2d-assets/tileable-texture/SKILL.md +205 -0
- package/skills/2d-assets/ui-graphics/SKILL.md +210 -0
- package/skills/3d-assets/character-model/SKILL.md +225 -0
- package/skills/3d-assets/environment-kit/SKILL.md +164 -0
- package/skills/3d-assets/organic-model/SKILL.md +192 -0
- package/skills/3d-assets/prop-model/SKILL.md +133 -0
- package/skills/3d-assets/vehicle-model/SKILL.md +202 -0
- package/skills/ai-and-npcs/design-npc/SKILL.md +3 -1
- package/skills/animation/animation-tree/SKILL.md +260 -0
- package/skills/animation/facial-and-lipsync/SKILL.md +282 -0
- package/skills/animation/generate-motion/SKILL.md +129 -0
- package/skills/animation/procedural-animation/SKILL.md +234 -0
- package/skills/animation/retarget/SKILL.md +172 -0
- package/skills/asset-pipeline/asset-strategy/SKILL.md +102 -212
- package/skills/audio/adaptive-music/SKILL.md +326 -0
- package/skills/audio/ambient-bed/SKILL.md +278 -0
- package/skills/audio/music-track/SKILL.md +268 -0
- package/skills/audio/sound-effect/SKILL.md +237 -0
- package/skills/audio/voice-line/SKILL.md +253 -0
- package/skills/catalog.yaml +249 -3
- package/skills/video/animated-loop/SKILL.md +200 -0
- package/skills/video/cinematic-cutscene/SKILL.md +204 -0
- package/skills/video/trailer-shot/SKILL.md +203 -0
- package/skills/visual-effects/_building-blocks/additive-billboard-particles.md +28 -0
- package/skills/visual-effects/_building-blocks/noise-3d-fbm.gdshaderinc +45 -0
- package/skills/visual-effects/_building-blocks/trauma-shake-snippet.md +51 -0
- package/skills/visual-effects/{vfx → game-feel}/SKILL.md +1 -1
- package/skills/visual-effects/recipes/dissolve/SKILL.md +349 -0
- package/skills/visual-effects/recipes/fire/SKILL.md +387 -0
- package/skills/visual-effects/recipes/hit-spark/SKILL.md +330 -0
- package/skills/visual-effects/recipes/lightning/SKILL.md +372 -0
- package/skills/visual-effects/recipes/magic-glow/SKILL.md +377 -0
- package/skills/visual-effects/recipes/muzzle-flash/SKILL.md +326 -0
- package/skills/visual-effects/recipes/smoke/SKILL.md +381 -0
- package/skills/visual-effects/recipes/water-ripple/SKILL.md +340 -0
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: fire
|
|
3
|
+
description: Use when authoring a fire visual effect — animated flames built with a particle shader, GPUParticles3D, and a noise-based color ramp. Trigger on "torch", "campfire", "bonfire", "flame", "fire effect", "burning", "make this thing on fire", "candle".
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: [Cursor, Claude Code, Windsurf, Codex]
|
|
6
|
+
category: visual-effects
|
|
7
|
+
user-invocable: true
|
|
8
|
+
allowed-tools: Read Write Edit summer_add_node summer_set_prop summer_set_resource_property summer_inspect_node summer_save_scene
|
|
9
|
+
paths: ["**/*.tscn", "**/*.gd", "**/*.gdshader", "addons/vfx/**"]
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# fire — Animated Particle Flames
|
|
13
|
+
|
|
14
|
+
Fire in games is almost never a video clip and never an AI-generated sprite. It's a pile of additive billboard quads emitted from a cone, each running a noise-distorted UV through a hot color ramp (red → orange → yellow → smoky alpha-out). Think Skyrim torches, Doom Eternal's pyre, BotW's bonfires — all built from this same recipe with different dials. This skill writes the shader, the controller, and the node tree, then wires them into the open scene. Authoring, not generating.
|
|
15
|
+
|
|
16
|
+
## When to use
|
|
17
|
+
|
|
18
|
+
- "Add fire to this torch."
|
|
19
|
+
- "Make a campfire / bonfire / hearth flame."
|
|
20
|
+
- "Light the brazier."
|
|
21
|
+
- "I need a candle flame on this candlestick."
|
|
22
|
+
- "The dragon needs a flame breath" (use this for the persistent emitter; pair with a one-shot `muzzle-flash` for the burst).
|
|
23
|
+
- The player set something on fire and you need a continuous, physically-anchored flame.
|
|
24
|
+
|
|
25
|
+
## When NOT to use
|
|
26
|
+
|
|
27
|
+
- The user wants an object to *burn away* (mesh disintegrating with a fire edge) — route to `dissolve`. Fire is a separate effect; pair them.
|
|
28
|
+
- The user wants smoke without flame — go straight to `smoke`. (Most fires want a smoke trail above; spawn this recipe and add `smoke` on top.)
|
|
29
|
+
- The user wants a one-frame muzzle flash from a gun — that's `muzzle-flash`, not fire.
|
|
30
|
+
- The user wants 2D fire on a UI element — use `canvas_item` shader; this recipe is `spatial`/`particles`.
|
|
31
|
+
- The user wants stylized magic flame (blue, purple, ethereal) — use this recipe and load the `magic-fire` variant in the cookbook below.
|
|
32
|
+
|
|
33
|
+
## Recipe
|
|
34
|
+
|
|
35
|
+
### 1. Files to create
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
addons/vfx/fire/fire.gdshader # spatial shader for the visual quad
|
|
39
|
+
addons/vfx/fire/fire_process.gdshader # particle shader for the simulation
|
|
40
|
+
addons/vfx/fire/fire_controller.gd # parameter dials + lifetime
|
|
41
|
+
addons/vfx/fire/fire.tscn # reusable scene (instantiate per torch)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### 2. Visual shader code
|
|
45
|
+
|
|
46
|
+
`addons/vfx/fire/fire.gdshader`:
|
|
47
|
+
|
|
48
|
+
```glsl
|
|
49
|
+
shader_type spatial;
|
|
50
|
+
render_mode unshaded, blend_add, depth_draw_never, cull_disabled, shadows_disabled;
|
|
51
|
+
|
|
52
|
+
#include "res://addons/vfx/_building-blocks/noise-3d-fbm.gdshaderinc"
|
|
53
|
+
|
|
54
|
+
uniform vec4 color_hot : source_color = vec4(1.0, 0.95, 0.55, 1.0);
|
|
55
|
+
uniform vec4 color_mid : source_color = vec4(1.0, 0.45, 0.10, 1.0);
|
|
56
|
+
uniform vec4 color_cool : source_color = vec4(0.85, 0.10, 0.02, 1.0);
|
|
57
|
+
uniform vec4 color_smoke : source_color = vec4(0.05, 0.04, 0.04, 0.0);
|
|
58
|
+
|
|
59
|
+
uniform float noise_scale : hint_range(0.5, 8.0) = 3.0;
|
|
60
|
+
uniform float noise_speed : hint_range(0.0, 4.0) = 1.4;
|
|
61
|
+
uniform float distortion : hint_range(0.0, 0.6) = 0.20;
|
|
62
|
+
uniform float emission_energy : hint_range(0.0, 8.0) = 3.5;
|
|
63
|
+
uniform float soft_edge : hint_range(0.01, 0.5) = 0.20;
|
|
64
|
+
|
|
65
|
+
void vertex() {
|
|
66
|
+
// Billboard the quad toward the camera (view-space).
|
|
67
|
+
MODELVIEW_MATRIX = VIEW_MATRIX * mat4(
|
|
68
|
+
INV_VIEW_MATRIX[0],
|
|
69
|
+
INV_VIEW_MATRIX[1],
|
|
70
|
+
INV_VIEW_MATRIX[2],
|
|
71
|
+
MODEL_MATRIX[3]
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
void fragment() {
|
|
76
|
+
// CUSTOM.x is particle age 0..1 (set by the process material).
|
|
77
|
+
float age = CUSTOM.x;
|
|
78
|
+
|
|
79
|
+
// Distort UVs with scrolling 3D noise so the flame writhes.
|
|
80
|
+
vec3 np = vec3(UV * noise_scale, TIME * noise_speed);
|
|
81
|
+
float n = fbm3(np);
|
|
82
|
+
vec2 uv = UV + vec2(n - 0.5) * distortion;
|
|
83
|
+
|
|
84
|
+
// Soft round mask, hottest at the bottom-center, cooler toward the top.
|
|
85
|
+
vec2 c = uv - vec2(0.5, 0.15);
|
|
86
|
+
float r = length(c * vec2(1.0, 0.55));
|
|
87
|
+
float mask = smoothstep(0.55, 0.55 - soft_edge, r);
|
|
88
|
+
|
|
89
|
+
// Color ramp by particle age (0 = hot bright, 1 = smoky alpha-out).
|
|
90
|
+
vec4 col;
|
|
91
|
+
if (age < 0.33) {
|
|
92
|
+
col = mix(color_hot, color_mid, age / 0.33);
|
|
93
|
+
} else if (age < 0.75) {
|
|
94
|
+
col = mix(color_mid, color_cool, (age - 0.33) / 0.42);
|
|
95
|
+
} else {
|
|
96
|
+
col = mix(color_cool, color_smoke, (age - 0.75) / 0.25);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Add the noise as a brightness modulation so flames flicker.
|
|
100
|
+
float flicker = mix(0.6, 1.2, n);
|
|
101
|
+
|
|
102
|
+
ALBEDO = col.rgb * flicker;
|
|
103
|
+
EMISSION = col.rgb * flicker * emission_energy * (1.0 - age);
|
|
104
|
+
ALPHA = col.a * mask * (1.0 - age * age);
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### 3. Particle process shader (optional)
|
|
109
|
+
|
|
110
|
+
For most fires the built-in `ParticleProcessMaterial` is fine. Drop in this custom shader only if you need turbulence beyond the standard params:
|
|
111
|
+
|
|
112
|
+
`addons/vfx/fire/fire_process.gdshader`:
|
|
113
|
+
|
|
114
|
+
```glsl
|
|
115
|
+
shader_type particles;
|
|
116
|
+
|
|
117
|
+
uniform float rise_speed : hint_range(0.1, 6.0) = 1.8;
|
|
118
|
+
uniform float spread_angle : hint_range(0.0, 1.5) = 0.35;
|
|
119
|
+
uniform float lifetime_var : hint_range(0.0, 1.0) = 0.25;
|
|
120
|
+
|
|
121
|
+
void start() {
|
|
122
|
+
// Random spawn within a small disc.
|
|
123
|
+
float r = sqrt(rand_from_seed(RANDOM_SEED + uint(7))) * 0.18;
|
|
124
|
+
float th = rand_from_seed(RANDOM_SEED + uint(11)) * 6.2831853;
|
|
125
|
+
TRANSFORM[3].xyz = vec3(cos(th) * r, 0.0, sin(th) * r);
|
|
126
|
+
|
|
127
|
+
// Initial velocity: mostly up, slight cone.
|
|
128
|
+
vec3 dir = vec3(
|
|
129
|
+
(rand_from_seed(RANDOM_SEED + uint(13)) - 0.5) * spread_angle,
|
|
130
|
+
1.0,
|
|
131
|
+
(rand_from_seed(RANDOM_SEED + uint(17)) - 0.5) * spread_angle
|
|
132
|
+
);
|
|
133
|
+
VELOCITY = normalize(dir) * rise_speed;
|
|
134
|
+
|
|
135
|
+
// Stagger lifetimes for variety.
|
|
136
|
+
LIFETIME = LIFETIME * (1.0 + (rand_from_seed(RANDOM_SEED + uint(19)) - 0.5) * 2.0 * lifetime_var);
|
|
137
|
+
|
|
138
|
+
CUSTOM.x = 0.0; // age
|
|
139
|
+
CUSTOM.y = rand_from_seed(RANDOM_SEED + uint(23)); // per-particle seed
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
void process() {
|
|
143
|
+
// Rise + small horizontal sway.
|
|
144
|
+
VELOCITY.y += DELTA * 0.6;
|
|
145
|
+
VELOCITY.x += sin(TIME * 2.0 + CUSTOM.y * 6.28) * DELTA * 0.4;
|
|
146
|
+
VELOCITY.z += cos(TIME * 2.3 + CUSTOM.y * 6.28) * DELTA * 0.4;
|
|
147
|
+
|
|
148
|
+
// Shrink as it ages.
|
|
149
|
+
float age = clamp(1.0 - LIFETIME / TOTAL_LIFETIME, 0.0, 1.0);
|
|
150
|
+
CUSTOM.x = age;
|
|
151
|
+
float scale = mix(1.0, 0.35, age);
|
|
152
|
+
TRANSFORM[0].xyz = vec3(scale, 0.0, 0.0);
|
|
153
|
+
TRANSFORM[1].xyz = vec3(0.0, scale, 0.0);
|
|
154
|
+
TRANSFORM[2].xyz = vec3(0.0, 0.0, scale);
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### 4. GDScript controller
|
|
159
|
+
|
|
160
|
+
`addons/vfx/fire/fire_controller.gd`:
|
|
161
|
+
|
|
162
|
+
```gdscript
|
|
163
|
+
@tool
|
|
164
|
+
class_name FireController
|
|
165
|
+
extends GPUParticles3D
|
|
166
|
+
|
|
167
|
+
@export_group("Flame size")
|
|
168
|
+
@export_range(0.05, 5.0) var flame_height: float = 1.2 :
|
|
169
|
+
set(v): flame_height = v; _apply()
|
|
170
|
+
@export_range(0.05, 3.0) var flame_radius: float = 0.35 :
|
|
171
|
+
set(v): flame_radius = v; _apply()
|
|
172
|
+
|
|
173
|
+
@export_group("Flame intensity")
|
|
174
|
+
@export_range(8, 1024) var particle_count: int = 96 :
|
|
175
|
+
set(v): particle_count = v; _apply()
|
|
176
|
+
@export_range(0.2, 4.0) var flame_lifetime: float = 1.4 :
|
|
177
|
+
set(v): flame_lifetime = v; _apply()
|
|
178
|
+
@export_range(0.0, 8.0) var emission_energy: float = 3.5 :
|
|
179
|
+
set(v): emission_energy = v; _apply()
|
|
180
|
+
|
|
181
|
+
@export_group("Color")
|
|
182
|
+
@export var color_hot: Color = Color(1.0, 0.95, 0.55)
|
|
183
|
+
@export var color_mid: Color = Color(1.0, 0.45, 0.10)
|
|
184
|
+
@export var color_cool: Color = Color(0.85, 0.10, 0.02)
|
|
185
|
+
|
|
186
|
+
func _ready() -> void:
|
|
187
|
+
one_shot = false
|
|
188
|
+
explosiveness = 0.0
|
|
189
|
+
_apply()
|
|
190
|
+
|
|
191
|
+
func _apply() -> void:
|
|
192
|
+
amount = particle_count
|
|
193
|
+
lifetime = flame_lifetime
|
|
194
|
+
|
|
195
|
+
var pm := process_material as ParticleProcessMaterial
|
|
196
|
+
if pm:
|
|
197
|
+
pm.emission_shape = ParticleProcessMaterial.EMISSION_SHAPE_SPHERE
|
|
198
|
+
pm.emission_sphere_radius = flame_radius
|
|
199
|
+
pm.direction = Vector3.UP
|
|
200
|
+
pm.spread = 12.0
|
|
201
|
+
pm.initial_velocity_min = flame_height * 0.9
|
|
202
|
+
pm.initial_velocity_max = flame_height * 1.4
|
|
203
|
+
pm.gravity = Vector3.ZERO
|
|
204
|
+
pm.scale_min = flame_radius * 1.1
|
|
205
|
+
pm.scale_max = flame_radius * 1.6
|
|
206
|
+
|
|
207
|
+
if draw_pass_1:
|
|
208
|
+
var mesh := draw_pass_1 as QuadMesh
|
|
209
|
+
var mat := mesh.surface_get_material(0) as ShaderMaterial
|
|
210
|
+
if mat:
|
|
211
|
+
mat.set_shader_parameter("color_hot", color_hot)
|
|
212
|
+
mat.set_shader_parameter("color_mid", color_mid)
|
|
213
|
+
mat.set_shader_parameter("color_cool", color_cool)
|
|
214
|
+
mat.set_shader_parameter("emission_energy", emission_energy)
|
|
215
|
+
|
|
216
|
+
func extinguish(fade_seconds: float = 0.5) -> void:
|
|
217
|
+
var t := create_tween()
|
|
218
|
+
t.tween_property(self, "amount_ratio", 0.0, fade_seconds)
|
|
219
|
+
t.tween_callback(queue_free)
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### 5. Node tree
|
|
223
|
+
|
|
224
|
+
```
|
|
225
|
+
GPUParticles3D ("Fire") [script: fire_controller.gd]
|
|
226
|
+
├── process_material: ParticleProcessMaterial (or ShaderMaterial w/ fire_process.gdshader)
|
|
227
|
+
└── draw_pass_1: QuadMesh (size 1.0 × 1.0)
|
|
228
|
+
└── surface_material_override[0]: ShaderMaterial (shader: fire.gdshader)
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Add an OmniLight3D as a sibling (parented to the same anchor as the GPUParticles3D, NOT under it) for proper light cast. Recommended: `light_color = Color(1.0, 0.6, 0.25)`, `light_energy = 1.6`, `omni_range = 6.0`. Pulse it lightly via the controller for free atmosphere.
|
|
232
|
+
|
|
233
|
+
### 6. Wire it in (MCP calls)
|
|
234
|
+
|
|
235
|
+
```
|
|
236
|
+
summer_add_node(parent="./World/Torch", type="GPUParticles3D", name="Fire")
|
|
237
|
+
summer_set_prop(path="./World/Torch/Fire", property="script", value="res://addons/vfx/fire/fire_controller.gd")
|
|
238
|
+
|
|
239
|
+
summer_add_node(parent="./World/Torch/Fire", type="ParticleProcessMaterial", name="ProcessMat")
|
|
240
|
+
summer_set_resource_property(nodePath="./World/Torch/Fire", resourceProperty="process_material", value="res://addons/vfx/fire/process_mat.tres")
|
|
241
|
+
|
|
242
|
+
summer_set_resource_property(nodePath="./World/Torch/Fire", resourceProperty="draw_pass_1", value="res://addons/vfx/fire/quad.tres")
|
|
243
|
+
# quad.tres is a QuadMesh with size (1,1) and surface_material_override[0] = fire_material.tres (ShaderMaterial wrapping fire.gdshader)
|
|
244
|
+
|
|
245
|
+
summer_set_prop(path="./World/Torch/Fire", property="flame_height", value=1.2)
|
|
246
|
+
summer_set_prop(path="./World/Torch/Fire", property="flame_radius", value=0.35)
|
|
247
|
+
summer_set_prop(path="./World/Torch/Fire", property="particle_count", value=96)
|
|
248
|
+
|
|
249
|
+
summer_add_node(parent="./World/Torch", type="OmniLight3D", name="FireLight")
|
|
250
|
+
summer_set_prop(path="./World/Torch/FireLight", property="light_color", value="Color(1.0, 0.6, 0.25)")
|
|
251
|
+
summer_set_prop(path="./World/Torch/FireLight", property="light_energy", value=1.6)
|
|
252
|
+
summer_set_prop(path="./World/Torch/FireLight", property="omni_range", value=6.0)
|
|
253
|
+
|
|
254
|
+
summer_save_scene
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### 7. Parameters to tune
|
|
258
|
+
|
|
259
|
+
| Parameter | Range | Effect |
|
|
260
|
+
|---|---|---|
|
|
261
|
+
| `particle_count` (`amount`) | 32–512 | density of the flame, perf cost is linear |
|
|
262
|
+
| `flame_lifetime` | 0.5–3.0 s | how long each particle lives before fade |
|
|
263
|
+
| `flame_height` | 0.2–4.0 | initial upward velocity → height of the column |
|
|
264
|
+
| `flame_radius` | 0.05–2.0 | spawn disc + per-particle scale |
|
|
265
|
+
| `emission_energy` | 0.0–8.0 | how much it blooms in the post-process |
|
|
266
|
+
| `noise_scale` | 0.5–8.0 | small = wide flowing tongues; large = fine flicker |
|
|
267
|
+
| `noise_speed` | 0.0–4.0 | how fast the flame moves |
|
|
268
|
+
| `distortion` | 0.0–0.6 | how warped the silhouette gets |
|
|
269
|
+
| `color_hot/mid/cool` | Color | the ramp; shift toward blue/green/purple for magic |
|
|
270
|
+
|
|
271
|
+
## Cookbook — named variants
|
|
272
|
+
|
|
273
|
+
### torch-flame (default)
|
|
274
|
+
|
|
275
|
+
Wall-mounted torch, ~1 m flame.
|
|
276
|
+
|
|
277
|
+
```
|
|
278
|
+
particle_count = 64
|
|
279
|
+
flame_lifetime = 1.2
|
|
280
|
+
flame_height = 1.2
|
|
281
|
+
flame_radius = 0.20
|
|
282
|
+
emission_energy = 3.5
|
|
283
|
+
color_hot = Color(1.0, 0.95, 0.55)
|
|
284
|
+
color_mid = Color(1.0, 0.45, 0.10)
|
|
285
|
+
color_cool = Color(0.85, 0.10, 0.02)
|
|
286
|
+
OmniLight3D: light_energy = 1.6, omni_range = 6.0
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### bonfire
|
|
290
|
+
|
|
291
|
+
Wide, tall, hungry. Pair with `smoke` directly above.
|
|
292
|
+
|
|
293
|
+
```
|
|
294
|
+
particle_count = 256
|
|
295
|
+
flame_lifetime = 1.8
|
|
296
|
+
flame_height = 2.4
|
|
297
|
+
flame_radius = 0.80
|
|
298
|
+
emission_energy = 4.5
|
|
299
|
+
OmniLight3D: light_energy = 3.5, omni_range = 12.0, pulse 1.0 Hz
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### candle-flame
|
|
303
|
+
|
|
304
|
+
Tiny, calm, mostly motionless. Lifetime small so it shimmers in place.
|
|
305
|
+
|
|
306
|
+
```
|
|
307
|
+
particle_count = 24
|
|
308
|
+
flame_lifetime = 0.6
|
|
309
|
+
flame_height = 0.18
|
|
310
|
+
flame_radius = 0.04
|
|
311
|
+
emission_energy = 2.0
|
|
312
|
+
noise_speed = 0.7
|
|
313
|
+
distortion = 0.10
|
|
314
|
+
OmniLight3D: light_energy = 0.6, omni_range = 2.5
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### magic-fire (blue / arcane)
|
|
318
|
+
|
|
319
|
+
Cool flame. Same dynamics, recolor the ramp.
|
|
320
|
+
|
|
321
|
+
```
|
|
322
|
+
particle_count = 96
|
|
323
|
+
flame_lifetime = 1.6
|
|
324
|
+
flame_height = 1.4
|
|
325
|
+
flame_radius = 0.30
|
|
326
|
+
emission_energy = 5.0
|
|
327
|
+
color_hot = Color(0.85, 0.95, 1.00)
|
|
328
|
+
color_mid = Color(0.20, 0.55, 1.00)
|
|
329
|
+
color_cool = Color(0.05, 0.10, 0.55)
|
|
330
|
+
OmniLight3D: light_color = Color(0.40, 0.65, 1.0), light_energy = 2.4
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
## Anti-patterns
|
|
334
|
+
|
|
335
|
+
- **Spatial-only shader on the visual quad.** It still works, but you forgot `blend_add` + `unshaded` and the flame looks like a flat orange triangle in shadow. Always: `render_mode unshaded, blend_add, depth_draw_never;`.
|
|
336
|
+
- **Looping a one-shot emitter.** `one_shot = false` for fire (continuous). One-shot is for `muzzle-flash` and `hit-spark`.
|
|
337
|
+
- **Missing `cull_disabled`.** Particles billboard toward camera; if you cull backfaces, the flame disappears at certain angles.
|
|
338
|
+
- **`depth_draw_opaque` on the visual material.** You'll z-fight with anything inside the flame (the torch handle). Use `depth_draw_never`.
|
|
339
|
+
- **OmniLight3D parented under the GPUParticles3D.** The particles emit in local space; the light moves with them. Sibling, not child.
|
|
340
|
+
- **Procedural noise computed in world space without a per-instance seed.** Every particle pulses identically, looking like a strobe. The included shader uses `CUSTOM.y` as a per-particle seed.
|
|
341
|
+
- **Generating a fire image and using it as the particle texture.** That's the misroute this skill exists to prevent. The shader-driven look is more alive and trivially recolorable.
|
|
342
|
+
|
|
343
|
+
## Performance notes
|
|
344
|
+
|
|
345
|
+
- Default 96 particles per fire: ~0.05 ms on desktop GPU, scales linearly. Budget ~512 fire particles total in a scene before you should think about LOD.
|
|
346
|
+
- Mobile: drop default to 48, disable the `OmniLight3D` shadow cast, and use a 64×64 noise texture instead of FBM if FPS dips.
|
|
347
|
+
- LOD: in the controller's `_process`, scale `amount_ratio` by `clamp(1.0 - dist_to_camera / 30.0, 0.05, 1.0)`. Past 30 m, drop to a single billboard quad with the same shader (no particles).
|
|
348
|
+
- The OmniLight3D shadow is the expensive part. Disable shadows on torches that aren't doing dramatic work.
|
|
349
|
+
- `blend_add` skips the depth write — good for perf, but means you can't sort fires against each other. Avoid stacking 5 flames at the same Z.
|
|
350
|
+
|
|
351
|
+
## Edge cases
|
|
352
|
+
|
|
353
|
+
- **Underwater fire.** Don't. If the user insists (a magic underwater shrine), drop `flame_height` to 0.4, `noise_speed` to 0.3, and tint everything blue-green via the color ramp.
|
|
354
|
+
- **Wind affecting the flame.** The simple `process_material` doesn't know about wind. Either drive `pm.gravity = Vector3(wind.x, 0.6, wind.z)` from a script polling a wind vector, or use the `fire_process.gdshader` and pass a wind uniform.
|
|
355
|
+
- **Fire on a moving object (torch held by NPC).** Set `local_coords = true` so particles emit in the torch's local space. Otherwise they trail behind as the NPC walks.
|
|
356
|
+
- **Fire viewed in mirror / reflection probe.** The flame won't appear in baked reflections (it's transparent). Add a fake glow card (MeshInstance3D with an emissive material) for reflections to pick up.
|
|
357
|
+
- **Cinematic close-up of the flame.** Default 96 particles looks sparse at 1080p. Bump to 256 for cinematics, drop back for gameplay.
|
|
358
|
+
|
|
359
|
+
## Fallback (no MCP)
|
|
360
|
+
|
|
361
|
+
VFX is code, no MCP required. The user can:
|
|
362
|
+
|
|
363
|
+
1. Create the four files at `addons/vfx/fire/` with the contents above.
|
|
364
|
+
2. In Godot, add a `GPUParticles3D` node, attach `fire_controller.gd`.
|
|
365
|
+
3. Set `process_material` to a new `ParticleProcessMaterial` (the script configures it on `_ready`).
|
|
366
|
+
4. Set `draw_pass_1` to a new `QuadMesh` (size 1×1), with `surface_material_override[0]` set to a `ShaderMaterial` whose shader is `fire.gdshader`.
|
|
367
|
+
5. Save as `fire.tscn` and instantiate at every torch / brazier.
|
|
368
|
+
|
|
369
|
+
## Handoff
|
|
370
|
+
|
|
371
|
+
After firing this recipe, suggest neighbors:
|
|
372
|
+
|
|
373
|
+
- `summer:visual-effects/recipes/smoke` — every fire that lasts > 5 s wants smoke rising above. Spawn 0.5 m above the flame top.
|
|
374
|
+
- `summer:visual-effects/recipes/dissolve` — if the user wants the *fuel* (logs, body, paper) to gradually disappear as it burns.
|
|
375
|
+
- `summer:visual-effects/recipes/magic-glow` — for an `OmniLight3D` with proper sin-wave pulsing if the default is too constant.
|
|
376
|
+
- `summer:visual-effects/game-feel` — for a slight camera flicker / thermal heat-haze post-process near very large fires.
|
|
377
|
+
- `summer:audio/sound-effect` — generate `fire crackle, dry wood, soft pops, 2s loop` and wire to an `AudioStreamPlayer3D` on the same anchor.
|
|
378
|
+
|
|
379
|
+
## See also
|
|
380
|
+
|
|
381
|
+
- `_building-blocks/noise-3d-fbm.gdshaderinc` — the FBM noise this shader includes
|
|
382
|
+
- `_building-blocks/additive-billboard-particles.md` — canonical additive material settings
|
|
383
|
+
- `summer:visual-effects/recipes/smoke` — sister recipe for the column rising above
|
|
384
|
+
- `summer:visual-effects/recipes/dissolve` — for objects burning away
|
|
385
|
+
- `summer:visual-effects/recipes/magic-glow` — for the light source pulsing
|
|
386
|
+
- `summer:visual-effects/game-feel` — non-particle game-feel pairings
|
|
387
|
+
- `summer:gdscript-patterns` — for the controller's idioms (signals, exports, `@tool`)
|