Stop paint particles when no stamp lands during a held stroke

Particles kept playing at the last hit position when the cursor left the
mesh mid-stroke. New _stampLandedThisFrame flag: reset before each stamp
attempt, set only where the dab actually paints (after placement and
corner guard); no landed stamp -> StopPaintParticles immediately.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Fable 5 committed 2026-07-11 22:21:56 +02:00
1 parent 094cf3a493
commit 1d5676ab3c
1 file changed
+13 -5
@@ -335,6 +335,7 @@ public partial class MeshPainter : MonoBehaviour
"(Start Color is overwritten). +Z of the instance points along the surface normal.")]
public ParticleSystem paintParticlesPrefab;
private ParticleSystem _paintParticles;
private bool _stampLandedThisFrame; // any dab actually hit the mesh this frame (gates the particles)
[Header("Canvas")]
[Tooltip("Resolution of the paint textures (square).")]
@@ -940,6 +941,7 @@ public partial class MeshPainter : MonoBehaviour
if (stamp)
{
_stampLandedThisFrame = false;
BeginBackendStroke();
if (!firstPress && paintInterval <= 0f)
@@ -957,6 +959,10 @@ public partial class MeshPainter : MonoBehaviour
}
EndBackendStroke();
// Held button but nothing landed (cursor off the mesh / corner guard): kill the splatter
// immediately instead of letting it play at the last hit until release.
if (!_stampLandedThisFrame) StopPaintParticles();
}
_lastScreen = screenPos;
@@ -986,6 +992,7 @@ public partial class MeshPainter : MonoBehaviour
else UseResolvedStampMask();
DrawBackendStamp(pLocal, radiusLocal, u, v, nLocal);
_stampLandedThisFrame = true;
UpdatePaintParticles(pLocal, radiusLocal, nLocal);
EmitStamp(pLocal, radiusLocal, u, v, nLocal);
@@ -994,8 +1001,8 @@ public partial class MeshPainter : MonoBehaviour
/// <summary>Moves/scales/tints the stroke particle effect at a landed stamp and keeps it playing.
/// Scale = brush world radius (author the prefab for radius 1, scaling mode Hierarchy), tint =
/// brush albedo color with a small per-particle random offset (alpha forced opaque - the brush
/// alpha is paint opacity, not particle fade).</summary>
/// brush albedo color with a small per-particle random offset; particle alpha = brush albedo
/// alpha (faint paint = faint splatter).</summary>
private void UpdatePaintParticles(Vector3 pLocal, float radiusLocal, Vector3 nLocal)
{
if (!paintParticlesPrefab) return;
@@ -1009,11 +1016,12 @@ public partial class MeshPainter : MonoBehaviour
var main = _paintParticles.main;
Color c = brushColor;
c.a = 1f;
// Per-particle random tint offset around the albedo (darker <-> lighter).
// Per-particle random tint offset around the albedo (darker <-> lighter);
// alpha follows the brush albedo alpha (paint opacity) so faint paint splatters faintly;
// +0.4 bias keeps the splatter readable even at low paint opacity.
Color dark = Color.Lerp(c, Color.black, 0.13f);
Color light = Color.Lerp(c, Color.white, 0.09f);
dark.a = light.a = 1f;
dark.a = light.a = Mathf.Clamp01(c.a + 0.4f);
main.startColor = new ParticleSystem.MinMaxGradient(dark, light);
if (!_paintParticles.isPlaying) _paintParticles.Play();