FpsUI/Widgets/HitmarkerWidget.cs

A UI widget that draws a hitmarker X and presents it with a pop (scale+fade) animation. HitmarkerModel stores state and is driven each frame; Hit(kill:true) triggers a pop tinted by the theme accent for kills.

File Access
using Goo;
using Sandbox;
using Sandbox.UI;
using PanelTransform = Goo.PanelTransform;

namespace Goo.FpsUI;

// Stateless presenter: an X that pops (scale + fade) on a hit, accent-tinted on a kill.
static class HitmarkerView
{
    const float Box = 28f, Len = 14f, Thick = 2f;

    public static Container Build( HitmarkerModel m, FpsTheme t, Color? killColor = null )
    {
        float s = m.Scale;
        Color color = m.Kill ? (killColor ?? t.Accent) : Color.White;
        float scale = 0.6f + 0.5f * s;

        var root = new Container
        {
            Key = "hit", Position = PositionMode.Relative, Width = Box, Height = Box,
            Opacity = s, Transform = PanelTransform.Scale( scale ),
            // Pin the scale pivot to center so the pop grows/shrinks in place (default pivot drifts it up-left).
            TransformOriginX = Length.Percent( 50 ), TransformOriginY = Length.Percent( 50 ),
            Children =
            {
                Diagonal( "d1", 45f, color ),
                Diagonal( "d2", -45f, color ),
            },
        };
        return root;

        static Container Diagonal( string key, float deg, Color color ) => new()
        {
            Key = key, Position = PositionMode.Absolute,
            Top = Box / 2f - Thick / 2f, Left = Box / 2f - Len / 2f,
            Width = Len, Height = Thick, BackgroundColor = color,
            // Rotate about the arm's own center so both arms cross at the box center (not their shared corner).
            TransformOriginX = Length.Percent( 50 ), TransformOriginY = Length.Percent( 50 ),
            Transform = PanelTransform.Rotate( deg ),
        };
    }
}

// Standalone hitmarker. Call Hit() / Hit(kill:true) when your shot connects.
public sealed partial class HitmarkerWidget : GooPanel<Container>
{
    readonly HitmarkerModel _m = new();
    readonly FpsTheme _t = new();

    public void Hit( bool kill = false ) => _m.Pop( kill ); // register a hit (kill = accent tint)

    // Demo-only seam: implemented in FpsDemo.cs, compiles out when that file is deleted.
    partial void StepDemo( float dt, ref bool active );

    protected override bool Tick( float dt )
    {
        bool demo = false;
        StepDemo( dt, ref demo );
        bool moving = _m.Tick( dt );
        return demo || moving;
    }

    protected override Container Build()
    {
        var root = Parts.Root( "fpsHitmarker" );
        root.Children.Add( Parts.Anchor( "a", Parts.Corner.Center, 0f, HitmarkerView.Build( _m, _t ) ) );
        return root;
    }
}