neon_run/Assets/Scripts/GameManager.cs
2025-12-09 12:37:58 +05:30

415 lines
14 KiB
C#

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<GameObject> trafficVehicles = new List<GameObject>();
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<float>());
if(context.ReadValue<float>() < 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<float>();
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<CinemachineBasicMultiChannelPerlin>().m_FrequencyGain = max_shake_ferquency * movespeed / maxspeed;
cam.GetCinemachineComponent<CinemachineBasicMultiChannelPerlin>().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<TrafficVehicle>()!=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<HighscoreEntry> highscores = new List<HighscoreEntry>();
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>();
TMP_Text txtScore = leaderboardItem.transform.GetChild(1).GetComponent<TMP_Text>();
txtName.text = highscores[i].name;
txtScore.text = highscores[i].score.ToString("n0");
if(i == 0){
RectTransform rectTransform = leaderboardItem.GetComponent<RectTransform>();
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>();
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<HighscoreEntry> highscores = new List<HighscoreEntry>();
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<HighscoreEntry> highscores = new List<HighscoreEntry>();
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;
}