Expose the camo knobs in the HUD, persist and replicate them
Surface the PlayerCamoLit material knobs (HalfLambert, HalfLambert brightness, specular dampen) as material-backed properties on MeshPainter, guarded by an IsReady gate since the HUD binds before the material instance exists. Wire the three HUD sliders to them and seed the sliders from the live material once the painter is ready. Persist the values into the same HUD JSON as the brush settings (with a hasCamoValues flag so pre-camo save files never clobber the authored material) and carry them across a scene-switch respawn. Replicate them owner-authoritatively via a NetworkVariable in PlayerPaintSync, exactly like emission intensity, so remotes and late joiners show the same camo. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
b5bd1fdb8b
commit
7c84f6ba92
4 files changed
+187
-1
No files matched your search
@@ -84,6 +84,16 @@ public class PlayerPaintSync : NetworkBehaviour
|
||||
private readonly NetworkVariable<float> _emissionIntensity =
|
||||
new(1f, NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Owner);
|
||||
|
||||
// Per-player camouflage knobs on the PlayerCamoLit material (x = HalfLambert, y = HalfLambert
|
||||
// brightness, z = specular dampen). Exactly like emission intensity these are per-player material
|
||||
// values, not painted into the maps, so they ride their own owner-authoritative NetworkVariable. The
|
||||
// remote applies them to its material once its painter is ready and again on every change; a late
|
||||
// joiner gets the current value on spawn for free. The default mirrors Player.mat's authored camo so
|
||||
// the brief pre-publish spawn frame shows no flash (the owner re-publishes the real value once ready).
|
||||
private readonly NetworkVariable<Vector3> _camo =
|
||||
new(new Vector3(0.5f, 0f, 0.1f), NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Owner);
|
||||
private bool _camoApplyPending; // remote: apply _camo.Value to the material once IsReady / on change
|
||||
|
||||
// Monotonic message counter on the owner (one per stamp batch / checkpoint / restore); on every peer,
|
||||
// the highest sequence accounted for (applied to a remote replica / sent by the owner). Used as the
|
||||
// snapshot's cutoff marker so the join window neither loses nor double-applies events.
|
||||
@@ -149,6 +159,11 @@ public class PlayerPaintSync : NetworkBehaviour
|
||||
_emissionIntensity.OnValueChanged += OnEmissionChanged;
|
||||
_painter.emissionIntensity = _emissionIntensity.Value;
|
||||
|
||||
// Track the owner's camo knobs; apply the current (late-join) value once the material exists
|
||||
// (deferred to LateUpdate - the setter is a no-op before the painter's Start builds the instance).
|
||||
_camo.OnValueChanged += OnCamoChanged;
|
||||
_camoApplyPending = true;
|
||||
|
||||
// The server (host) has been present since the start, so its replica already holds the full
|
||||
// paint via the live stream; only a genuine late joiner needs the baseline pulled over.
|
||||
if (!IsServer)
|
||||
@@ -175,6 +190,7 @@ public class PlayerPaintSync : NetworkBehaviour
|
||||
_painter.OnHistoryRestore -= OnLocalRestore;
|
||||
}
|
||||
_emissionIntensity.OnValueChanged -= OnEmissionChanged;
|
||||
_camo.OnValueChanged -= OnCamoChanged;
|
||||
}
|
||||
|
||||
// Owner history events -> appended to the ordered outgoing FIFO, flushed in LateUpdate.
|
||||
@@ -202,6 +218,10 @@ public class PlayerPaintSync : NetworkBehaviour
|
||||
if (_painter) _painter.emissionIntensity = current;
|
||||
}
|
||||
|
||||
// Apply on the next LateUpdate once the material is ready (the camo setters are a no-op before that,
|
||||
// and on a fresh spawn the OnValueChanged may arrive before the painter's Start has built the instance).
|
||||
private void OnCamoChanged(Vector3 previous, Vector3 current) => _camoApplyPending = true;
|
||||
|
||||
// ---- Live stamp stream (owner -> server -> remotes) ----
|
||||
|
||||
private void OnStampCommitted(PaintStampNet stamp) =>
|
||||
@@ -221,6 +241,17 @@ public class PlayerPaintSync : NetworkBehaviour
|
||||
_awaitingSnapshot = false;
|
||||
}
|
||||
|
||||
// Remote replica: apply the owner's camo onto this avatar's material once it exists / whenever it
|
||||
// changed. The owner drives its own material via the HUD + persistence, so it never applies here.
|
||||
if (!IsOwner && _camoApplyPending && _painter && _painter.IsReady)
|
||||
{
|
||||
Vector3 c = _camo.Value;
|
||||
_painter.CamoHalfLambert = c.x;
|
||||
_painter.CamoHalfLambertBrightness = c.y;
|
||||
_painter.CamoSpecularDampen = c.z;
|
||||
_camoApplyPending = false;
|
||||
}
|
||||
|
||||
if (!IsOwner)
|
||||
return;
|
||||
|
||||
@@ -243,6 +274,16 @@ public class PlayerPaintSync : NetworkBehaviour
|
||||
if (_painter && _emissionIntensity.Value != _painter.emissionIntensity)
|
||||
_emissionIntensity.Value = _painter.emissionIntensity;
|
||||
|
||||
// Publish the owner's camo knobs when the HUD/persistence changes them (delta-only; the
|
||||
// NetworkVariable dirties only on an actual change). Gated on IsReady so the pre-Start 0-read is
|
||||
// never published - that would briefly flatten remotes' lighting until the real value lands.
|
||||
if (_painter && _painter.IsReady)
|
||||
{
|
||||
var camo = new Vector3(_painter.CamoHalfLambert, _painter.CamoHalfLambertBrightness, _painter.CamoSpecularDampen);
|
||||
if (_camo.Value != camo)
|
||||
_camo.Value = camo;
|
||||
}
|
||||
|
||||
FlushOutgoing(unlimited: false);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user