UnitChainedVfx.cs

A component that renders a chained visual effect between two Units using a LineRenderer. It maintains three spline points (start, midpoint, end), updates their positions each frame, clamps minimum height, and controls lifetime-based alpha via a gradient. It also accepts RPCs to set an anchor position or anchor unit.

Networking
using System;
using Sandbox;

public class UnitChainedVfx : Component
{
	[Property] public LineRenderer LineRenderer { get; set; }

	public Unit ChainedUnit { get; set; }
	public Unit AnchorUnit { get; set; }

	private List<Vector3> _linePoints = new();
	private const int NUM_POINTS = 3;

	private Vector3 _anchorPos;

	private TimeSince _timeSinceSpawn;

	public float Lifetime { get; set; }

	public float ChainLength { get; set; }

	private const float MIN_HEIGHT = 5f;

	protected override void OnAwake()
	{
		base.OnAwake();

		_timeSinceSpawn = 0f;

		for(int i = 0; i < NUM_POINTS; i++ )
		{
			_linePoints.Add( Vector3.Zero );
		}
	}

	protected override void OnUpdate()
	{
		base.OnUpdate();

		if ( ChainedUnit.IsValid() )
			_linePoints[0] = ChainedUnit.WorldPosition + new Vector3( 0f, 0f, ChainedUnit.Height * 0.5f );

		if ( AnchorUnit.IsValid() )
			_linePoints[NUM_POINTS - 1] = AnchorUnit.WorldPosition + new Vector3( 0f, 0f, AnchorUnit.Height * 0.75f );
		else
			_linePoints[NUM_POINTS - 1] = _anchorPos;

		var a = _linePoints[0];
		var b = _linePoints[NUM_POINTS - 1];
		_linePoints[1] = a + (b - a) * 0.5f;

		// prevent initial line flicker
		if ( _timeSinceSpawn > 0.05f )
		{
			for ( int i = 0; i < NUM_POINTS; i++ )
			{
				LineRenderer.VectorPoints[i] = _linePoints[i].WithZ( Math.Max( _linePoints[i].z, MIN_HEIGHT ) );
			}

			var alpha = _timeSinceSpawn < 0.25f ? (_timeSinceSpawn / 0.25f) : (_timeSinceSpawn > Lifetime - 1f ? Utils.Map( _timeSinceSpawn, Lifetime - 1f, Lifetime, 1f, 0f ) : 1f);
			var gradient = new Gradient();
			gradient.AddAlpha( x: 0f, alpha: 0f );
			gradient.AddAlpha( x: 0.025f, alpha: 1f );
			gradient.AddAlpha( x: 0.975f, alpha: 1f );
			gradient.AddAlpha( x: 1f, alpha: 0f );

			var color = new Color( 0.8f, 0.8f, 1f );
			gradient.AddColor( 0.5f, color.WithAlpha( alpha ) );
			LineRenderer.Color = gradient;
		}
	}

	[Rpc.Broadcast]
	public void SetAnchorPos( Vector2 pos )
	{
		_anchorPos = new Vector3( pos.x, pos.y, 0f );
	}

	[Rpc.Broadcast]
	public void SetAnchorUnit( Unit unit )
	{
		AnchorUnit = unit;
	}
}