Decals: explosion scorch pool + cover damage-cracks + room-arena scars (Phase 1.5b bundle 2)
All procedural, client-only, observe-only in PresentationSystemGroup, zero netcode surface (read existing replicated state). Shared FeedbackFx decal primitives (scorch blob + crack star meshes + transparent decal material). ScorchDecalSystem: static RequestScorch queue -> pooled fading discs (mirrors DynamicLightSystem), wired from the barrel boom, reusable by the geyser. CoverDamageSystem: crack accretion keyed on BlightClutter.Remaining (Variant 4) + proximity-gated shatter-scorch. RoomDressingSystem: persistent arena scars on a distinct hash sub-stream, torn down with dressing. Knobs in DecalConfig. Cover cracks use decals (not URPMaterialPropertyBaseColor) because the Synty prop shader is not Hybrid-Per-Instance. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -109,5 +109,76 @@ namespace ProjectM.Client
|
||||
if (clip == null) return;
|
||||
AudioSource.PlayClipAtPoint(clip, pos, vol * GameVolume.Sfx);
|
||||
}
|
||||
|
||||
// ---- ground DECAL primitives (Bundle 2 — explosion scorch, cover cracks, room scars) ----
|
||||
|
||||
// Transparent unlit ground-decal material: Sprites/Default honours vertex colour × the per-renderer _Color
|
||||
// MPB, blends alpha, is double-sided (Cull Off) and writes no depth (ZWrite Off) — so flat, overlapping
|
||||
// ground decals never z-fight and never cast/receive shadows. Same shader family as the fuse disc.
|
||||
public static Material MakeDecalMaterial(string name = "GroundDecal")
|
||||
{
|
||||
Shader sh = Shader.Find("Sprites/Default");
|
||||
if (sh == null) sh = Shader.Find("Universal Render Pipeline/Particles/Unlit");
|
||||
if (sh == null) sh = Shader.Find("Unlit/Transparent");
|
||||
return new Material(sh) { name = name, renderQueue = 3000 }; // Transparent
|
||||
}
|
||||
|
||||
// Irregular soft radial disc on the XZ plane (already flat — scale x/z, spin about Y): vertex alpha 1 at the
|
||||
// centre fading to 0 at a JITTERED rim, so it reads as an organic scorch blob with NO hard rectangle edge
|
||||
// (the feathered rim is what keeps it from ghosting over fog the way an opaque quad did). White vertex colour
|
||||
// so the caller's material/MPB tint (dark char) shows through.
|
||||
public static Mesh BuildScorchMesh(int segments = 24, float jitter = 0.3f, int seed = 1)
|
||||
{
|
||||
if (segments < 6) segments = 6;
|
||||
var rng = new System.Random(seed * 6151 + 13);
|
||||
var m = new Mesh { name = "ScorchDecal" };
|
||||
var v = new Vector3[segments + 1];
|
||||
var col = new Color[segments + 1];
|
||||
var tris = new int[segments * 3];
|
||||
v[0] = Vector3.zero;
|
||||
col[0] = new Color(1f, 1f, 1f, 1f);
|
||||
for (int i = 0; i < segments; i++)
|
||||
{
|
||||
float a = i / (float)segments * Mathf.PI * 2f;
|
||||
float r = 1f - jitter * (float)rng.NextDouble();
|
||||
v[i + 1] = new Vector3(Mathf.Cos(a) * r, 0f, Mathf.Sin(a) * r);
|
||||
col[i + 1] = new Color(1f, 1f, 1f, 0f); // transparent feathered rim
|
||||
int n = (i + 1) % segments;
|
||||
tris[i * 3] = 0; tris[i * 3 + 1] = n + 1; tris[i * 3 + 2] = i + 1;
|
||||
}
|
||||
m.vertices = v; m.colors = col; m.triangles = tris;
|
||||
m.RecalculateBounds();
|
||||
return m;
|
||||
}
|
||||
|
||||
// A jagged crack star on the XZ plane: `spokes` thin tapered triangles radiating from the centre at random
|
||||
// angles/lengths, opaque at the base and feathered to nothing at the tip. Reads as surface cracking, visually
|
||||
// distinct from the scorch blob. White vertex colour; caller tints dark and scales it to the piece.
|
||||
public static Mesh BuildCrackMesh(int spokes = 3, int seed = 1)
|
||||
{
|
||||
if (spokes < 1) spokes = 1;
|
||||
var rng = new System.Random(seed * 9277 + 5);
|
||||
var m = new Mesh { name = "CrackDecal" };
|
||||
var v = new Vector3[spokes * 3];
|
||||
var col = new Color[spokes * 3];
|
||||
var tris = new int[spokes * 3];
|
||||
for (int s = 0; s < spokes; s++)
|
||||
{
|
||||
float a = (s / (float)spokes) * Mathf.PI * 2f + (float)(rng.NextDouble() - 0.5) * 1.2f;
|
||||
float len = 0.6f + 0.4f * (float)rng.NextDouble();
|
||||
float w = 0.06f + 0.05f * (float)rng.NextDouble();
|
||||
var dir = new Vector3(Mathf.Cos(a), 0f, Mathf.Sin(a));
|
||||
var perp = new Vector3(-dir.z, 0f, dir.x) * w;
|
||||
int b = s * 3;
|
||||
v[b] = perp; v[b + 1] = -perp; v[b + 2] = dir * len;
|
||||
col[b] = new Color(1f, 1f, 1f, 1f);
|
||||
col[b + 1] = new Color(1f, 1f, 1f, 1f);
|
||||
col[b + 2] = new Color(1f, 1f, 1f, 0f); // feathered tip
|
||||
tris[b] = b; tris[b + 1] = b + 2; tris[b + 2] = b + 1;
|
||||
}
|
||||
m.vertices = v; m.colors = col; m.triangles = tris;
|
||||
m.RecalculateBounds();
|
||||
return m;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user