Initial commit: AIGame Unity client with URP, HybridCLR, YooAsset and tooling.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+120
@@ -0,0 +1,120 @@
|
||||
using UnityEngine;
|
||||
using Unity.Pipeline.CodeReload;
|
||||
|
||||
namespace Unity.Pipeline
|
||||
{
|
||||
/// <summary>
|
||||
/// Minimal on-screen code-reload status for players: what is happening now (compiling → applied,
|
||||
/// or failed) and how many method overrides are active. Deliberately IMGUI — no canvas,
|
||||
/// EventSystem or render-pipeline dependency, and it is development-only anyway (the body
|
||||
/// compiles out of release players; the type itself stays so AddComponent call sites compile).
|
||||
/// Added automatically by <see cref="RuntimePipelineDriver"/> when its
|
||||
/// reload-overlay flag is on. Hidden entirely while idle with no overrides.
|
||||
/// </summary>
|
||||
[DisallowMultipleComponent]
|
||||
class CodeReloadStatusOverlay : MonoBehaviour
|
||||
{
|
||||
#if UNITY_EDITOR || DEVELOPMENT_BUILD || ENABLE_RUNTIME_PIPELINE
|
||||
// How long the "applied" confirmation stays up before falling back to the count line.
|
||||
private const int AppliedFlashMs = 3500;
|
||||
// A "compiling…" with no follow-up payload or failure notice goes stale (editor died,
|
||||
// connection dropped) — stop showing it after this.
|
||||
private const int CompilingStaleMs = 15000;
|
||||
private const float StatsPollSeconds = 1f;
|
||||
private const int MaxMessageLength = 96;
|
||||
|
||||
private int m_OverrideCount;
|
||||
private int m_CallsPerSecond;
|
||||
private long m_LastCallCount;
|
||||
private float m_LastPollTime;
|
||||
private float m_NextStatsPoll;
|
||||
private GUIStyle m_Style;
|
||||
|
||||
private void Update()
|
||||
{
|
||||
// GetStats() allocates; poll at 1 Hz instead of per frame.
|
||||
if (Time.unscaledTime >= m_NextStatsPoll)
|
||||
{
|
||||
m_NextStatsPoll = Time.unscaledTime + StatsPollSeconds;
|
||||
m_OverrideCount = CodeReloadRegistry.GetStats().ActiveOverrideCount;
|
||||
|
||||
long calls = CodeReloadActivity.OverrideCallCount;
|
||||
float elapsed = Time.unscaledTime - m_LastPollTime;
|
||||
if (m_LastPollTime > 0 && elapsed > 0)
|
||||
m_CallsPerSecond = (int)((calls - m_LastCallCount) / elapsed);
|
||||
m_LastCallCount = calls;
|
||||
m_LastPollTime = Time.unscaledTime;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
var phase = CodeReloadActivity.Snapshot(out var message, out var ageMs);
|
||||
|
||||
string activity = null;
|
||||
Color activityColor = Color.white;
|
||||
switch (phase)
|
||||
{
|
||||
case CodeReloadActivity.Phase.Compiling when ageMs < CompilingStaleMs:
|
||||
activity = $"compiling {Truncate(message)}…";
|
||||
activityColor = new Color(1f, 0.8f, 0.25f); // amber
|
||||
break;
|
||||
case CodeReloadActivity.Phase.Applied when ageMs < AppliedFlashMs:
|
||||
activity = Truncate(message);
|
||||
activityColor = new Color(0.45f, 1f, 0.45f); // green
|
||||
break;
|
||||
case CodeReloadActivity.Phase.Failed:
|
||||
activity = Truncate(message); // sticky until the next reload attempt
|
||||
activityColor = new Color(1f, 0.45f, 0.45f); // red
|
||||
break;
|
||||
}
|
||||
|
||||
if (activity == null && m_OverrideCount == 0)
|
||||
return; // nothing to show
|
||||
|
||||
m_Style ??= new GUIStyle(GUI.skin.label) { fontSize = 12, wordWrap = false };
|
||||
|
||||
var lines = activity != null
|
||||
? new[] { ($"CodeReload: {activity}", activityColor), (CountLine(), Color.white) }
|
||||
: new[] { (CountLine(), Color.white) };
|
||||
|
||||
float width = 0, height = 4;
|
||||
foreach (var l in lines)
|
||||
{
|
||||
var s = m_Style.CalcSize(new GUIContent(l.Item1));
|
||||
if (s.x > width) width = s.x;
|
||||
height += s.y;
|
||||
}
|
||||
|
||||
var box = new Rect(8, 8, width + 12, height + 4);
|
||||
GUI.color = new Color(0f, 0f, 0f, 0.6f);
|
||||
GUI.DrawTexture(box, Texture2D.whiteTexture);
|
||||
|
||||
float y = box.y + 4;
|
||||
foreach (var l in lines)
|
||||
{
|
||||
var size = m_Style.CalcSize(new GUIContent(l.Item1));
|
||||
var r = new Rect(box.x + 6, y, size.x, size.y);
|
||||
// 1px shadow keeps it readable over bright scenes.
|
||||
GUI.color = new Color(0f, 0f, 0f, 0.9f);
|
||||
GUI.Label(new Rect(r.x + 1, r.y + 1, r.width, r.height), l.Item1, m_Style);
|
||||
GUI.color = l.Item2;
|
||||
GUI.Label(r, l.Item1, m_Style);
|
||||
y += size.y;
|
||||
}
|
||||
GUI.color = Color.white;
|
||||
}
|
||||
|
||||
// "N overrides · 0 calls/s" is a diagnostic in itself: bound but never dispatched.
|
||||
private string CountLine() => $"{m_OverrideCount} override(s) active · {m_CallsPerSecond} calls/s";
|
||||
|
||||
private static string Truncate(string s)
|
||||
{
|
||||
if (string.IsNullOrEmpty(s)) return "";
|
||||
int nl = s.IndexOf('\n');
|
||||
if (nl >= 0) s = s.Substring(0, nl); // failures can carry multi-line diagnostics
|
||||
return s.Length <= MaxMessageLength ? s : s.Substring(0, MaxMessageLength) + "…";
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user