Ashby Issac c466e28d6c AI FSM changes.
- Protoyped on the AI movement, found solutions for turns and
   moving after impacting the player.
- Created a full base for AI state machine.
- Worked on binding the core AI logic with the finite state machine.
2026-01-07 19:38:03 +05:30

182 lines
5.2 KiB
C#

using System;
using UnityEngine;
using System.Collections;
using System.ComponentModel;
using UnityEngine.SceneManagement;
public class PlayerCarController : MonoBehaviour, ICar
{
[SerializeField] private bool isDebugTest = false;
[SerializeField] private Transform[] tireTransforms;
[SerializeField] private Transform[] frontTireTransforms;
[SerializeField] private Transform[] tireMeshes;
[SerializeField] private Rigidbody carRigidbody;
[SerializeField] private PlayerCarSpecs carSpecs;
[SerializeField] private Vector3 customCenterOfMass;
[SerializeField] private AnimationCurve steerLeftAnimCurve;
[SerializeField] private AnimationCurve steerRightAnimCurve;
[SerializeField] private AnimationCurve accelAnimCurve;
[Category("Input Attributes")]
private float steeringInput;
private float accelInput;
[Category("Global-Script-Attributes")]
private int tiresCount = 0;
private float remTime = 0f;
private float tiresInGround = 0f;
[Category("Script-Object-Refs.")]
private PlayerCarSystem carSystem;
private TimerSystem timerSystem;
[Category("Tags.")]
private const string checkpointTag = "Checkpoint";
private const string finishCheckpointTag = "Finish";
private RaycastHit hitInfo;
private bool isGameOver = false;
private Coroutine GroundCheckCoroutine = null;
[Category("Action Delegates")]
public static Action<float> OnCheckpointReached;
public Action<float, Transform, float, Transform> OnApplyForce;
public Action<float> OnCarRotate;
public Action<string> OnGameOver;
public GameplayManager GameplayManager
{
get;
private set;
}
public void Init(GameplayManager gameplayManager)
{
this.GameplayManager = gameplayManager;
isGameOver = false;
tiresCount = tireTransforms.Length;
timerSystem = new TimerSystem();
// remTime = timerSystem.Timer;
carSystem = new PlayerCarSystem(this, carRigidbody, carSpecs, frontTireTransforms, steerLeftAnimCurve: steerLeftAnimCurve, steerRightAnimCurve: steerRightAnimCurve, accelAnimCurve: accelAnimCurve);
}
private void EnableGameOverState()
{
return;
OnGameOver?.Invoke("");
isGameOver = true;
}
/* Editor movement inputs */
private void ProcessInputs()
{
accelInput = Input.GetAxis("Vertical");
steeringInput = Input.GetAxis("Horizontal");
}
private void CheckGameOverStates()
{
if (isGameOver)
return;
/* Check for Time run out */
if (remTime < 1)
{
EnableGameOverState();
return;
}
/* Check for Tires out of bounds (ground/platform) */
if (tiresInGround < 3 && GroundCheckCoroutine == null)
GroundCheckCoroutine = StartCoroutine(CheckTiresInGround());
}
/* Check if car/rigidbody is out of bounds/ground/platform */
private IEnumerator CheckTiresInGround()
{
yield return new WaitForSeconds(3f);
if (tiresInGround >= 3)
{
StopCoroutine();
}
/* Check for dealing with edge cases where
car would be moving through the edge of the platforms */
yield return new WaitForSeconds(2f);
if (tiresInGround < 3 && !isGameOver)
{
EnableGameOverState();
StopCoroutine();
}
}
private void StopCoroutine()
{
if (GroundCheckCoroutine != null)
{
StopCoroutine(GroundCheckCoroutine);
GroundCheckCoroutine = null;
}
}
/* Functions mapped to event triggers of associated buttons */
public void MoveInput(float input) => accelInput = input;
public void SteerInput(float input) => steeringInput = input;
public void RestartLevel()
{
StopCoroutine();
SceneManager.LoadSceneAsync(SceneManager.GetActiveScene().buildIndex);
}
#region Monobehaviour Callbacks
private void Awake()
{
Debug.Log($" Default Center of mass: {carRigidbody.centerOfMass} ");
carRigidbody.centerOfMass = customCenterOfMass;
}
/* Apply force to Car's rigidbody for Suspension(Y-Axis),
steering(X-Axis), and acceleration(Z-Axis) for each tire. */
private void FixedUpdate()
{
tiresInGround = 0;
for (int tireIndex = 0; tireIndex < tiresCount; tireIndex++)
{
if (Physics.Raycast(tireTransforms[tireIndex].position, -tireTransforms[tireIndex].up, out hitInfo, carSpecs.hitDist))
{
tiresInGround++;
Debug.Log($"## Input :: accelInput: {accelInput}");
OnApplyForce?.Invoke(hitInfo.distance, tireTransforms[tireIndex], accelInput, tireMeshes[tireIndex]);
}
}
CheckGameOverStates();
}
// Updates the timer for UI and Rotation of the wheels (transforms) according to input
private void Update()
{
if (Input.GetKeyDown(KeyCode.R))
RestartLevel();
OnCarRotate?.Invoke(steeringInput);
#if UNITY_EDITOR
if (isDebugTest)
ProcessInputs();
#endif
}
#endregion
}