Initial commit: AIGame Unity client with URP, HybridCLR, YooAsset and tooling.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
udx
2026-09-21 14:21:07 +08:00
co-authored by Cursor
commit f3f66ab063
931 changed files with 112366 additions and 0 deletions
@@ -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
}
}