415 lines
14 KiB
C#
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;
|
|
}
|