# CSL LAB03 Mario Kart ```unity= // PlayerController.cs using System.Collections; using System.Collections.Generic; using UnityEngine; public class PlayerController : MonoBehaviour { // Player Property Variable private Rigidbody _rb; // Forward Speed Variable [SerializeField] private float _currentSpeed = 0f; [Tooltip("The maximum forward speed the kart can reach.")] public float MaxSpeed; [Tooltip("The minimum forward speed the kart can reach.")] public float MinSpeed; [Tooltip("The acceleration.")] public float Acceleration; // Rotate speed variable private Vector3 _turnAngle; public float MaxRotationAngle; public float RotationSpeed; public Transform Hands; public Transform FrontLeftWheel; public Transform FrontRightWheel; // Start is called before the first frame update void Start() { _rb = GetComponent<Rigidbody>(); } // Update is called once per frame void FixedUpdate() { Steer(); Move(); } // Move the player forward private void Move() { // Calculate current speed if(Input.GetKey(KeyCode.W)) // Move forward when press W { _currentSpeed = Mathf.Lerp(_currentSpeed, MaxSpeed, Time.fixedDeltaTime * Acceleration * 1f); } else if(Input.GetKey(KeyCode.S)){ // Move backward when press S _currentSpeed = Mathf.Lerp(_currentSpeed, MinSpeed, Time.fixedDeltaTime * Acceleration * 2f); } else{ // Slow down when no key's pressed _currentSpeed = Mathf.Lerp(_currentSpeed, 0, Time.fixedDeltaTime * Acceleration * 6f); } RotateRigidBody(); Vector3 vel = transform.forward * _currentSpeed; vel.y = _rb.velocity.y; // Keep the gravity _rb.velocity = vel; // Apply the speed to the rigidbody } private float RegularizeAngle(float angle) { // equil to angle = (angle+540)%360 - 180; angle = (angle > 180) ? angle-360 : angle; angle = (angle < -180) ? angle+360 : angle; return angle; } private void RotateVisual(float targetAngle, float rotateSpeed) { float handAngle = RegularizeAngle(Hands.localRotation.eulerAngles.z); float wheelAngle = RegularizeAngle(FrontLeftWheel.localRotation.eulerAngles.y); Hands.Rotate(0, 0, (targetAngle-handAngle) * Time.fixedDeltaTime * rotateSpeed, Space.Self); FrontLeftWheel.Rotate(0, (-targetAngle-wheelAngle) * Time.fixedDeltaTime * rotateSpeed, 0, Space.Self); FrontRightWheel.Rotate(0, (-targetAngle-wheelAngle) * Time.fixedDeltaTime * rotateSpeed, 0, Space.Self); } private void Steer() { if(Input.GetKey(KeyCode.A)) { RotateVisual(MaxRotationAngle, RotationSpeed * 1f); } else if(Input.GetKey(KeyCode.D)) { RotateVisual(-MaxRotationAngle, RotationSpeed * 1f); } else { RotateVisual(0, RotationSpeed * 3f); } } private void RotateRigidBody() { _turnAngle = FrontLeftWheel.eulerAngles - transform.eulerAngles; _turnAngle.y = RegularizeAngle(_turnAngle.y); Quaternion deltaRotation = Quaternion.Euler(Mathf.Sign(_currentSpeed) * _turnAngle * Time.fixedDeltaTime); _rb.MoveRotation(_rb.rotation * deltaRotation); } } ``` ```unity= // CheckPoint.cs using System.Collections; using System.Collections.Generic; using UnityEngine; public class CheckPoint : MonoBehaviour { public AudioClip CheckPointSE; private void OnTriggerEnter(Collider other) { AudioSource.PlayClipAtPoint(CheckPointSE, other.gameObject.transform.position); // Debug.Log("Passed CheckPoint."); transform.parent.GetComponent<LevelManager>().LoadCheckPoint(); } // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { } } ``` ```unity= // LevelManager.cs using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class LevelManager : MonoBehaviour { [Tooltip("The start/finish line.")] public GameObject StartFinishLine; [Tooltip("The list of checkpoints.")] public GameObject[] CheckPointList; private int _gameState = -1; private int _checkPointNum; private Collider _startLineCollider; private float _startTime; private bool _isPlaying = false; public Text TimerText; // Start is called before the first frame update void Start() { _checkPointNum = CheckPointList.Length; for(int i=0; i<_checkPointNum; i++) { CheckPointList[i].SetActive(false); } _startLineCollider = StartFinishLine.GetComponent<Collider>(); } public void LoadCheckPoint() { if (_gameState == -1) { _startTime = Time.time; _isPlaying = true; _startLineCollider.enabled = false; } else if (_gameState == _checkPointNum) { Debug.Log("Finished!"); _isPlaying = false; _startLineCollider.enabled = false; return; } else { Debug.Log(_gameState); CheckPointList[_gameState].SetActive(false); } _gameState++; if(_gameState == _checkPointNum) { _startLineCollider.enabled = true; } else { CheckPointList[_gameState].SetActive(true); } } private void UpdateTimer() { float currentTime = Time.time - _startTime; currentTime = Mathf.Max(0, currentTime); float minutes = (int)(currentTime/60); float seconds = currentTime % 60; TimerText.text = string.Format("{0:0}:{1:00.00}", minutes, seconds); } // Update is called once per frame private void Update() { if (_isPlaying) { UpdateTimer(); } } } ```