Adding a ton of funcitonality to the builder's movement and build queue
This commit is contained in:
parent
a63cce53e2
commit
f05734e19b
31 changed files with 3104 additions and 339 deletions
126
Assets/_Project/Scripts/Gameplay/SelectionRingVisual.cs
Normal file
126
Assets/_Project/Scripts/Gameplay/SelectionRingVisual.cs
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
// Assets/_Project/Scripts/Gameplay/SelectionRingVisual.cs
|
||||
using UnityEngine;
|
||||
|
||||
namespace TD.Gameplay
|
||||
{
|
||||
/// <summary>
|
||||
/// Local-only visual indicator for builder selection. Sits as a child of the
|
||||
/// builder prefab. Subscribes to <see cref="SelectionState.OnSelectionChanged"/>
|
||||
/// and toggles the visibility of its own renderers (and any descendant
|
||||
/// renderers) when the parent builder becomes selected/unselected.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para><b>Pure local visualization.</b> No NetworkBehaviour. Selection is a
|
||||
/// UI concept — every client renders selection state for its own player only.
|
||||
/// This component has no networked state.</para>
|
||||
///
|
||||
/// <para><b>Why a separate component.</b> Keeps Builder focused on gameplay
|
||||
/// state. The visual prefab structure can change independently
|
||||
/// (disc → ring → decal projector → animated effect) without touching gameplay
|
||||
/// code. The contract is just "renderers visible when selected."</para>
|
||||
///
|
||||
/// <para><b>Why renderer-toggle, not GameObject.SetActive.</b> Disabling our
|
||||
/// own GameObject would prevent OnEnable/Update from running, which would
|
||||
/// break the SelectionState subscription lifecycle (we'd never receive the
|
||||
/// "you're selected again" event). Toggling the renderers' enabled state
|
||||
/// achieves the same visual effect without breaking the event flow.</para>
|
||||
///
|
||||
/// <para><b>Prefab setup.</b> Attach this component to a child GameObject of
|
||||
/// the Builder prefab. The child carries (or has descendants carrying) a
|
||||
/// flattened cylinder mesh sitting just above the ground plane, with an
|
||||
/// unlit transparent green material. The component handles initial visibility
|
||||
/// — leave the renderer enabled in the prefab; we'll turn it off in Awake
|
||||
/// until selection fires.</para>
|
||||
/// </remarks>
|
||||
public class SelectionRingVisual : MonoBehaviour
|
||||
{
|
||||
// Cached parent builder, resolved in Awake.
|
||||
private Builder parentBuilder;
|
||||
|
||||
// Cached renderers (this object plus all descendants). Captured once in
|
||||
// Awake to avoid per-event GetComponentsInChildren allocations.
|
||||
private Renderer[] cachedRenderers;
|
||||
|
||||
// Tracks subscription state so OnDisable / OnDestroy unsubscribe correctly,
|
||||
// and so Update can retry subscription if SelectionState wasn't ready at OnEnable.
|
||||
private bool subscribed;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
parentBuilder = GetComponentInParent<Builder>();
|
||||
if (parentBuilder == null)
|
||||
{
|
||||
Debug.LogError("[SelectionRingVisual] No Builder component found on " +
|
||||
"self or any parent. Disabling.");
|
||||
enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
cachedRenderers = GetComponentsInChildren<Renderer>(includeInactive: true);
|
||||
|
||||
// Start hidden. If selection fires later (including the auto-select
|
||||
// in Builder.OnNetworkSpawn), HandleSelectionChanged will turn the
|
||||
// renderers back on.
|
||||
SetRenderersVisible(false);
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
TrySubscribe();
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
Unsubscribe();
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
Unsubscribe();
|
||||
}
|
||||
|
||||
// Per-frame fallback: if SelectionState wasn't available at OnEnable
|
||||
// (scene load ordering), keep trying. Cost is one null-check per frame
|
||||
// until subscription succeeds, then nothing.
|
||||
private void Update()
|
||||
{
|
||||
if (!subscribed) TrySubscribe();
|
||||
}
|
||||
|
||||
private void TrySubscribe()
|
||||
{
|
||||
if (subscribed) return;
|
||||
var sel = SelectionState.Instance;
|
||||
if (sel == null) return;
|
||||
|
||||
sel.OnSelectionChanged += HandleSelectionChanged;
|
||||
subscribed = true;
|
||||
|
||||
// Sync to current state — selection may have happened before we subscribed
|
||||
// (e.g., Builder.OnNetworkSpawn auto-selecting before this Awake runs).
|
||||
HandleSelectionChanged(sel.SelectedBuilder);
|
||||
}
|
||||
|
||||
private void Unsubscribe()
|
||||
{
|
||||
if (!subscribed) return;
|
||||
var sel = SelectionState.Instance;
|
||||
if (sel != null) sel.OnSelectionChanged -= HandleSelectionChanged;
|
||||
subscribed = false;
|
||||
}
|
||||
|
||||
private void HandleSelectionChanged(Builder newSelection)
|
||||
{
|
||||
SetRenderersVisible(newSelection == parentBuilder);
|
||||
}
|
||||
|
||||
private void SetRenderersVisible(bool visible)
|
||||
{
|
||||
if (cachedRenderers == null) return;
|
||||
foreach (var rend in cachedRenderers)
|
||||
{
|
||||
if (rend != null) rend.enabled = visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue