UI/NameTags/NameTags.razor
@using Sandbox.UI
@inherits PanelComponent
<root>
@foreach ( var tag in _tags )
{
<div class="nametag" style="left: @(tag.X * 100)%; top: @(tag.Y * 100)%; opacity: @tag.Alpha.ToString("F2");">
<img class="target" src="ui/crosshair/target.png" />
<label class="name">@tag.Name</label>
</div>
}
</root>
@code
{
private record struct NameTagData( string Name, float X, float Y, float Alpha );
private readonly List<NameTagData> _tags = new();
protected override void OnUpdate()
{
_tags.Clear();
var cam = Game.ActiveScene?.Camera;
if ( cam == null ) return;
var localPawn = LocalPlayer.Pawn;
foreach ( var pawn in Game.ActiveScene?.GetAllComponents<PlayerPawn>() ?? Enumerable.Empty<PlayerPawn>() )
{
if ( pawn == localPawn ) continue;
if ( !pawn.IsAlive ) continue;
var worldPos = pawn.WorldPosition + Vector3.Up * 50f;
var toTarget = worldPos - cam.WorldPosition;
if ( Vector3.Dot( toTarget.Normal, cam.WorldRotation.Forward ) < 0.1f ) continue;
var dist = toTarget.Length;
if ( dist > 5000f ) continue;
var screenPos = cam.PointToScreenNormal( worldPos );
var alpha = MathX.Remap( dist, 500f, 5000f, 1f, 0.2f );
var name = pawn.IsBot ? (pawn.BotName.Length > 0 ? pawn.BotName : "Bot") : (pawn.Network.Owner?.DisplayName ?? pawn.GameObject.Name);
_tags.Add( new NameTagData( name, screenPos.x, screenPos.y, alpha ) );
}
}
protected override int BuildHash() => HashCode.Combine( _tags.Count, Time.Now );
}