RaycastCar.cs
using System;
using System.Reflection.Metadata.Ecma335;
using System.Xml.Xsl;
using Sandbox;
public sealed class RaycastCar : Component
{

	// [WARNING] This is not currently finished. The breaks are currently not working and the collisions are a bit junky!
	// [NOTICE] This is still not a tutorial, i will add comments so you can understand the code at a later time! (Statement was written at 12th September 2026)

	//This is a simple raycast car that also works in multiplayer!
	//This particular raycast car is single ray only so it is not very acurrate on small bumps!

	//This code ofcourse goes together with the wheel component!

	[Property] [Category("Wheels")] List<CarWheelComponent> Wheels {get; set;}

	[Property] [Category("RigidBody")] Rigidbody RB {get; set;}

	[Property] [Category("Vehicle")] float Acceleration {get; set;} = 500000f;
	[Property] [Category("Vehicle")] Curve AccelerationCurve {get; set;}
	[Property] [Category("Vehicle")] float MaxSpeed {get; set;} = 20f;
	[Property] [Category("Vehicle")] float TireTurnSpeed {get; set;} = 100f;
	[Property] [Category("Vehicle")] int TireMaxTurnDegrees {get; set;} = 25;
	[Property] [Category("Vehicle")] GameObject DriverSeat {get; set;}
	[Property] [Category("Vehicle")] GameObject CarBody {get; set;}

	[Property] [Category("SteeringWheel")] GameObject SteeringWheel {get; set;}
	[Property] [Category("SteeringWheel")] GameObject SteeringWheelTurnedRight {get; set;}
	[Property] [Category("SteeringWheel")] GameObject SteeringWheelTurnedLeft {get; set;}
	[Property] [Category("SteeringWheel")] GameObject SteeringWheelTurnedMiddle {get; set;}
	[Property] [Category("SteeringWheel")] float SteeringWheelTurneSpeed {get; set;} = 5f;

	[Property] [Category("Sounds")] SoundPointComponent EngineSound {get; set;}
	[Property] [Category("Sounds")] float EngineSoundLenght {get; set;} = 37;
	[Property] [Category("Sounds")] SoundEvent SitSoundEvent {get; set;}

	private int motorInput = 0;
	[Sync] [Property] public float TurnInput {get; private set;} = 0;
	private bool HandBreak = false;
	private bool IsBraking = false;
	private float MultipliedTurnInput = 0;
	private bool DriverSeatOccupied = false;
	private List<BaseChair> CarChairs = new List<BaseChair>();

	private TimeUntil _EndEngineSound;

	private void SingleWheelSuspension(CarWheelComponent WheelComp)
	{
		//Fire a raycast
		var endPosition = WheelComp.Wheel.WorldPosition + (-WheelComp.Wheel.WorldTransform.Up * WheelComp.RaycastLenght);
		var Raycast =  Scene.Trace.Ray(WheelComp.Wheel.WorldPosition, endPosition).WithCollisionRules("carraycast").Run();
		if (Raycast.Hit)
		{
			//We have hit something lets do this!
			//* Move Wheels Up-Down

			//Some normal variables to use later
			var contact = Raycast.HitPosition;
			var springUpDirection = WheelComp.Wheel.WorldTransform.Up;
			var springLenght = Vector3.DistanceBetween(WheelComp.Wheel.WorldPosition, contact) - WheelComp.WheelRadius;
			var offsetDistance = WheelComp.RestDistance - springLenght;

			//Positon Wheels
			WheelComp.WheelRenderer.LocalPosition = Vector3.Lerp(WheelComp.WheelRenderer.LocalPosition, new Vector3(WheelComp.WheelRenderer.LocalPosition.x, WheelComp.WheelRenderer.LocalPosition.y, -springLenght), WheelComp.TireLerpSpeed * Time.Delta, false);

			//Calculate spring force
			var SpringForce = WheelComp.SpringStrenght * offsetDistance;

			var WorldVelocity = RB.GetVelocityAtPoint(WheelComp.Wheel.WorldPosition);
			var RelativeVelocity = springUpDirection.Dot(WorldVelocity);
			var springDampingForce = WheelComp.SpringDamping * RelativeVelocity;

			var ForceVector = (SpringForce - springDampingForce) * springUpDirection;
			var VerticalForceVector = new Vector3(0,0, ForceVector.z);

			RB.ApplyForceAt(WheelComp.Wheel.WorldPosition, VerticalForceVector);

			//* Rotate the wheel model! And also calculate some stuff for accelerating the car!
			var ForwardDirection = WheelComp.Wheel.WorldTransform.Rotation.Forward;
			var CarVelocity = Vector3.Dot(RB.Velocity, WorldRotation.Forward);

			WheelComp.WheelRenderer.WorldRotation = WheelComp.WheelRenderer.WorldRotation.RotateAroundAxis(Vector3.Forward, (float)(CarVelocity * Time.Delta / WheelComp.WheelRadius * 180f / Math.PI));

			//* Accelerate the car forwards
			//Move car
			if (motorInput != 0 && WheelComp.isMotor == true)
			{
				//Calculate speed and acceleration ratio so we get realistic acceleration
				var SpeedRatio = Math.Abs(CarVelocity / MaxSpeed);
				var AccelerationFactor = AccelerationCurve.Evaluate(SpeedRatio);

				var ForwardForceVector = ForwardDirection * Acceleration * motorInput * AccelerationFactor;

				RB.ApplyForceAt(WheelComp.Wheel.WorldPosition, ForwardForceVector);
			}

			//* Rotate Neccecary wheels
			var SteerSideDirection = WheelComp.SteeringGameObject.WorldTransform.Forward;
			var TireVelocity = WorldVelocity;
			var SteeringXVelocity = SteerSideDirection.Dot(TireVelocity);

			var GripFactor = Math.Abs(SteeringXVelocity/TireVelocity.Length);
			var YTraction = WheelComp.GripCurve.Evaluate(GripFactor); //! in the tutorial it is xTraction and xForce, because in godot it is that direction, but here it is Y

			if (HandBreak == true) YTraction = 0.1f;

			var Gravity = 9.8f;
			var YForce = -SteerSideDirection * SteeringXVelocity * YTraction * ((RB.Mass * Gravity)/4f);

			//FORCE WTRACTION (TRIBH)
			var forwardVelocity = CarVelocity;
			var ChosenTraction = WheelComp.WheelTraction;
			if (IsBraking == true) ChosenTraction = WheelComp.BrakesBackwardsTraction;
			var BackwardsForce = WheelComp.Wheel.WorldTransform.Backward * forwardVelocity * ChosenTraction * ((RB.Mass * Gravity)/4f);

			//Apply forces
			RB.ApplyForceAt(WheelComp.Wheel.WorldPosition, YForce);
			RB.ApplyForceAt(WheelComp.Wheel.WorldPosition, BackwardsForce);
		}
	}

	protected override void OnFixedUpdate()
	{
		if (IsProxy) return;

		//Do the wheel stuff
		foreach (var Wheel in Wheels)
		{
			SingleWheelSuspension(Wheel);
		}
	}

	private void TurnSteeringWheel()
	{
		//* Turn the steering wheel
		if (TurnInput == 0)
		{
			SteeringWheel.WorldRotation = Rotation.Slerp(SteeringWheel.WorldRotation, SteeringWheelTurnedMiddle.WorldRotation, Time.Delta * SteeringWheelTurneSpeed, true);
		}
		else
		{
			if (TurnInput == 1)
			{
				SteeringWheel.WorldRotation = Rotation.Slerp(SteeringWheel.WorldRotation, SteeringWheelTurnedLeft.WorldRotation, Time.Delta * SteeringWheelTurneSpeed, true);
			}
			else
			{
				SteeringWheel.WorldRotation = Rotation.Slerp(SteeringWheel.WorldRotation, SteeringWheelTurnedRight.WorldRotation, Time.Delta * SteeringWheelTurneSpeed, true);
			}
		}
	}

	private void TurnTheWheels()
	{
		//* Turn the car
		MultipliedTurnInput = TurnInput * TireTurnSpeed;

		foreach (var WheelComp in Wheels)
		{
			if (WheelComp.isFrontWheel == true)
			{
				if (MultipliedTurnInput != 0)
				{
					var newYaw = Math.Clamp(WheelComp.Wheel.LocalRotation.Angles().yaw + MultipliedTurnInput * Time.Delta, - TireMaxTurnDegrees, TireMaxTurnDegrees);

					WheelComp.Wheel.LocalRotation = WheelComp.Wheel.LocalTransform.Rotation.Angles().WithYaw( newYaw).ToRotation();
				}
				else
				{
					var newYaw = MathX.Approach(WheelComp.Wheel.LocalTransform.Rotation.Angles().yaw, 0, TireTurnSpeed * Time.Delta);
					WheelComp.Wheel.LocalRotation = WheelComp.Wheel.LocalTransform.Rotation.Angles().WithYaw( newYaw).ToRotation();
				}
			}
		}
	}

	protected override void OnStart()
	{
		_EndEngineSound = 1f;
	}

	protected override void OnUpdate()
	{
		//* Play Audio if someone sits on the chair
		var CarBodyChildren = CarBody.Children;
		foreach (var ChairGameObject in CarBodyChildren)
		{
			var Chair = ChairGameObject.GetComponent<BaseChair>();
			if (Chair == null) continue;

			if (Chair.IsOccupied == true && !CarChairs.Contains(Chair))
			{
				CarChairs.Add(Chair);
				Sound.Play(SitSoundEvent, ChairGameObject.WorldPosition);
			}
			else if (Chair.IsOccupied == false && CarChairs.Contains(Chair))
			{
				Sound.Play(SitSoundEvent, ChairGameObject.WorldPosition);
				CarChairs.Remove(Chair);
			}
		}
		

		//These happen for the owner and the other clients
		DriverSeatOccupied = DriverSeat.GetComponent<BaseChair>().IsOccupied;

		//* Turn the steering wheel
		TurnSteeringWheel();

		//* Turn the car
		TurnTheWheels();

		//* Car Sounds
		if (DriverSeatOccupied == false)
		{
			//Stop engine sounds
			EngineSound.StopSound();
			_EndEngineSound = .1f;

		}
		else
		{
			//Adjust the sound volume based on speed
			var CarVelocity = Vector3.Dot(RB.Velocity, WorldRotation.Forward);
			var SoundVolume = CarVelocity / 570 * 2.5f; //! Max Velocity

			EngineSound.Volume = SoundVolume;

			//See if we should play the sound or not
			if (_EndEngineSound)
			{
				_EndEngineSound = EngineSoundLenght;
				EngineSound.StopSound();
				EngineSound.StartSound();
			}
		}


		if (IsProxy) return;

		//* Check if a player has sut on the driver seat
		var CurrentSittingPlayer = DriverSeat.Components.GetInDescendants<PlayerController>();
		if (CurrentSittingPlayer != null && CurrentSittingPlayer.GameObject.IsProxy == true)
		{
			//We have found that a player who does not own the car has sut on the car, so we need to change the ownership!
			GameObject.Network.AssignOwnership(CurrentSittingPlayer.GameObject.Network.Owner);
			CarBody.Network.AssignOwnership(CurrentSittingPlayer.GameObject.Network.Owner);
		}

		//* See if someone has the seat occupied!
		if (DriverSeatOccupied == false )
		{
			//* All settings are default
			TurnInput = 0;
			HandBreak = false;
			IsBraking = false;
			motorInput = 0;
			MultipliedTurnInput = 0;
		}
		else
		{
			//* Forwards backwards
			//Accelerate
			if (Input.Pressed("CarAccelerate")) motorInput = 1;
			if (Input.Released("CarAccelerate")) motorInput = 0;

			//Descelerate
			if (Input.Pressed("CarDescelerate")) motorInput = -1;
			if (Input.Released("CarDescelerate")) motorInput = 0;

			//* Turning Right & Left
			TurnInput = 0;

			if (Input.Down("CarTurnLeft")) TurnInput += 1;
			if (Input.Down("CarTurnRight")) TurnInput -= 1;

			//* Handbreak
			if (Input.Down("CarHandBrake")) HandBreak = true; else HandBreak = false;
			//* Car brakes
			if (Input.Down("CarBrake")) IsBraking = true; else IsBraking = false;

		}
	}

}