一、框架视图
二、关键代码
ArcadeKartPowerup
using KartGame.KartSystems;
using UnityEngine;
using UnityEngine.Events;
public class ArcadeKartPowerup : MonoBehaviour {
public ArcadeKart.StatPowerup boostStats = new ArcadeKart.StatPowerup
{
MaxTime = 5
};
public bool isCoolingDown { get; private set; }
public float lastActivatedTimestamp { get; private set; }
public float cooldown = 5f;
public bool disableGameObjectWhenActivated;
public UnityEvent onPowerupActivated;
public UnityEvent onPowerupFinishCooldown;
private void Awake()
{
lastActivatedTimestamp = -9999f;
}
private void Update()
{
if (isCoolingDown) {
if (Time.time - lastActivatedTimestamp > cooldown) {
//finished cooldown!
isCoolingDown = false;
onPowerupFinishCooldown.Invoke();
}
}
}
private void OnTriggerEnter(Collider other)
{
if (isCoolingDown) return;
var rb = other.attachedRigidbody;
if (rb) {
var kart = rb.GetComponent<ArcadeKart>();
if (kart)
{
lastActivatedTimestamp = Time.time;
kart.AddPowerup(this.boostStats);
onPowerupActivated.Invoke();
isCoolingDown = true;
if (disableGameObjectWhenActivated) this.gameObject.SetActive(false);
}
}
}
}
AudioManager
using System;
using System.Collections;
using UnityEngine;
using UnityEngine.Audio;
public class AudioManager : MonoBehaviour
{
public AudioMixer audioMixer;
public void EnsureSFXDestruction(AudioSource source)
{
StartCoroutine("DelayedSFXDestruction", source);
}
private IEnumerator DelayedSFXDestruction(AudioSource source)
{
while (source.isPlaying)
{
yield return null;
}
GameObject.Destroy(source.gameObject);
}
}
FramerateCounter
using UnityEngine;
using TMPro;
public class FramerateCounter : MonoBehaviour
{
[Tooltip("Delay between updates of the displayed framerate value")]
public float pollingTime = 0.5f;
[Tooltip("The text field displaying the framerate")]
public TextMeshProUGUI uiText;
float m_AccumulatedDeltaTime = 0f;
int m_AccumulatedFrameCount = 0;
void Update()
{
m_AccumulatedDeltaTime += Time.deltaTime;
m_AccumulatedFrameCount++;
if (m_AccumulatedDeltaTime >= pollingTime)
{
int framerate = Mathf.RoundToInt((float)m_AccumulatedFrameCount / m_AccumulatedDeltaTime);
uiText.text = framerate.ToString();
m_AccumulatedDeltaTime = 0f;
m_AccumulatedFrameCount = 0;
}
}
}
GameConstants
public class GameConstants
{
// all the constant string used across the game
public const string k_AxisNameVertical = "Vertical";
public const string k_AxisNameHorizontal = "Horizontal";
public const string k_MouseAxisNameVertical = "Mouse Y";
public const string k_MouseAxisNameHorizontal = "Mouse X";
public const string k_AxisNameJoystickLookVertical = "Look Y";
public const string k_AxisNameJoystickLookHorizontal = "Look X";
public const string k_ButtonNameJump = "Jump";
public const string k_ButtonNameFire = "Fire";
public const string k_ButtonNameGamepadFire = "Gamepad Fire";
public const string k_ButtonNameSprint = "Sprint";
public const string k_ButtonNameCrouch = "Crouch";
public const string k_ButtonNameAim = "Aim";
public const string k_ButtonNameGamepadAim = "Gamepad Aim";
public const string k_ButtonNameSwitchWeapon = "Mouse ScrollWheel";
public const string k_ButtonNameGamepadSwitchWeapon = "Gamepad Switch";
public const string k_ButtonNameNextWeapon = "NextWeapon";
public const string k_ButtonNamePauseMenu = "Pause Menu";
public const string k_ButtonNameSubmit = "Submit";
public const string k_ButtonNameCancel = "Cancel";
}
GameFlowManager
using System.Collections;
using UnityEngine;
using UnityEngine.Playables;
using KartGame.KartSystems;
using UnityEngine.SceneManagement;
public enum GameState{Play, Won, Lost}
public class GameFlowManager : MonoBehaviour
{
[Header("Parameters")]
[Tooltip("Duration of the fade-to-black at the end of the game")]
public float endSceneLoadDelay = 3f;
[Tooltip("The canvas group of the fade-to-black screen")]
public CanvasGroup endGameFadeCanvasGroup;
[Header("Win")]
[Tooltip("This string has to be the name of the scene you want to load when winning")]
public string winSceneName = "WinScene";
[Tooltip("Duration of delay before the fade-to-black, if winning")]
public float delayBeforeFadeToBlack = 4f;
[Tooltip("Duration of delay before the win message")]
public float delayBeforeWinMessage = 2f;
[Tooltip("Sound played on win")]
public AudioClip victorySound;
[Tooltip("Prefab for the win game message")]
public DisplayMessage winDisplayMessage;
public PlayableDirector raceCountdownTrigger;
[Header("Lose")]
[Tooltip("This string has to be the name of the scene you want to load when losing")]
public string loseSceneName = "LoseScene";
[Tooltip("Prefab for the lose game message")]
public DisplayMessage loseDisplayMessage;
public GameState gameState { get; private set; }
public bool autoFindKarts = true;
public ArcadeKart playerKart;
ArcadeKart[] karts;
ObjectiveManager m_ObjectiveManager;
TimeManager m_TimeManager;
float m_TimeLoadEndGameScene;
string m_SceneToLoad;
float elapsedTimeBeforeEndScene = 0;
void Start()
{
if (autoFindKarts)
{
karts = FindObjectsOfType<ArcadeKart>();
if (karts.Length > 0)
{
if (!playerKart) playerKart = karts[0];
}
DebugUtility.HandleErrorIfNullFindObject<ArcadeKart, GameFlowManager>(playerKart, this);
}
m_ObjectiveManager = FindObjectOfType<ObjectiveManager>();
DebugUtility.HandleErrorIfNullFindObject<ObjectiveManager, GameFlowManager>(m_ObjectiveManager, this);
m_TimeManager = FindObjectOfType<TimeManager>();
DebugUtility.HandleErrorIfNullFindObject<TimeManager, GameFlowManager>(m_TimeManager, this);
AudioUtility.SetMasterVolume(1);
winDisplayMessage.gameObject.SetActive(false);
loseDisplayMessage.gameObject.SetActive(false);
m_TimeManager.StopRace();
foreach (ArcadeKart k in karts)
{
k.SetCanMove(false);
}
//run race countdown animation
ShowRaceCountdownAnimation();
StartCoroutine(ShowObjectivesRoutine());
StartCoroutine(CountdownThenStartRaceRoutine());
}
IEnumerator CountdownThenStartRaceRoutine() {
yield return new WaitForSeconds(3f);
StartRace();
}
void StartRace() {
foreach (ArcadeKart k in karts)
{
k.SetCanMove(true);
}
m_TimeManager.StartRace();
}
void ShowRaceCountdownAnimation() {
raceCountdownTrigger.Play();
}
IEnumerator ShowObjectivesRoutine() {
while (m_ObjectiveManager.Objectives.Count == 0)
yield return null;
yield return new WaitForSecondsRealtime(0.2f);
for (int i = 0; i < m_ObjectiveManager.Objectives.Count; i++)
{
if (m_ObjectiveManager.Objectives[i].displayMessage)m_ObjectiveManager.Objectives[i].displayMessage.Display();
yield return new WaitForSecondsRealtime(1f);
}
}
void Update()
{
if (gameState != GameState.Play)
{
elapsedTimeBeforeEndScene += Time.deltaTime;
if(elapsedTimeBeforeEndScene >= endSceneLoadDelay)
{
float timeRatio = 1 - (m_TimeLoadEndGameScene - Time.time) / endSceneLoadDelay;
endGameFadeCanvasGroup.alpha = timeRatio;
float volumeRatio = Mathf.Abs(timeRatio);
float volume = Mathf.Clamp(1 - volumeRatio, 0, 1);
AudioUtility.SetMasterVolume(volume);
// See if it's time to load the end scene (after the delay)
if (Time.time >= m_TimeLoadEndGameScene)
{
SceneManager.LoadScene(m_SceneToLoad);
gameState = GameState.Play;
}
}
}
else
{
if (m_ObjectiveManager.AreAllObjectivesCompleted())
EndGame(true);
if (m_TimeManager.IsFinite && m_TimeManager.IsOver)
EndGame(false);
}
}
void EndGame(bool win)
{
// unlocks the cursor before leaving the scene, to be able to click buttons
Cursor.lockState = CursorLockMode.None;
Cursor.visible = true;
m_TimeManager.StopRace();
// Remember that we need to load the appropriate end scene after a delay
gameState = win ? GameState.Won : GameState.Lost;
endGameFadeCanvasGroup.gameObject.SetActive(true);
if (win)
{
m_SceneToLoad = winSceneName;
m_TimeLoadEndGameScene = Time.time + endSceneLoadDelay + delayBeforeFadeToBlack;
// play a sound on win
var audioSource = gameObject.AddComponent<AudioSource>();
audioSource.clip = victorySound;
audioSource.playOnAwake = false;
audioSource.outputAudioMixerGroup = AudioUtility.GetAudioGroup(AudioUtility.AudioGroups.HUDVictory);
audioSource.PlayScheduled(AudioSettings.dspTime + delayBeforeWinMessage);
// create a game message
winDisplayMessage.delayBeforeShowing = delayBeforeWinMessage;
winDisplayMessage.gameObject.SetActive(true);
}
else
{
m_SceneToLoad = loseSceneName;
m_TimeLoadEndGameScene = Time.time + endSceneLoadDelay + delayBeforeFadeToBlack;
// create a game message
loseDisplayMessage.delayBeforeShowing = delayBeforeWinMessage;
loseDisplayMessage.gameObject.SetActive(true);
}
}
}
InGameMenuManager
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public class InGameMenuManager : MonoBehaviour
{
[Tooltip("Root GameObject of the menu used to toggle its activation")]
public GameObject menuRoot;
[Tooltip("Master volume when menu is open")]
[Range(0.001f, 1f)]
public float volumeWhenMenuOpen = 0.5f;
[Tooltip("Toggle component for shadows")]
public Toggle shadowsToggle;
[Tooltip("Toggle component for framerate display")]
public Toggle framerateToggle;
[Tooltip("GameObject for the controls")]
public GameObject controlImage;
//PlayerInputHandler m_PlayerInputsHandler;
FramerateCounter m_FramerateCounter;
void Start()
{
//m_PlayerInputsHandler = FindObjectOfType<PlayerInputHandler>();
//DebugUtility.HandleErrorIfNullFindObject<PlayerInputHandler, InGameMenuManager>(m_PlayerInputsHandler, this);
m_FramerateCounter = FindObjectOfType<FramerateCounter>();
//DebugUtility.HandleErrorIfNullFindObject<FramerateCounter, InGameMenuManager>(m_FramerateCounter, this);
menuRoot.SetActive(false);
shadowsToggle.isOn = QualitySettings.shadows != ShadowQuality.Disable;
shadowsToggle.onValueChanged.AddListener(OnShadowsChanged);
framerateToggle.isOn = m_FramerateCounter.uiText.gameObject.activeSelf;
framerateToggle.onValueChanged.AddListener(OnFramerateCounterChanged);
}
private void Update()
{
if (Input.GetButtonDown(GameConstants.k_ButtonNamePauseMenu)
|| (menuRoot.activeSelf && Input.GetButtonDown(GameConstants.k_ButtonNameCancel)))
{
if (controlImage.activeSelf)
{
controlImage.SetActive(false);
return;
}
SetPauseMenuActivation(!menuRoot.activeSelf);
}
if (Input.GetAxisRaw(GameConstants.k_AxisNameVertical) != 0)
{
if (EventSystem.current.currentSelectedGameObject == null)
{
EventSystem.current.SetSelectedGameObject(null);
shadowsToggle.Select();
}
}
}
public void ClosePauseMenu()
{
SetPauseMenuActivation(false);
}
public void TogglePauseMenu()
{
SetPauseMenuActivation(!menuRoot.activeSelf);
}
void SetPauseMenuActivation(bool active)
{
menuRoot.SetActive(active);
if (menuRoot.activeSelf)
{
// Cursor.lockState = CursorLockMode.None;
// Cursor.visible = true;
Time.timeScale = 0f;
AudioUtility.SetMasterVolume(volumeWhenMenuOpen);
EventSystem.current.SetSelectedGameObject(null);
}
else
{
// Cursor.lockState = CursorLockMode.Locked;
// Cursor.visible = false;
Time.timeScale = 1f;
AudioUtility.SetMasterVolume(1);
}
}
void OnShadowsChanged(bool newValue)
{
QualitySettings.shadows = newValue ? ShadowQuality.All : ShadowQuality.Disable;
}
void OnFramerateCounterChanged(bool newValue)
{
m_FramerateCounter.uiText.gameObject.SetActive(newValue);
}
public void OnShowControlButtonClicked(bool show)
{
controlImage.SetActive(show);
}
}
MinMaxParameters
using UnityEngine;
[System.Serializable]
public struct MinMaxFloat
{
public float min;
public float max;
public float GetValueFromRatio(float ratio)
{
return Mathf.Lerp(min, max, ratio);
}
}
[System.Serializable]
public struct MinMaxColor
{
[ColorUsage(true, true)]
public Color min;
[ColorUsage(true, true)]
public Color max;
public Color GetValueFromRatio(float ratio)
{
return Color.Lerp(min, max, ratio);
}
}
[System.Serializable]
public struct MinMaxVector3
{
public Vector3 min;
public Vector3 max;
public Vector3 GetValueFromRatio(float ratio)
{
return Vector3.Lerp(min, max, ratio);
}
}
Objective
using System;
using System.Collections.Generic;
using KartGame.Track;
using UnityEngine;
using UnityEngine.Events;
public enum GameMode
{
TimeLimit, Crash, Laps
}
public abstract class Objective : MonoBehaviour
{
[Tooltip("Which game mode are you playing?")]
public GameMode gameMode;
protected int m_PickupTotal;
[Tooltip("Name of the target object the player will collect/crash/complete for this objective")]
public string targetName;
[Tooltip("Short text explaining the objective that will be shown on screen")]
public string title;
[Tooltip("Short text explaining the objective that will be shown on screen")]
public string description;
[Tooltip("Whether the objective is required to win or not")]
public bool isOptional;
[Tooltip("Delay before the objective becomes visible")]
public float delayVisible;
[Header("Requirements")] [Tooltip("Does the objective have a time limit?")]
public bool isTimed;
[Tooltip("If there is a time limit, how long in secs?")]
public int totalTimeInSecs;
public bool isCompleted { get; protected set; }
public bool isBlocking() => !(isOptional || isCompleted);
public UnityAction<UnityActionUpdateObjective> onUpdateObjective;
protected NotificationHUDManager m_NotificationHUDManager;
protected ObjectiveHUDManger m_ObjectiveHUDManger;
public static Action<TargetObject> OnRegisterPickup;
public static Action<TargetObject> OnUnregisterPickup;
public DisplayMessage displayMessage;
private List<TargetObject> pickups = new List<TargetObject>();
public List<TargetObject> Pickups => pickups;
public int NumberOfPickupsTotal { get; private set; }
public int NumberOfPickupsRemaining => Pickups.Count;
public int NumberOfActivePickupsRemaining()
{
int total = 0;
for (int i = 0; i < Pickups.Count; i++)
{
if (Pickups[i].active) total++;
}
return total;
}
protected abstract void ReachCheckpoint(int remaining);
void OnEnable()
{
OnRegisterPickup += RegisterPickup;
OnUnregisterPickup += UnregisterPickup;
}
protected void Register()
{
// add this objective to the list contained in the objective manager
ObjectiveManager.RegisterObjective(this);
// register this objective in the ObjectiveHUDManger
m_ObjectiveHUDManger = FindObjectOfType<ObjectiveHUDManger>();
DebugUtility.HandleErrorIfNullFindObject<ObjectiveHUDManger, Objective>(m_ObjectiveHUDManger, this);
m_ObjectiveHUDManger.RegisterObjective(this);
// register this objective in the NotificationHUDManager
m_NotificationHUDManager = FindObjectOfType<NotificationHUDManager>();
DebugUtility.HandleErrorIfNullFindObject<NotificationHUDManager, Objective>(m_NotificationHUDManager, this);
m_NotificationHUDManager.RegisterObjective(this);
}
public void UpdateObjective(string descriptionText, string counterText, string notificationText)
{
onUpdateObjective?.Invoke(new UnityActionUpdateObjective(this, descriptionText, counterText, false,
notificationText));
}
public void CompleteObjective(string descriptionText, string counterText, string notificationText)
{
isCompleted = true;
UpdateObjective(descriptionText, counterText, notificationText);
// unregister this objective form both HUD managers
m_ObjectiveHUDManger.UnregisterObjective(this);
m_NotificationHUDManager.UnregisterObjective(this);
}
public virtual string GetUpdatedCounterAmount()
{
return "";
}
public void RegisterPickup(TargetObject pickup)
{
if (pickup.gameMode != gameMode) return;
Pickups.Add(pickup);
NumberOfPickupsTotal++;
}
public void UnregisterPickup(TargetObject pickupCollected)
{
if (pickupCollected.gameMode != gameMode) return;
// removes the pickup from the list, so that we can keep track of how many are left on the map
if (pickupCollected.gameMode == GameMode.Laps)
{
pickupCollected.active = false;
LapObject lapObject = (LapObject) pickupCollected;
if (!lapObject.finishLap) return;
if (!lapObject.lapOverNextPass)
{
TimeDisplay.OnUpdateLap();
lapObject.lapOverNextPass = true;
return;
}
if (NumberOfActivePickupsRemaining() != 0) return;
ReachCheckpoint(0);
ResetPickups();
TimeDisplay.OnUpdateLap();
}
else
{
ReachCheckpoint(NumberOfPickupsRemaining - 1);
Pickups.Remove(pickupCollected);
if (gameMode == GameMode.Laps)
KartGame.Track.TimeDisplay.OnUpdateLap();
}
}
public void ResetPickups()
{
for (int i = 0; i < Pickups.Count; i++)
{
Pickups[i].active = true;
}
}
void OnDisable()
{
OnRegisterPickup -= RegisterPickup;
OnUnregisterPickup -= UnregisterPickup;
}
}
public class UnityActionUpdateObjective
{
public Objective objective;
public string descriptionText;
public string counterText;
public bool isComplete;
public string notificationText;
public UnityActionUpdateObjective(Objective objective, string descriptionText, string counterText, bool isComplete, string notificationText)
{
this.objective = objective;
this.descriptionText = descriptionText;
this.counterText = counterText;
this.isComplete = isComplete;
this.notificationText = notificationText;
}
}
ObjectiveManager
using System;
using System.Collections.Generic;
using UnityEngine;
public class ObjectiveManager : MonoBehaviour
{
List<Objective> m_Objectives = new List<Objective>();
public List<Objective> Objectives => m_Objectives;
public static Action<Objective> RegisterObjective;
public void OnEnable()
{
RegisterObjective += OnRegisterObjective;
}
public bool AreAllObjectivesCompleted()
{
if (m_Objectives.Count == 0)
return false;
for (int i = 0; i < m_Objectives.Count; i++)
{
// pass every objectives to check if they have been completed
if (m_Objectives[i].isBlocking())
{
// break the loop as soon as we find one uncompleted objective
return false;
}
}
// found no uncompleted objective
return true;
}
public void OnRegisterObjective(Objective objective)
{
m_Objectives.Add(objective);
}
}
PrefabReplacer
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PrefabReplacer : MonoBehaviour
{
[System.Serializable]
public struct ReplacementDefinition
{
public GameObject SourcePrefab;
public GameObject TargetPrefab;
}
public bool switchOrder;
public List<ReplacementDefinition> replacements = new List<ReplacementDefinition>();
}
PrefabReplacerOnInstance
using System.Collections.Generic;
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
[ExecuteInEditMode]
public class PrefabReplacerOnInstance : MonoBehaviour
{
public GameObject TargetPrefab;
void Awake()
{
#if UNITY_EDITOR
List<GameObject> allPrefabObjectsInScene = new List<GameObject>();
foreach (Transform t in GameObject.FindObjectsOfType<Transform>())
{
if (PrefabUtility.IsAnyPrefabInstanceRoot(t.gameObject))
{
allPrefabObjectsInScene.Add(t.gameObject);
}
}
foreach (GameObject go in allPrefabObjectsInScene)
{
GameObject instanceSource = PrefabUtility.GetCorrespondingObjectFromSource(go);
if (instanceSource == TargetPrefab)
{
transform.SetParent(go.transform.parent);
transform.position = go.transform.position;
transform.rotation = go.transform.rotation;
transform.localScale = go.transform.localScale;
// Undo.Register
Undo.DestroyObjectImmediate(go);
Debug.Log("Replaced prefab in scene");
DestroyImmediate(this);
break;
}
}
#endif
}
}
SimpleShaker
using UnityEngine;
namespace KartGame
{
public class SimpleShaker : MonoBehaviour
{
Vector3 basePos;
Quaternion baseRot;
public float shakeAmount = .1f;
public float rotationShakeAmount = .1f;
public float frequency = 10;
float seed1;
float seed2;
float seed3;
// Start is called before the first frame update
void Start()
{
basePos = transform.localPosition;
baseRot = transform.localRotation;
seed1 = Random.Range(0, 999);
seed2 = Random.Range(0, 999);
seed3 = Random.Range(0, 999);
}
// Update is called once per frame
void Update()
{
transform.localPosition = basePos + shakeAmount * new Vector3(
Mathf.PerlinNoise(Time.time * frequency, seed1)-.5f,
Mathf.PerlinNoise(Time.time * frequency, seed2)-.5f,
Mathf.PerlinNoise(Time.time * frequency, seed3)-.5f);
var rotationNoise = new Vector3(
Mathf.PerlinNoise(Time.time * frequency, seed3) - .5f,
Mathf.PerlinNoise(Time.time * frequency, seed2) - .5f,
Mathf.PerlinNoise(Time.time * frequency, seed1) - .5f);
transform.localRotation = Quaternion.Euler( rotationNoise * rotationShakeAmount) * baseRot;
}
}
}
TakeScreenshot
using System.IO;
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
using UnityEngine.UI;
public class TakeScreenshot : MonoBehaviour
{
[Tooltip("Root of the screenshot panel in the menu")]
public GameObject screenshotPanel;
[Tooltip("Name for the screenshot file")]
public string fileName = "Screenshot";
[Tooltip("Image to display the screenshot in")]
public RawImage previewImage;
CanvasGroup m_MenuCanvas = null;
Texture2D m_Texture;
bool m_TakeScreenshot;
bool m_ScreenshotTaken;
bool m_IsFeatureDisable;
string getPath() => k_ScreenshotPath + fileName + ".png";
const string k_ScreenshotPath = "Assets/";
void Awake()
{
#if !UNITY_EDITOR
// this feature is available only in the editor
screenshotPanel.SetActive(false);
m_IsFeatureDisable = true;
#else
m_IsFeatureDisable = false;
var gameMenuManager = GetComponent<InGameMenuManager>();
DebugUtility.HandleErrorIfNullGetComponent<InGameMenuManager, TakeScreenshot>(gameMenuManager, this, gameObject);
m_MenuCanvas = gameMenuManager.menuRoot.GetComponent<CanvasGroup>();
DebugUtility.HandleErrorIfNullGetComponent<CanvasGroup, TakeScreenshot>(m_MenuCanvas, this, gameMenuManager.menuRoot.gameObject);
LoadScreenshot();
#endif
}
void Update()
{
previewImage.enabled = previewImage.texture != null;
if (m_IsFeatureDisable)
return;
if (m_TakeScreenshot)
{
m_MenuCanvas.alpha = 0;
ScreenCapture.CaptureScreenshot(getPath());
m_TakeScreenshot = false;
m_ScreenshotTaken = true;
return;
}
if (m_ScreenshotTaken)
{
LoadScreenshot();
#if UNITY_EDITOR
AssetDatabase.Refresh();
#endif
m_MenuCanvas.alpha = 1;
m_ScreenshotTaken = false;
}
}
public void OnTakeScreenshotButtonPressed()
{
m_TakeScreenshot = true;
}
void LoadScreenshot()
{
if (File.Exists(getPath()))
{
var bytes = File.ReadAllBytes(getPath());
m_Texture = new Texture2D(2, 2);
m_Texture.LoadImage(bytes);
m_Texture.Apply();
previewImage.texture = m_Texture;
}
}
}
TimeManager
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TimeManager : MonoBehaviour
{
public bool IsFinite { get; private set; }
public float TotalTime { get; private set; }
public float TimeRemaining { get; private set; }
public bool IsOver { get; private set; }
private bool raceStarted;
public static Action<float> OnAdjustTime;
public static Action<int, bool, GameMode> OnSetTime;
private void Awake()
{
IsFinite = false;
TimeRemaining = TotalTime;
}
void OnEnable()
{
OnAdjustTime += AdjustTime;
OnSetTime += SetTime;
}
private void OnDisable()
{
OnAdjustTime -= AdjustTime;
OnSetTime -= SetTime;
}
private void AdjustTime(float delta)
{
TimeRemaining += delta;
}
private void SetTime(int time, bool isFinite, GameMode gameMode)
{
TotalTime = time;
IsFinite = isFinite;
TimeRemaining = TotalTime;
}
void Update()
{
if (!raceStarted) return;
if (IsFinite && !IsOver)
{
TimeRemaining -= Time.deltaTime;
if (TimeRemaining <= 0)
{
TimeRemaining = 0;
IsOver = true;
}
}
}
public void StartRace()
{
raceStarted = true;
}
public void StopRace() {
raceStarted = false;
}
}