Code/WheelSkidMark.cs
using Meteor.VehicleTool.Util;
using Meteor.VehicleTool.Vehicle.Wheel;
using Sandbox;
using System;
using System.Collections.Generic;

/// <summary>
/// Creates skid marks when a vehicle's wheels lose traction
/// </summary>
public class WheelSkidMarkComponent : Component
{

	[ConVar( "skidmark_max_skid" )]
	public static float MaxSkid { get; set; } = 50.0f;

	[ConVar( "skidmark_min_slide" )]
	public static float MinSlide { get; set; } = 0.1f;

	private WheelCollider wheel;

	private readonly List<LineRenderer> SkidMarks = [];

	private LineRenderer _skidMark;
	protected override void OnStart()
	{
		ResetSkid();
		wheel ??= Components.Get<WheelCollider>( FindMode.InSelf );
	}

	private void ResetSkid()
	{
		_skidMark = null;
	}
	private void CreateSkid()
	{
		var go = Scene.CreateObject();

		go.WorldPosition = wheel.ContactPoint;
		go.WorldRotation = Rotation.LookAt( wheel.ContactRight );
		_skidMark = go.AddComponent<LineRenderer>();
		_skidMark.Face = SceneLineObject.FaceMode.Normal;
		_skidMark.Points = [go];
		_skidMark.Color = Color.Black.WithAlpha( 0.5f );
		_skidMark.Opaque = false;
		_skidMark.CastShadows = false;
		go.Flags = go.Flags.WithFlag( GameObjectFlags.Hidden, true );
		SkidMarks.Add( _skidMark );
	}

	protected override void OnUpdate()
	{
		while ( SkidMarks.Count > MaxSkid )
		{
			SkidMarks[0].DestroyGameObject();
			SkidMarks.RemoveAt( 0 );
		}

		if ( !wheel.IsGrounded )
		{
			ResetSkid();
			return;
		}

		var slideAmount = wheel.NormalizedSlip;
		if ( slideAmount < MinSlide )
		{
			ResetSkid();
			return;
		}

		if ( !_skidMark.IsValid() )
		{
			CreateSkid();
		}

		var go = Scene.CreateObject();
		go.WorldPosition = wheel.ContactPoint;
		go.WorldRotation = Rotation.LookAt( wheel.ContactRight );
		go.Flags = go.Flags.WithFlag( GameObjectFlags.Hidden, true );
		_skidMark.Points.Add( go );
	}

}