Deduplication pass (44 extractions): - New SmoothedLook: shared smoothed yaw/pitch look tick + view seeding (FirstPersonController, FreeCamController) - New CharacterTeleport: disable-CC-around-teleport idiom (rescue, owner position apply, free-cam sync) - New GeometryUtil: RescaleToUnitScale + TransformBounds (concealment meter/volume, skinned collider baker) - Paint family: canonical map read/write shared between Net and Persistence, snapshot decode, backend stroke dispatch, RebuildBackend, ResolveSeed, chunked RPC send/gate/copy, CamoKnobs/HeightKnobs aggregates, SanitizeFileKey, NewMaskTexture, AdoptAlbedo, FindSlider - Player/modes: GameMode SetPlayerControl/SetPainting/SetOrbitInputEnabled, InputUtil.GameplayKeyPressed, NearestClimbHit, ComputeXRay delegates to ComputeResolutionFreeze, FingerIK TryInPlaneAngle - Match/misc: UnhookSessionCallbacks, SendEmptyNamedMessage, SwapScenes, RunSessionOpAsync, CovarianceEigen, CollectClusterPoints, MatchHud GUIStyle cache, HsvColorPicker rehook, VoxelCenter Documentation pass: remove 6 triplicated doc blocks and 2 restating inline comments left after the previous reduction. Behavior-neutral; hot-path rasterizer loops intentionally untouched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
101 lines
3.6 KiB
C#
101 lines
3.6 KiB
C#
using UnityEngine;
|
|
|
|
/// <summary>
|
|
/// Placeholder on-screen match HUD: the current sub-phase + countdown (top centre) and a hunter crosshair
|
|
/// during the seek phase. Drawn with IMGUI (OnGUI) on purpose - it needs no Canvas / font asset authoring,
|
|
/// so the match-state logic can be exercised immediately; swap it for a proper uGUI canvas once the look is
|
|
/// finalised. Added at runtime by <see cref="MatchManager"/> (AddComponent), so it rides the persistent
|
|
/// manager object and reads <see cref="MatchManager.Instance"/> on every machine.
|
|
/// </summary>
|
|
[DisallowMultipleComponent]
|
|
public class MatchHud : MonoBehaviour
|
|
{
|
|
private GUIStyle _phaseStyle;
|
|
private GUIStyle _timeStyle;
|
|
|
|
private void OnGUI()
|
|
{
|
|
var match = MatchManager.Instance;
|
|
if (!match || !match.MatchActive) return;
|
|
|
|
EnsureStyles();
|
|
|
|
const float w = 360f;
|
|
var phaseRect = new Rect((Screen.width - w) * 0.5f, 12f, w, 30f);
|
|
var timeRect = new Rect(phaseRect.x, phaseRect.y + 28f, w, 36f);
|
|
DrawShadowed(phaseRect, PhaseLabel(match.LocalSubPhase), _phaseStyle);
|
|
DrawShadowed(timeRect, FormatTime(match.LocalPhaseRemaining), _timeStyle);
|
|
|
|
if (match.LocalSubPhase == MatchManager.MatchSubPhase.Seek && LocalIsHunter())
|
|
DrawCrosshair();
|
|
}
|
|
|
|
private static string PhaseLabel(MatchManager.MatchSubPhase phase)
|
|
{
|
|
switch (phase)
|
|
{
|
|
case MatchManager.MatchSubPhase.Hide: return "Verstecken";
|
|
case MatchManager.MatchSubPhase.Seek: return "Suchen";
|
|
case MatchManager.MatchSubPhase.Resolution: return "Auflösung";
|
|
default: return "";
|
|
}
|
|
}
|
|
|
|
private static string FormatTime(float seconds)
|
|
{
|
|
int s = Mathf.CeilToInt(Mathf.Max(0f, seconds));
|
|
return $"{s / 60:0}:{s % 60:00}";
|
|
}
|
|
|
|
private static bool LocalIsHunter()
|
|
{
|
|
var nm = Unity.Netcode.NetworkManager.Singleton;
|
|
if (!nm || nm.LocalClient == null) return false;
|
|
var po = nm.LocalClient.PlayerObject;
|
|
if (!po) return false;
|
|
var setup = po.GetComponent<NetworkPlayerSetup>();
|
|
return setup && setup.ScopedModes && setup.Role == PlayerRole.Hunter;
|
|
}
|
|
|
|
private static void DrawCrosshair()
|
|
{
|
|
float cx = Screen.width * 0.5f;
|
|
float cy = Screen.height * 0.5f;
|
|
const float r = 8f, t = 2f;
|
|
Color prev = GUI.color;
|
|
GUI.color = new Color(1f, 1f, 1f, 0.85f);
|
|
GUI.DrawTexture(new Rect(cx - r, cy - t * 0.5f, r * 2f, t), Texture2D.whiteTexture);
|
|
GUI.DrawTexture(new Rect(cx - t * 0.5f, cy - r, t, r * 2f), Texture2D.whiteTexture);
|
|
GUI.color = prev;
|
|
}
|
|
|
|
// One label drawn twice (dark offset behind, then the real colour) so it stays readable on any backdrop.
|
|
private static void DrawShadowed(Rect r, string text, GUIStyle style)
|
|
{
|
|
if (string.IsNullOrEmpty(text)) return;
|
|
Color real = style.normal.textColor;
|
|
style.normal.textColor = new Color(0f, 0f, 0f, 0.7f);
|
|
GUI.Label(new Rect(r.x + 1f, r.y + 1f, r.width, r.height), text, style);
|
|
style.normal.textColor = real;
|
|
GUI.Label(r, text, style);
|
|
}
|
|
|
|
private void EnsureStyles()
|
|
{
|
|
if (_phaseStyle == null) _phaseStyle = MakeCenteredBoldLabel(22);
|
|
if (_timeStyle == null) _timeStyle = MakeCenteredBoldLabel(28);
|
|
}
|
|
|
|
private static GUIStyle MakeCenteredBoldLabel(int fontSize)
|
|
{
|
|
var style = new GUIStyle(GUI.skin.label)
|
|
{
|
|
alignment = TextAnchor.MiddleCenter,
|
|
fontSize = fontSize,
|
|
fontStyle = FontStyle.Bold,
|
|
};
|
|
style.normal.textColor = Color.white;
|
|
return style;
|
|
}
|
|
}
|