using System.Collections; using System.Collections.Generic; using Cinemachine; using TMPro; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.SceneManagement; using static UnityEngine.ParticleSystem; public class GameManager : MonoBehaviour { public PlayerInputScheme playerInputScheme; private InputAction forwardAction; private InputAction switchAction; public bool isAuto = false; public static GameManager instance; public static float movespeed = 0f; public float acceleration = 5; public float maxspeed = 30f; public CinemachineVirtualCamera cam; public ParticleSystem speedLinesEffect; public float minFov = 50; public float maxFov = 90; public float Distance; public float max_shake_ferquency = 2f; public float max_shake_amplitude = 0.08f; public TMP_Text txtSpeed; public TMP_Text txtDistance; public float FovRange => maxFov - minFov; [Header("Obstacle avoidance")] public int currentLaneId = 0; public float laneSwitchSpeed = 10f; public float laneSwitchRotationMultiplier = 0.2f; public float[] laneXs = {-1.5f, 0f, 1.5f}; public Transform car; public CollisionDetector collisionDetector; EmissionModule speedLinesEmission; public CinemachineImpulseSource impulseSource; public GameObject trafficVehiclePrefab; public List trafficVehicles = new List(); public int maxTrafficVehicles = 10; public float trafficVehicleSpawnInterval = 5f; float trafficVehicleSpawnTimer =0f; public GameObject gameOverScreen; [Header("Point system")] public float pointsMultiplier = 1; public float maxMult; public float speedMultIncreaseRate = 0.5f; public float speedMultDecreaseRate = 2f; public float points; public float pointsPerSecond = 10; public TMP_Text txt_points; public TMP_Text txt_pointsMultiplier; [Header("Highscore")] public TMP_InputField highscoreNameInput; public GameObject highscorePanel; public Transform leaderboardItemsParent; public GameObject leaderboardItemPrefab; [Header("Pause Menu")] public GameObject pauseMenu; public InputAction pauseAction; float startRotationY; public void Awake(){ instance = this; playerInputScheme = new PlayerInputScheme(); forwardAction = playerInputScheme.Car.forward; switchAction = playerInputScheme.Car.horizontal; switchAction.performed += OnSwitchPerformed; pauseAction = playerInputScheme.Car.pause; pauseAction.performed += OnPausePerformed; Debug.Log("Verifying Inputs:\n" +forwardAction.name + " " + switchAction.name); } void OnEnable(){ forwardAction.Enable(); switchAction.Enable(); pauseAction.Enable(); } void OnDisable(){ forwardAction.Disable(); switchAction.Disable(); pauseAction.Disable(); } public void Resume(){ Time.timeScale = 1; pauseMenu.SetActive(false); } public void OnPausePerformed(InputAction.CallbackContext context){ pauseMenu.SetActive(!pauseMenu.activeSelf); if(pauseMenu.activeSelf){ Time.timeScale = 0.001f; } else{ Time.timeScale = 1; } } void Start() { speedLinesEmission = speedLinesEffect.emission; startRotationY = car.eulerAngles.y; collisionDetector.OnEnter += OnTriggerEnter; } public static void Reset(){ instance = null; SectionTriger.counter=0; } // Update is called once per frame void FixedUpdate() { if(lives <=0){return;} MoveForward(); } void Update(){ if(isAuto){return;} if(lives <=0){return;} HandlePoints(); SwitchLanes(); TrafficVehicleSpawn(); } void HandlePoints(){ points += pointsPerSecond * Time.deltaTime * pointsMultiplier; float moveMult = movespeed / maxspeed * 10; float targetMult = moveMult; if(targetMult > pointsMultiplier){ pointsMultiplier += Time.deltaTime * speedMultIncreaseRate; } else if(targetMult < pointsMultiplier){ pointsMultiplier -= Time.deltaTime * speedMultDecreaseRate; } pointsMultiplier = Mathf.Clamp(pointsMultiplier, 0, maxMult); txt_points.text = points.ToString("000000"); // Always display at least 6 digits, pad with zeros txt_pointsMultiplier.text = pointsMultiplier.ToString("n1") + " x"; txt_pointsMultiplier.color = new Color(1, 1, 1, Mathf.Lerp(0.5f, 1f, pointsMultiplier / maxMult)); } void TrafficVehicleSpawn(){ trafficVehicleSpawnTimer += Time.deltaTime * movespeed / maxspeed; if(trafficVehicleSpawnTimer >= trafficVehicleSpawnInterval){ trafficVehicleSpawnTimer = 0; SpawnNextTrafficVehicle(); } } void SpawnNextTrafficVehicle(){ trafficVehicles.RemoveAll(item => item == null); if(trafficVehicles.Count < maxTrafficVehicles){ GameObject trafficVehicle = Instantiate(trafficVehiclePrefab, new Vector3(laneXs[currentLaneId], 0, -500), Quaternion.Euler(0,-90,0)); trafficVehicles.Add(trafficVehicle); } } float laneSwitchTime =0; void OnSwitchPerformed(InputAction.CallbackContext context){ Debug.Log("OnSwitchPerformed: " + context.ReadValue()); if(context.ReadValue() < 0){ currentLaneId++; } else{ currentLaneId--; } laneSwitchTime = 0; } void SwitchLanes(){ if(displaySpeed < 10){return;} float switchSpeedMultiplier = 1 + (displaySpeed/laneSwitchSpeed); /// OBSOLETE: Old input method // if(Input.GetKeyDown(KeyCode.LeftArrow)){ // currentLaneId--; // laneSwitchTime = 0; // } // else if(Input.GetKeyDown(KeyCode.RightArrow)){ // currentLaneId++; // laneSwitchTime = 0; // } if(laneSwitchTime < 1){ laneSwitchTime += Time.deltaTime * 2; } currentLaneId = Mathf.Clamp(currentLaneId, 0, laneXs.Length - 1); float lerper = switchSpeedMultiplier*Time.deltaTime * laneSwitchTime; car.position = new Vector3(Mathf.Lerp(car.position.x,laneXs[currentLaneId],lerper), car.position.y, car.position.z); float xDist = (car.position.x - laneXs[currentLaneId]); Vector3 targetRotation = new Vector3(car.eulerAngles.x, startRotationY + Mathf.Atan2(xDist, 1) * Mathf.Rad2Deg * laneSwitchRotationMultiplier * (1-(movespeed/maxspeed)), car.eulerAngles.z); car.eulerAngles = Vector3.Lerp(car.eulerAngles, targetRotation, lerper); } float displaySpeed = 0; void MoveForward(){ float input = forwardAction.ReadValue(); if(isAuto){input = 0; movespeed = maxspeed /3f;} movespeed += input * acceleration * Time.deltaTime; movespeed = Mathf.Clamp(movespeed, 0, maxspeed); float x = (FovRange / maxspeed) * movespeed; cam.m_Lens.FieldOfView = minFov + x; cam.GetCinemachineComponent().m_FrequencyGain = max_shake_ferquency * movespeed / maxspeed; cam.GetCinemachineComponent().m_AmplitudeGain = max_shake_amplitude * movespeed / maxspeed; displaySpeed = movespeed * 2.5f; txtSpeed.text = displaySpeed.ToString("n0") + " km/h"; Distance += displaySpeed * Time.deltaTime / 3600; txtDistance.text = Distance.ToString("n2") + " km"; float fontSize = Mathf.Lerp(10f, 15f, movespeed / maxspeed); txtSpeed.fontSize = isAuto ? 0 : fontSize; txtDistance.fontSize = isAuto ? 0 : fontSize; speedLinesEmission.rateOverTime = displaySpeed > 100 ? displaySpeed : 0; } public int lives = 3; void OnTriggerEnter(Collider other) { Debug.Log("OnTriggerEnter: " + other.gameObject.name + " : " + other.gameObject.tag); string objectNameLowered = other.gameObject.name.ToLower(); if(objectNameLowered.Contains("section")){ return; } if(objectNameLowered.Contains("player")){ return; } movespeed = maxspeed / 10f; lives--; LivesVisualizer.instance.SetLives(lives); impulseSource.GenerateImpulse(); //Snap to lane car.position = new Vector3(laneXs[currentLaneId], car.position.y, car.position.z); if(other.GetComponent()!=null){ Destroy(other.gameObject); } if(lives <=0){ gameOverScreen.SetActive(true); PopulateLeaderboard(); movespeed=0; if(GetHighestScore() < points){ highscorePanel.SetActive(true); } } } public void Retry(){ Reset(); SceneManager.LoadScene("GameScene"); } public void MainMenu(){ Resume(); Reset(); SceneManager.LoadScene("MainMenu"); } public void PopulateLeaderboard(){ for(int i=0; i < leaderboardItemsParent.childCount; i++){ Destroy(leaderboardItemsParent.GetChild(i).gameObject); } // Load the locally saved highscore list List highscores = new List(); for (int i = 0; i < 10; i++) { string nameKey = $"highscore_name_{i}"; string scoreKey = $"highscore_score_{i}"; if (PlayerPrefs.HasKey(scoreKey)) { string playerName = PlayerPrefs.GetString(nameKey, ""); int score = PlayerPrefs.GetInt(scoreKey, 0); highscores.Add(new HighscoreEntry() { name = playerName, score = score }); } } for(int i=0; i < highscores.Count; i++){ GameObject leaderboardItem = Instantiate(leaderboardItemPrefab, leaderboardItemsParent); TMP_Text txtName = leaderboardItem.transform.GetChild(0).GetComponent(); TMP_Text txtScore = leaderboardItem.transform.GetChild(1).GetComponent(); txtName.text = highscores[i].name; txtScore.text = highscores[i].score.ToString("n0"); if(i == 0){ RectTransform rectTransform = leaderboardItem.GetComponent(); rectTransform.sizeDelta = new Vector2(rectTransform.sizeDelta.x, rectTransform.sizeDelta.y * 1.4f); txtName.color = Color.Lerp(Color.green, Color.white, 0.7f); txtScore.color = Color.Lerp(Color.green, Color.white, 0.7f); txtName.fontSize *= 1.4f; txtScore.fontSize *= 1.4f; }else if(i == 1){ RectTransform rectTransform = leaderboardItem.GetComponent(); rectTransform.sizeDelta = new Vector2(rectTransform.sizeDelta.x, rectTransform.sizeDelta.y * 1.2f); txtName.color = Color.Lerp(Color.green, Color.white, 0.3f); txtScore.color = Color.Lerp(Color.green, Color.white, 0.3f); txtName.fontSize *= 1.2f; txtScore.fontSize *= 1.2f; } } } int GetHighestScore(){ // Load the locally saved highscore list List highscores = new List(); for (int i = 0; i < 10; i++) { string nameKey = $"highscore_name_{i}"; string scoreKey = $"highscore_score_{i}"; if (PlayerPrefs.HasKey(scoreKey)) { string playerName = PlayerPrefs.GetString(nameKey, ""); int score = PlayerPrefs.GetInt(scoreKey, 0); highscores.Add(new HighscoreEntry() { name = playerName, score = score }); } } int maxScore = 0; foreach (var entry in highscores) { if (entry.score > maxScore) maxScore = entry.score; } return maxScore; } public void SetHighscore(){ SetHighscore(highscoreNameInput.text, (int)points); highscorePanel.SetActive(false); PopulateLeaderboard(); } void SetHighscore(string name, int score){ // Load list List highscores = new List(); for (int i = 0; i < 10; i++) { string nameKey = $"highscore_name_{i}"; string scoreKey = $"highscore_score_{i}"; if (PlayerPrefs.HasKey(scoreKey)) { string playerName = PlayerPrefs.GetString(nameKey, ""); int oldScore = PlayerPrefs.GetInt(scoreKey, 0); highscores.Add(new HighscoreEntry() { name = playerName, score = oldScore }); } } // Add new score highscores.Add(new HighscoreEntry() { name = name, score = score }); // Sort descending, keep top 10 highscores.Sort((a, b) => b.score.CompareTo(a.score)); if (highscores.Count > 10) highscores.RemoveRange(10, highscores.Count - 10); // Save for (int i = 0; i < highscores.Count; i++) { PlayerPrefs.SetString($"highscore_name_{i}", highscores[i].name); PlayerPrefs.SetInt($"highscore_score_{i}", highscores[i].score); } PlayerPrefs.Save(); } } [System.Serializable] public class HighscoreEntry { public string name; public int score; }