using System; using UnityEngine; using System.Collections; using System.ComponentModel; using UnityEngine.SceneManagement; using UnityEngine.EventSystems; using Newtonsoft.Json; public class CarController : MonoBehaviour { [SerializeField] private bool isDebugTest = false; [SerializeField] private Transform[] tireTransforms; [SerializeField] private Transform[] frontTireTransforms; [SerializeField] private Transform[] tireMeshes; [SerializeField] private Rigidbody carRigidbody; [SerializeField] private CarSpecs 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 CarSystem 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 OnCheckpointReached; public Action OnApplyForce; public Action OnCarRotate; public Action OnGameOver; public void Init() { isGameOver = false; tiresCount = tireTransforms.Length; timerSystem = new TimerSystem(); remTime = timerSystem.Timer; carSystem = new CarSystem(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 }