From cad5015d42b8b45b85b38c8e2521714d9f6468c0 Mon Sep 17 00:00:00 2001 From: MelekTaus Moribundus Date: Mon, 6 Jul 2026 12:23:42 +0200 Subject: [PATCH] Radar: fade in/out statt hartem Enable-Toggle Gemeinsamer 0..1-Fade-Faktor treibt Disc-Alpha (baseAlpha * fade via MPB), Blip-Alpha (RadarTarget, im Gleichschritt) und Radar-Scale (0.8 -> 1.0). Fade-in/-out-Speed separat einstellbar; Kamera bleibt an solange fade > 0. Co-Authored-By: Claude Opus 4.8 --- Assets/Scripts/Components/Radar.cs | 89 +++++++++++++++++++++--- Assets/Scripts/Components/RadarTarget.cs | 19 ++++- 2 files changed, 96 insertions(+), 12 deletions(-) diff --git a/Assets/Scripts/Components/Radar.cs b/Assets/Scripts/Components/Radar.cs index 4a70141..8c3bfd3 100644 --- a/Assets/Scripts/Components/Radar.cs +++ b/Assets/Scripts/Components/Radar.cs @@ -8,8 +8,10 @@ using UnityEngine; /// destroys itself after a given lifetime. See for the per-blip behaviour. /// /// The whole radar overlay is only shown while it is useful: at least one live target, first-person is active, -/// AND the match is in its Seek phase. It is hidden by switching the overlay camera's rendering off (not by -/// deactivating the GameObjects), so hidden blips keep counting down their lifetime and self-destruct on schedule. +/// AND the match is in its Seek phase. Instead of snapping on/off it fades: a shared 0..1 factor () +/// drives the alpha of every (transparent) radar renderer - the disc here, the blip points in - +/// and scales the disc from up to 1. Fade-in and fade-out speeds are independent. The overlay +/// camera is switched off only once fully faded out, so hidden blips keep counting down their lifetime meanwhile. /// [DisallowMultipleComponent] public sealed class Radar : MonoBehaviour @@ -31,9 +33,28 @@ public sealed class Radar : MonoBehaviour [Tooltip("First-person rig. The radar is only shown while first-person is active. Empty = found automatically.")] [SerializeField] private FirstPersonController firstPerson; + [Header("Fade")] + [Tooltip("Fade-in speed in alpha units per second (0 -> 1). e.g. 5 = ~0.2s to fully appear.")] + [SerializeField] private float fadeInSpeed = 5f; + [Tooltip("Fade-out speed in alpha units per second (1 -> 0). e.g. 3 = ~0.33s to fully vanish.")] + [SerializeField] private float fadeOutSpeed = 3f; + [Tooltip("Disc scale multiplier while fully hidden (fade 0); grows to 1 as it fades in, and back down on fade-out.")] + [SerializeField] private float hiddenScale = 0.8f; + // Live blips, pruned as they self-destruct; the radar shows only while at least one is alive. private readonly List _blips = new List(); + // Shared 0..1 visibility factor: drives disc/blip alpha and disc scale. Ramped in LateUpdate. + private float _fade; + /// Current 0..1 fade factor. Blips multiply their point alpha by this so they fade in step with the disc. + public float CurrentFade => _fade; + + private Vector3 _baseScale; // authored disc scale, captured once; fade lerps hiddenScale..1 on top + private Renderer[] _staticRenderers; // radar's own renderers (the disc); the template subtree is excluded + private Color[] _staticBaseColors; // their full-opacity _BaseColor; alpha is scaled by _fade each frame + private MaterialPropertyBlock _mpb; + private static readonly int BaseColorId = Shader.PropertyToID("_BaseColor"); + private void Awake() { Instance = this; @@ -47,7 +68,30 @@ public sealed class Radar : MonoBehaviour if (!radarCamera) radarCamera = GetComponentInParent(); if (!firstPerson) firstPerson = FindFirstObjectByType(FindObjectsInactive.Include); - ApplyVisibility(false); // hidden until a target exists, first-person is live AND the match is seeking + _baseScale = transform.localScale; + CacheStaticRenderers(); + + _fade = 0f; // hidden until a target exists, first-person is live AND the match is seeking + ApplyFade(); + } + + // Gathers the radar's own renderers (the disc) once, with their full-opacity _BaseColor, so their alpha can be + // scaled by _fade each frame. The "Target Template" subtree is skipped: it is the blip prototype, and its clones + // fade themselves via RadarTarget (which owns their per-blip colour). + private void CacheStaticRenderers() + { + var all = GetComponentsInChildren(true); + var renderers = new List(all.Length); + var colors = new List(all.Length); + foreach (var r in all) + { + if (targetTemplate && r.transform.IsChildOf(targetTemplate)) continue; + renderers.Add(r); + var mat = r.sharedMaterial; + colors.Add(mat && mat.HasProperty(BaseColorId) ? mat.GetColor(BaseColorId) : Color.white); + } + _staticRenderers = renderers.ToArray(); + _staticBaseColors = colors.ToArray(); } private void OnDestroy() @@ -93,7 +137,14 @@ public sealed class Radar : MonoBehaviour if (!_blips[i]) _blips.RemoveAt(i); bool visible = _blips.Count > 0 && firstPerson && firstPerson.enabled && InSeekPhase(); - ApplyVisibility(visible); + + // Ramp the shared factor toward shown/hidden with the matching speed, so the whole overlay fades + // (alpha + scale) instead of snapping. A mid-fade visibility flip reverses smoothly. + float target = visible ? 1f : 0f; + float speed = Mathf.Max(0f, visible ? fadeInSpeed : fadeOutSpeed); + _fade = Mathf.MoveTowards(_fade, target, speed * Time.deltaTime); + + ApplyFade(); } // The radar only makes sense during the seek phase (hunters chasing hiders). Blips only spawn then anyway, @@ -104,12 +155,30 @@ public sealed class Radar : MonoBehaviour return m && m.MatchActive && m.LocalSubPhase == MatchManager.MatchSubPhase.Seek; } - // Shows/hides the whole radar overlay in one toggle (disc + blips) by switching the overlay camera's - // rendering. The GameObjects stay active, so blips keep ticking down their lifetime while hidden. The - // overlay renders only the Radar layer, so nothing else is affected. - private void ApplyVisibility(bool visible) + // Applies the current _fade to the whole overlay: disc scale (hiddenScale..1), disc alpha (baseAlpha*fade), + // and the overlay camera - switched off only once fully faded out, so blips keep ticking their lifetime while + // hidden and no rendering happens for an invisible radar. Blips fade themselves via RadarTarget/CurrentFade. + private void ApplyFade() { - if (radarCamera && radarCamera.enabled != visible) - radarCamera.enabled = visible; + transform.localScale = _baseScale * Mathf.Lerp(hiddenScale, 1f, _fade); + + if (_staticRenderers != null) + { + _mpb ??= new MaterialPropertyBlock(); + for (int i = 0; i < _staticRenderers.Length; i++) + { + var r = _staticRenderers[i]; + if (!r) continue; + Color c = _staticBaseColors[i]; + c.a *= _fade; + r.GetPropertyBlock(_mpb); + _mpb.SetColor(BaseColorId, c); + r.SetPropertyBlock(_mpb); + } + } + + bool render = _fade > 0.0001f; + if (radarCamera && radarCamera.enabled != render) + radarCamera.enabled = render; } } diff --git a/Assets/Scripts/Components/RadarTarget.cs b/Assets/Scripts/Components/RadarTarget.cs index f519796..3a4c2a8 100644 --- a/Assets/Scripts/Components/RadarTarget.cs +++ b/Assets/Scripts/Components/RadarTarget.cs @@ -27,6 +27,7 @@ public sealed class RadarTarget : MonoBehaviour private bool _initialised; private MaterialPropertyBlock _mpb; + private Color _pointColor = Color.white; // last gradient-evaluated point colour; its alpha is scaled by the radar fade private static readonly int BaseColorId = Shader.PropertyToID("_BaseColor"); public Transform Target => _target; @@ -40,6 +41,7 @@ public sealed class RadarTarget : MonoBehaviour _age = 0f; _initialised = true; Aim(); + PushPointColor(); } // LateUpdate so the aim uses the camera's final position/rotation for this frame. @@ -62,6 +64,7 @@ public sealed class RadarTarget : MonoBehaviour } Aim(); + PushPointColor(); // every frame so the blip's alpha tracks the radar fade even when Aim early-returns } private void Aim() @@ -88,13 +91,25 @@ public sealed class RadarTarget : MonoBehaviour } // Tints the point's base colour by how far the target sits ahead of (0) vs directly behind (1) the camera. + // Only caches the colour here; PushPointColor writes it (with the fade applied) so the alpha can track the + // radar fade every frame independent of when the bearing last changed. private void ApplyForwardColor(Vector3 dirNormalized) { - if (!pointRenderer || forwardGradient == null) return; + if (forwardGradient == null) return; float t = Vector3.Angle(_camera.transform.forward, dirNormalized) / 180f; + _pointColor = forwardGradient.Evaluate(t); + } + + // Writes the point's _BaseColor with its alpha scaled by the radar's shared fade factor, so blips fade in/out + // in step with the disc instead of the overlay snapping on/off. + private void PushPointColor() + { + if (!pointRenderer) return; + Color c = _pointColor; + c.a *= Radar.Instance ? Radar.Instance.CurrentFade : 1f; _mpb ??= new MaterialPropertyBlock(); pointRenderer.GetPropertyBlock(_mpb); - _mpb.SetColor(BaseColorId, forwardGradient.Evaluate(t)); + _mpb.SetColor(BaseColorId, c); pointRenderer.SetPropertyBlock(_mpb); } }