96 lines
4.3 KiB
C#
96 lines
4.3 KiB
C#
// Assets/_Project/Scripts/VFX/TowerDropReticle.cs
|
|
using UnityEngine;
|
|
using UnityEngine.Rendering.Universal;
|
|
|
|
namespace TD.VFX
|
|
{
|
|
/// <summary>
|
|
/// Local visual for a tower-drop targeting reticle — the ground decal, the vertical
|
|
/// energy beam, and the rising motes, bundled under one root. It is spawned as a child
|
|
/// of a <c>BuildSiteVisual</c>, which feeds it construction progress via
|
|
/// <see cref="SetProgress"/> each frame. The decal (and optionally the beam) shrink/fade
|
|
/// as progress climbs toward 1 through the shader's <c>_Progress</c> property.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <para>Pure visualization — no networking. It runs on every peer because the owning
|
|
/// <c>BuildSiteVisual</c> spawns on every peer, and it reads the build site's already-
|
|
/// replicated progress. It is destroyed automatically when the build site despawns at
|
|
/// construction-complete or cancel (it's a child of that NetworkObject).</para>
|
|
///
|
|
/// <para><b>Per-instance materials.</b> Several towers can build at once, so each reticle
|
|
/// needs its own material — a shared material would make every reticle share one
|
|
/// <c>_Progress</c> value and shrink in lockstep. The decal's material is cloned here;
|
|
/// beam renderers use <see cref="Renderer.material"/>, which auto-instantiates.</para>
|
|
/// </remarks>
|
|
public class TowerDropReticle : MonoBehaviour
|
|
{
|
|
[Tooltip("Ground decal projector. Its material's _Progress drives the reticle shrink/fade. " +
|
|
"Auto-found in children if left empty.")]
|
|
[SerializeField] private DecalProjector decal;
|
|
|
|
[Tooltip("Optional extra renderers (e.g. the beam) whose material also exposes _Progress " +
|
|
"so they fade with construction. Renderers without the property are skipped.")]
|
|
[SerializeField] private Renderer[] progressRenderers;
|
|
|
|
[Tooltip("Shader float property that drives the shrink: 0 = full size, 1 = complete/gone.")]
|
|
[SerializeField] private string progressProperty = "_Progress";
|
|
|
|
private int progressId;
|
|
private Material decalMaterialInstance;
|
|
private Material[] rendererMaterialInstances;
|
|
|
|
private void Awake()
|
|
{
|
|
progressId = Shader.PropertyToID(progressProperty);
|
|
|
|
if (decal == null) decal = GetComponentInChildren<DecalProjector>();
|
|
|
|
// Clone the decal material so this reticle animates independently of any other
|
|
// build site sharing the same source material asset.
|
|
if (decal != null && decal.material != null)
|
|
{
|
|
decalMaterialInstance = new Material(decal.material);
|
|
decal.material = decalMaterialInstance;
|
|
}
|
|
|
|
// Renderer.material returns a per-renderer instance, so these are already unique.
|
|
if (progressRenderers != null && progressRenderers.Length > 0)
|
|
{
|
|
rendererMaterialInstances = new Material[progressRenderers.Length];
|
|
for (int i = 0; i < progressRenderers.Length; i++)
|
|
if (progressRenderers[i] != null)
|
|
rendererMaterialInstances[i] = progressRenderers[i].material;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Feed normalized construction progress. 0 = build just started (reticle full size);
|
|
/// 1 = complete (reticle shrunk away). Safe to call every frame on any peer.
|
|
/// </summary>
|
|
public void SetProgress(float t01)
|
|
{
|
|
t01 = Mathf.Clamp01(t01);
|
|
|
|
if (decalMaterialInstance != null)
|
|
decalMaterialInstance.SetFloat(progressId, t01);
|
|
|
|
if (rendererMaterialInstances != null)
|
|
{
|
|
for (int i = 0; i < rendererMaterialInstances.Length; i++)
|
|
{
|
|
var mat = rendererMaterialInstances[i];
|
|
if (mat != null && mat.HasProperty(progressId))
|
|
mat.SetFloat(progressId, t01);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void OnDestroy()
|
|
{
|
|
// The cloned decal material is ours to free. Renderer material instances are
|
|
// owned by their renderers and cleaned up when those GameObjects are destroyed.
|
|
if (decalMaterialInstance != null)
|
|
Destroy(decalMaterialInstance);
|
|
}
|
|
}
|
|
}
|