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,45 @@
|
|
|
1
|
+
// 3D Fractal Brownian Motion noise — used by fire, dissolve, smoke, magic-glow.
|
|
2
|
+
// Include with: #include "res://addons/vfx/_building-blocks/noise-3d-fbm.gdshaderinc"
|
|
3
|
+
|
|
4
|
+
float hash3(vec3 p) {
|
|
5
|
+
p = fract(p * vec3(443.8975, 397.2973, 491.1871));
|
|
6
|
+
p += dot(p, p.yxz + 19.19);
|
|
7
|
+
return fract((p.x + p.y) * p.z);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
float noise3(vec3 p) {
|
|
11
|
+
vec3 i = floor(p);
|
|
12
|
+
vec3 f = fract(p);
|
|
13
|
+
f = f * f * (3.0 - 2.0 * f);
|
|
14
|
+
|
|
15
|
+
float n000 = hash3(i + vec3(0.0, 0.0, 0.0));
|
|
16
|
+
float n100 = hash3(i + vec3(1.0, 0.0, 0.0));
|
|
17
|
+
float n010 = hash3(i + vec3(0.0, 1.0, 0.0));
|
|
18
|
+
float n110 = hash3(i + vec3(1.0, 1.0, 0.0));
|
|
19
|
+
float n001 = hash3(i + vec3(0.0, 0.0, 1.0));
|
|
20
|
+
float n101 = hash3(i + vec3(1.0, 0.0, 1.0));
|
|
21
|
+
float n011 = hash3(i + vec3(0.0, 1.0, 1.0));
|
|
22
|
+
float n111 = hash3(i + vec3(1.0, 1.0, 1.0));
|
|
23
|
+
|
|
24
|
+
float x00 = mix(n000, n100, f.x);
|
|
25
|
+
float x10 = mix(n010, n110, f.x);
|
|
26
|
+
float x01 = mix(n001, n101, f.x);
|
|
27
|
+
float x11 = mix(n011, n111, f.x);
|
|
28
|
+
|
|
29
|
+
float y0 = mix(x00, x10, f.y);
|
|
30
|
+
float y1 = mix(x01, x11, f.y);
|
|
31
|
+
|
|
32
|
+
return mix(y0, y1, f.z);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
float fbm3(vec3 p) {
|
|
36
|
+
float v = 0.0;
|
|
37
|
+
float amp = 0.5;
|
|
38
|
+
float freq = 1.0;
|
|
39
|
+
for (int i = 0; i < 4; i++) {
|
|
40
|
+
v += amp * noise3(p * freq);
|
|
41
|
+
freq *= 2.0;
|
|
42
|
+
amp *= 0.5;
|
|
43
|
+
}
|
|
44
|
+
return v;
|
|
45
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Trauma Camera Shake — Reusable Snippet
|
|
2
|
+
|
|
3
|
+
Variant of the canonical "trauma model" shake (see `visual-effects/game-feel/SKILL.md` for the full game-feel discipline). Lightning and explosion recipes call `add_trauma()` to chain visual impact with screen-level reaction.
|
|
4
|
+
|
|
5
|
+
## Setup (autoload script)
|
|
6
|
+
|
|
7
|
+
`autoloads/CameraShake.gd`:
|
|
8
|
+
|
|
9
|
+
```gdscript
|
|
10
|
+
extends Node
|
|
11
|
+
|
|
12
|
+
var trauma: float = 0.0
|
|
13
|
+
var trauma_decay: float = 1.4
|
|
14
|
+
var max_offset: Vector3 = Vector3(0.4, 0.4, 0.0)
|
|
15
|
+
var max_yaw: float = 0.05
|
|
16
|
+
var max_pitch: float = 0.05
|
|
17
|
+
var camera: Camera3D
|
|
18
|
+
|
|
19
|
+
func _process(delta: float) -> void:
|
|
20
|
+
if camera == null:
|
|
21
|
+
camera = get_viewport().get_camera_3d()
|
|
22
|
+
if camera == null:
|
|
23
|
+
return
|
|
24
|
+
if trauma > 0.0:
|
|
25
|
+
var s = trauma * trauma // perceived intensity grows quadratically
|
|
26
|
+
var t = Time.get_ticks_msec() * 0.01
|
|
27
|
+
camera.h_offset = max_offset.x * s * (randf() * 2.0 - 1.0)
|
|
28
|
+
camera.v_offset = max_offset.y * s * (randf() * 2.0 - 1.0)
|
|
29
|
+
camera.rotation.x += max_pitch * s * (randf() * 2.0 - 1.0) * 0.1
|
|
30
|
+
camera.rotation.y += max_yaw * s * (randf() * 2.0 - 1.0) * 0.1
|
|
31
|
+
trauma = max(0.0, trauma - trauma_decay * delta)
|
|
32
|
+
else:
|
|
33
|
+
camera.h_offset = 0.0
|
|
34
|
+
camera.v_offset = 0.0
|
|
35
|
+
|
|
36
|
+
func add_trauma(amount: float) -> void:
|
|
37
|
+
trauma = clamp(trauma + amount, 0.0, 1.0)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Register `CameraShake` as autoload in Project Settings.
|
|
41
|
+
|
|
42
|
+
## Usage
|
|
43
|
+
|
|
44
|
+
```gdscript
|
|
45
|
+
# In a recipe controller:
|
|
46
|
+
CameraShake.add_trauma(0.6) # lightning strike
|
|
47
|
+
CameraShake.add_trauma(1.0) # explosion (saturates to max)
|
|
48
|
+
CameraShake.add_trauma(0.2) # hit spark
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
The trauma model: noise-driven shake whose intensity grows quadratically with `trauma`. Multiple sources stack but saturate — a player taking 5 hits in a row shakes harder, but never beyond the max.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name:
|
|
2
|
+
name: game-feel
|
|
3
3
|
description: Use when the user says the game "feels flat", "lacks impact", "needs juice", "needs punch", or asks for hit feedback, screen shake, camera shake, audio ducking, or general "game feel". Walks the user through the canonical Godot 4.5 game-feel stack — hit-flash + trauma camera shake + audio ducking — wired together so a single hit fires all three. Trigger on "vfx", "juice", "punch", "feels flat", "game feel", "hit flash", "screen shake", "camera shake", "ducking".
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: [Cursor, Claude Code, Windsurf, Codex]
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: dissolve
|
|
3
|
+
description: Use when authoring a dissolve effect — an object's mesh disintegrating with a glowing burning edge, driven by a noise threshold ShaderMaterial overriding the target's existing material. Trigger on "dissolve", "disintegrate", "burn away", "Thanos snap", "vanish into ash", "enemy fades out", "object burns up".
|
|
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
|
+
# dissolve — Mesh Disintegration with Edge Glow
|
|
13
|
+
|
|
14
|
+
The dissolve shader samples 3D noise per fragment and clips pixels whose noise value is below a `threshold` uniform. Pixels just above the threshold are tinted with an emissive `edge_color` to fake the burning rim. Animate `threshold` from 0 to 1 over `duration` and the mesh appears to dissolve from "nothing missing" to "completely gone." Used for enemy death, item pickups, transitions, summon FX. NOT a particle system — this is a material override on the target mesh itself.
|
|
15
|
+
|
|
16
|
+
## When to use
|
|
17
|
+
|
|
18
|
+
- "Enemy disintegrates when killed."
|
|
19
|
+
- "Item dissolves when picked up."
|
|
20
|
+
- "Banish the demon back to the void."
|
|
21
|
+
- "Thanos snap — character turns to dust."
|
|
22
|
+
- "Object burns away."
|
|
23
|
+
- "Stealth cloak fades the player out."
|
|
24
|
+
- The user wants an object to *go away* dramatically rather than `queue_free()` instantly.
|
|
25
|
+
|
|
26
|
+
## When NOT to use
|
|
27
|
+
|
|
28
|
+
- The user wants *fire* on the object that doesn't consume it — use `fire`, not dissolve. Dissolve makes the mesh disappear; fire just sits on top.
|
|
29
|
+
- The user wants a particle-driven ash cloud as the object disappears — pair this with `smoke` (recolor to ash gray) spawned at the dissolving mesh's bounds.
|
|
30
|
+
- The user wants a fade-out via alpha (cheap, no shader) — set `transparency = TRANSPARENCY_ALPHA` and tween `albedo_color.a` to 0. Dissolve is better but more expensive.
|
|
31
|
+
- The user wants the *world* to dissolve in for a level transition — that's a screen-space post-process, not this per-mesh recipe.
|
|
32
|
+
- The character has skinned mesh + multiple materials — this works but you have to override every material slot. Confirm with user; consider a fade-out instead.
|
|
33
|
+
|
|
34
|
+
## Recipe
|
|
35
|
+
|
|
36
|
+
### 1. Files to create
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
addons/vfx/dissolve/dissolve.gdshader
|
|
40
|
+
addons/vfx/dissolve/dissolve_controller.gd
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
No `.tscn` needed — this overrides materials on existing meshes.
|
|
44
|
+
|
|
45
|
+
### 2. Shader code
|
|
46
|
+
|
|
47
|
+
`addons/vfx/dissolve/dissolve.gdshader`:
|
|
48
|
+
|
|
49
|
+
```glsl
|
|
50
|
+
shader_type spatial;
|
|
51
|
+
render_mode cull_back, depth_draw_opaque;
|
|
52
|
+
|
|
53
|
+
#include "res://addons/vfx/_building-blocks/noise-3d-fbm.gdshaderinc"
|
|
54
|
+
|
|
55
|
+
// Original material inputs (so the mesh still looks like itself until it dissolves).
|
|
56
|
+
uniform sampler2D albedo_texture : source_color, hint_default_white;
|
|
57
|
+
uniform vec4 base_color : source_color = vec4(1.0, 1.0, 1.0, 1.0);
|
|
58
|
+
uniform float base_metallic : hint_range(0.0, 1.0) = 0.0;
|
|
59
|
+
uniform float base_roughness : hint_range(0.0, 1.0) = 0.7;
|
|
60
|
+
|
|
61
|
+
// Dissolve params.
|
|
62
|
+
uniform float threshold : hint_range(0.0, 1.0) = 0.0; // 0 = whole mesh, 1 = gone
|
|
63
|
+
uniform float edge_width : hint_range(0.001, 0.30) = 0.06;
|
|
64
|
+
uniform vec4 edge_color : source_color = vec4(1.0, 0.55, 0.10, 1.0);
|
|
65
|
+
uniform float edge_emission : hint_range(0.0, 16.0) = 6.0;
|
|
66
|
+
uniform float noise_scale : hint_range(0.5, 12.0) = 3.0;
|
|
67
|
+
uniform vec3 noise_offset = vec3(0.0);
|
|
68
|
+
uniform bool use_object_space = true;
|
|
69
|
+
|
|
70
|
+
void fragment() {
|
|
71
|
+
// Sample noise in object space so the dissolve pattern stays attached to the mesh.
|
|
72
|
+
vec3 npos = (use_object_space ? (inverse(MODEL_MATRIX) * vec4(VERTEX, 1.0)).xyz : VERTEX) * noise_scale + noise_offset;
|
|
73
|
+
float n = fbm3(npos);
|
|
74
|
+
|
|
75
|
+
// Discard everything below the threshold.
|
|
76
|
+
if (n < threshold) {
|
|
77
|
+
discard;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Edge: pixels within `edge_width` of the threshold get the burning glow.
|
|
81
|
+
float edge_t = smoothstep(threshold + edge_width, threshold, n);
|
|
82
|
+
|
|
83
|
+
vec4 tex = texture(albedo_texture, UV) * base_color;
|
|
84
|
+
ALBEDO = mix(tex.rgb, edge_color.rgb, edge_t);
|
|
85
|
+
METALLIC = base_metallic * (1.0 - edge_t);
|
|
86
|
+
ROUGHNESS = mix(base_roughness, 0.4, edge_t);
|
|
87
|
+
EMISSION = edge_color.rgb * edge_emission * edge_t;
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### 3. GDScript controller
|
|
92
|
+
|
|
93
|
+
`addons/vfx/dissolve/dissolve_controller.gd`:
|
|
94
|
+
|
|
95
|
+
```gdscript
|
|
96
|
+
@tool
|
|
97
|
+
class_name DissolveController
|
|
98
|
+
extends RefCounted
|
|
99
|
+
|
|
100
|
+
## One-shot dissolve helper. Static-style API. Call from anywhere.
|
|
101
|
+
## Example: DissolveController.dissolve_object(enemy, 1.5, Color(1,0.55,0.1)).
|
|
102
|
+
|
|
103
|
+
const SHADER_PATH := "res://addons/vfx/dissolve/dissolve.gdshader"
|
|
104
|
+
|
|
105
|
+
## Dissolve a Node3D (and all MeshInstance3Ds under it) over `duration` seconds, then queue_free.
|
|
106
|
+
## target: the Node3D to dissolve
|
|
107
|
+
## duration: seconds (typical 0.6–2.5)
|
|
108
|
+
## edge_color: glow color of the burning edge
|
|
109
|
+
## edge_emission: bloom strength of the edge
|
|
110
|
+
## free_when_done: queue_free target when threshold reaches 1.0
|
|
111
|
+
static func dissolve_object(
|
|
112
|
+
target: Node3D,
|
|
113
|
+
duration: float = 1.2,
|
|
114
|
+
edge_color: Color = Color(1.0, 0.55, 0.10),
|
|
115
|
+
edge_emission: float = 6.0,
|
|
116
|
+
free_when_done: bool = true
|
|
117
|
+
) -> Tween:
|
|
118
|
+
var meshes := _collect_meshes(target)
|
|
119
|
+
if meshes.is_empty():
|
|
120
|
+
push_warning("DissolveController: no MeshInstance3D under %s" % target.name)
|
|
121
|
+
if free_when_done: target.queue_free()
|
|
122
|
+
return null
|
|
123
|
+
|
|
124
|
+
var mats: Array[ShaderMaterial] = []
|
|
125
|
+
for mi in meshes:
|
|
126
|
+
var sm := _override_material(mi, edge_color, edge_emission)
|
|
127
|
+
if sm: mats.append(sm)
|
|
128
|
+
|
|
129
|
+
var tween := target.create_tween()
|
|
130
|
+
tween.tween_method(func(v: float) -> void:
|
|
131
|
+
for m in mats:
|
|
132
|
+
m.set_shader_parameter("threshold", v),
|
|
133
|
+
0.0, 1.0, duration).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
|
|
134
|
+
if free_when_done:
|
|
135
|
+
tween.tween_callback(target.queue_free)
|
|
136
|
+
return tween
|
|
137
|
+
|
|
138
|
+
## Re-form: dissolve from gone (1.0) back to whole (0.0). For summons, teleports-in.
|
|
139
|
+
static func materialize_object(
|
|
140
|
+
target: Node3D,
|
|
141
|
+
duration: float = 1.0,
|
|
142
|
+
edge_color: Color = Color(0.55, 0.85, 1.0),
|
|
143
|
+
edge_emission: float = 6.0
|
|
144
|
+
) -> Tween:
|
|
145
|
+
var meshes := _collect_meshes(target)
|
|
146
|
+
if meshes.is_empty(): return null
|
|
147
|
+
var mats: Array[ShaderMaterial] = []
|
|
148
|
+
for mi in meshes:
|
|
149
|
+
var sm := _override_material(mi, edge_color, edge_emission)
|
|
150
|
+
if sm:
|
|
151
|
+
sm.set_shader_parameter("threshold", 1.0)
|
|
152
|
+
mats.append(sm)
|
|
153
|
+
var tween := target.create_tween()
|
|
154
|
+
tween.tween_method(func(v: float) -> void:
|
|
155
|
+
for m in mats:
|
|
156
|
+
m.set_shader_parameter("threshold", v),
|
|
157
|
+
1.0, 0.0, duration).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_OUT)
|
|
158
|
+
tween.tween_callback(func() -> void:
|
|
159
|
+
# Restore original materials by clearing overrides.
|
|
160
|
+
for mi in meshes:
|
|
161
|
+
mi.material_override = null)
|
|
162
|
+
return tween
|
|
163
|
+
|
|
164
|
+
static func _collect_meshes(root: Node) -> Array[MeshInstance3D]:
|
|
165
|
+
var out: Array[MeshInstance3D] = []
|
|
166
|
+
if root is MeshInstance3D:
|
|
167
|
+
out.append(root)
|
|
168
|
+
for child in root.get_children():
|
|
169
|
+
out.append_array(_collect_meshes(child))
|
|
170
|
+
return out
|
|
171
|
+
|
|
172
|
+
static func _override_material(mi: MeshInstance3D, edge_color: Color, edge_emission: float) -> ShaderMaterial:
|
|
173
|
+
var sm := ShaderMaterial.new()
|
|
174
|
+
sm.shader = load(SHADER_PATH)
|
|
175
|
+
sm.set_shader_parameter("threshold", 0.0)
|
|
176
|
+
sm.set_shader_parameter("edge_color", edge_color)
|
|
177
|
+
sm.set_shader_parameter("edge_emission", edge_emission)
|
|
178
|
+
sm.set_shader_parameter("noise_offset", Vector3(randf(), randf(), randf()) * 10.0)
|
|
179
|
+
|
|
180
|
+
# Try to inherit base color/texture from the original BaseMaterial3D.
|
|
181
|
+
var orig_mat := mi.get_active_material(0)
|
|
182
|
+
if orig_mat is BaseMaterial3D:
|
|
183
|
+
var bm := orig_mat as BaseMaterial3D
|
|
184
|
+
sm.set_shader_parameter("base_color", bm.albedo_color)
|
|
185
|
+
sm.set_shader_parameter("base_metallic", bm.metallic)
|
|
186
|
+
sm.set_shader_parameter("base_roughness", bm.roughness)
|
|
187
|
+
if bm.albedo_texture:
|
|
188
|
+
sm.set_shader_parameter("albedo_texture", bm.albedo_texture)
|
|
189
|
+
|
|
190
|
+
mi.material_override = sm
|
|
191
|
+
return sm
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### 4. Node tree
|
|
195
|
+
|
|
196
|
+
No new nodes added. The recipe overrides the `material_override` on every `MeshInstance3D` under the target.
|
|
197
|
+
|
|
198
|
+
```
|
|
199
|
+
<target Node3D> (e.g., the enemy)
|
|
200
|
+
├── MeshInstance3D ← gets material_override = ShaderMaterial(dissolve.gdshader)
|
|
201
|
+
├── MeshInstance3D ← also overridden
|
|
202
|
+
└── ... (Skeleton3D etc. unchanged)
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### 5. Wire it in (MCP calls)
|
|
206
|
+
|
|
207
|
+
This recipe is mostly script-driven, no scene mutation needed. Just create the files (Write tool) and call from gameplay code.
|
|
208
|
+
|
|
209
|
+
```gdscript
|
|
210
|
+
# When the enemy dies:
|
|
211
|
+
DissolveController.dissolve_object($Enemy, 1.5)
|
|
212
|
+
|
|
213
|
+
# When summoning a creature:
|
|
214
|
+
var minion: Node3D = preload("res://scenes/skeleton.tscn").instantiate()
|
|
215
|
+
add_child(minion)
|
|
216
|
+
minion.global_position = $SummoningCircle.global_position
|
|
217
|
+
DissolveController.materialize_object(minion, 1.0, Color(0.45, 0.85, 1.0))
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
If the target needs the dissolve to be cosmetic-only (no `queue_free`):
|
|
221
|
+
|
|
222
|
+
```gdscript
|
|
223
|
+
DissolveController.dissolve_object(target, 1.2, Color(1, 0.55, 0.1), 6.0, false)
|
|
224
|
+
# ...then re-materialize later
|
|
225
|
+
DissolveController.materialize_object(target, 0.8)
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### 6. Parameters to tune
|
|
229
|
+
|
|
230
|
+
| Parameter | Range | Effect |
|
|
231
|
+
|---|---|---|
|
|
232
|
+
| `duration` | 0.3–4.0 s | how slow the dissolve plays (0.6 = fast, 1.5 = dramatic, 3.0 = ritual) |
|
|
233
|
+
| `edge_width` | 0.001–0.30 | thickness of the glowing rim; thicker = more "burning", thinner = "vanishing" |
|
|
234
|
+
| `edge_color` | Color | rim glow tint; orange = fire, blue = magic, green = poison, white = holy |
|
|
235
|
+
| `edge_emission` | 0.0–16.0 | rim bloom strength; needs Bloom in WorldEnvironment |
|
|
236
|
+
| `noise_scale` | 0.5–12.0 | small = big patches dissolving; large = fine pixel-grain dust |
|
|
237
|
+
| `use_object_space` | bool | true = pattern attached to mesh; false = pattern stays in world (cool for moving objects) |
|
|
238
|
+
|
|
239
|
+
## Cookbook — named variants
|
|
240
|
+
|
|
241
|
+
### enemy-burn-death (default)
|
|
242
|
+
|
|
243
|
+
Orange edge, 1.2s, fire-y character.
|
|
244
|
+
|
|
245
|
+
```
|
|
246
|
+
duration = 1.2
|
|
247
|
+
edge_width = 0.06
|
|
248
|
+
edge_color = Color(1.0, 0.55, 0.10)
|
|
249
|
+
edge_emission = 6.0
|
|
250
|
+
noise_scale = 3.0
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### thanos-snap
|
|
254
|
+
|
|
255
|
+
Fast, fine grain, blue edge for the cosmic feel. Pair with `smoke` in pale gray for ash particles rising.
|
|
256
|
+
|
|
257
|
+
```
|
|
258
|
+
duration = 0.8
|
|
259
|
+
edge_width = 0.04
|
|
260
|
+
edge_color = Color(0.45, 0.65, 1.0)
|
|
261
|
+
edge_emission = 8.0
|
|
262
|
+
noise_scale = 8.0
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### holy-banish
|
|
266
|
+
|
|
267
|
+
Slow, white edge, big halo. Add `magic-glow` underneath for a beam of light.
|
|
268
|
+
|
|
269
|
+
```
|
|
270
|
+
duration = 2.5
|
|
271
|
+
edge_width = 0.15
|
|
272
|
+
edge_color = Color(1.0, 1.0, 0.85)
|
|
273
|
+
edge_emission = 10.0
|
|
274
|
+
noise_scale = 1.5
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### poison-melt
|
|
278
|
+
|
|
279
|
+
Slow, sickly green, watery look.
|
|
280
|
+
|
|
281
|
+
```
|
|
282
|
+
duration = 3.0
|
|
283
|
+
edge_width = 0.10
|
|
284
|
+
edge_color = Color(0.45, 1.0, 0.30)
|
|
285
|
+
edge_emission = 4.0
|
|
286
|
+
noise_scale = 5.0
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### summon-arrival (use materialize_object)
|
|
290
|
+
|
|
291
|
+
Inverse of enemy-burn-death.
|
|
292
|
+
|
|
293
|
+
```
|
|
294
|
+
duration = 0.9
|
|
295
|
+
edge_color = Color(0.55, 0.85, 1.0)
|
|
296
|
+
edge_emission = 7.0
|
|
297
|
+
noise_scale = 4.0
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
## Anti-patterns
|
|
301
|
+
|
|
302
|
+
- **Calling `dissolve_object` and then `queue_free` immediately.** The tween needs the node alive. Pass `free_when_done = true` (default) so the tween's last call queues the free.
|
|
303
|
+
- **Dissolving a mesh with translucent materials (already `transparency = ALPHA`).** The dissolve shader's `discard` works in opaque pass; translucent meshes look weird. Convert to opaque or use a custom dissolve-with-alpha shader.
|
|
304
|
+
- **Dissolving a particle system (e.g., torch flame).** The shader is `spatial`, not `particles`. Stop the particles, don't try to dissolve them.
|
|
305
|
+
- **Forgetting to seed `noise_offset` per instance.** Two enemies dissolving at the same frame use identical patterns → they look like clones. The included controller randomizes per-instance.
|
|
306
|
+
- **Using world-space noise on a moving target.** The dissolve pattern slides over the surface as the mesh moves. Use `use_object_space = true` (default).
|
|
307
|
+
- **Skipping the texture inheritance.** The mesh dissolves but goes white because `base_color` defaults to white and no texture is set. The controller pulls from the original `BaseMaterial3D`; if your mesh uses a custom shader, manually wire the textures.
|
|
308
|
+
- **Dissolving a `Skeleton3D` directly.** It's not a mesh — recurse into `MeshInstance3D` children. The controller's `_collect_meshes` does this.
|
|
309
|
+
|
|
310
|
+
## Performance notes
|
|
311
|
+
|
|
312
|
+
- Per fragment: one FBM noise call + one branch. ~0.05 ms per dissolving object at 1080p covering 10% of the screen. Cheap.
|
|
313
|
+
- Multiple meshes on a character (head, body, weapon, cape): each gets its own override. ~5 materials × 0.05 ms = 0.25 ms during the dissolve. Still fine.
|
|
314
|
+
- `discard` defeats early-Z. For a screen full of dissolving enemies, consider `cull_back` (already enabled) and avoid stacking 20+ dissolves at once.
|
|
315
|
+
- Mobile: drop `noise_scale` to a value the FBM can compute in 1–2 octaves, or replace the `fbm3` include with a single-octave noise.
|
|
316
|
+
|
|
317
|
+
## Edge cases
|
|
318
|
+
|
|
319
|
+
- **Mesh has multiple material slots.** The controller only inherits slot 0. For multi-material meshes (helmet + body different textures), extend `_override_material` to loop over all surfaces with `mi.set_surface_override_material(i, ...)`.
|
|
320
|
+
- **Skinned mesh deforming during dissolve.** Object-space noise on a skinned mesh moves with the bones — looks fine. World-space noise (`use_object_space = false`) shears with deformation.
|
|
321
|
+
- **Mesh has emission already (glowing eyes, etc.).** The dissolve shader resets EMISSION. To preserve the original emission, sample an `emission_texture` uniform and add it to the EMISSION line.
|
|
322
|
+
- **Threshold at exactly 1.0 leaves a single subpixel of geometry.** The controller passes 1.0 as the final value; in practice this dissolves everything because no fbm3 returns above 1.0. If it doesn't on your noise, clamp the final value to 1.05.
|
|
323
|
+
- **Re-materializing while the dissolve is still tweening.** Both tweens run concurrently. Kill the previous tween first: store the returned `Tween` and call `kill()` before starting the next.
|
|
324
|
+
|
|
325
|
+
## Fallback (no MCP)
|
|
326
|
+
|
|
327
|
+
VFX is code, no MCP required:
|
|
328
|
+
|
|
329
|
+
1. Create `addons/vfx/dissolve/` and write the two files above.
|
|
330
|
+
2. From any gameplay script, call `DissolveController.dissolve_object(target_node)`.
|
|
331
|
+
3. The controller handles material override, texture inheritance, tween, and freeing.
|
|
332
|
+
|
|
333
|
+
## Handoff
|
|
334
|
+
|
|
335
|
+
After firing this recipe, suggest:
|
|
336
|
+
|
|
337
|
+
- `summer:visual-effects/recipes/smoke` — recolor pale gray, spawn at the target's bounds for ash particles. Especially good for `thanos-snap`.
|
|
338
|
+
- `summer:visual-effects/recipes/magic-glow` — for `holy-banish` and `summon-arrival` variants, add a vertical beam of light at the target.
|
|
339
|
+
- `summer:visual-effects/recipes/fire` — pair `enemy-burn-death` with a brief flame burst at the start of the dissolve.
|
|
340
|
+
- `summer:visual-effects/game-feel` — add a slow-mo on enemy death (`Engine.time_scale = 0.4` for 0.3 s) to emphasize the dissolve.
|
|
341
|
+
- `summer:audio/sound-effect` — generate `magical disintegration whoosh, fading shimmer, 1.5s` and play in sync.
|
|
342
|
+
|
|
343
|
+
## See also
|
|
344
|
+
|
|
345
|
+
- `_building-blocks/noise-3d-fbm.gdshaderinc` — the FBM noise this shader includes
|
|
346
|
+
- `summer:visual-effects/recipes/smoke` — pair for the ash cloud
|
|
347
|
+
- `summer:visual-effects/recipes/magic-glow` — for arrival/departure beams
|
|
348
|
+
- `summer:visual-effects/recipes/fire` — for the burn-up variant pairing
|
|
349
|
+
- `summer:gdscript-patterns` — for the static-API class pattern
|