Added comments to everything
This commit is contained in:
@@ -1,109 +0,0 @@
|
||||
using System.Linq;
|
||||
using UnityEngine; using Game; using Music; using Player;
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityEngine.UI;
|
||||
using UnityEngine.Events;
|
||||
using System.Collections;
|
||||
|
||||
namespace Game{
|
||||
public class HubManager : MonoBehaviour
|
||||
{
|
||||
public static HubManager Instance;
|
||||
public GameObject hubCamera;
|
||||
public GameObject gameButtonsParent;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (Instance == null)
|
||||
{
|
||||
Instance = this;
|
||||
}
|
||||
else
|
||||
{
|
||||
Destroy(this.gameObject);
|
||||
}
|
||||
if (hubCamera.GetComponent<AudioListener>() == null)
|
||||
{
|
||||
hubCamera.AddComponent<AudioListener>();
|
||||
}
|
||||
hubCamera.SetActive(true);
|
||||
MusicManager.Instance.StartPlaylist();
|
||||
print("Game started");
|
||||
}
|
||||
|
||||
public void LoadScene(string sceneName)
|
||||
{
|
||||
UnloadGameScene();
|
||||
hubCamera.SetActive(false);
|
||||
SceneManager.LoadScene(sceneName, LoadSceneMode.Additive);
|
||||
var activeCamera = Camera.main;
|
||||
if (activeCamera != null && activeCamera.GetComponent<AudioListener>() == null)
|
||||
{
|
||||
activeCamera.gameObject.AddComponent<AudioListener>();
|
||||
}
|
||||
MusicManager.Instance.StartPlaylist();
|
||||
print("Loading scene: " + sceneName);
|
||||
}
|
||||
|
||||
public void UnloadGameScene()
|
||||
{
|
||||
hubCamera.SetActive(true);
|
||||
try
|
||||
{
|
||||
SceneManager.UnloadSceneAsync(SceneManager.GetSceneAt(1));
|
||||
}
|
||||
catch {}
|
||||
ChangeGameButtonsInteractability(false);
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (InputSystem.GetDevice<Keyboard>().escapeKey.wasPressedThisFrame)
|
||||
{
|
||||
UnloadGameScene();
|
||||
ChangeGameButtonsInteractability(true);
|
||||
if (GameManager.players != null)
|
||||
{
|
||||
foreach (GameObject player in GameManager.players.ToList())
|
||||
{
|
||||
GameManager.players.Remove(player);
|
||||
if (player != null)
|
||||
{
|
||||
Destroy(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (MusicManager.Instance != null)
|
||||
{
|
||||
MusicManager.Instance.StartPlaylist("Title Screen");
|
||||
}
|
||||
var cameras = FindObjectsByType<Camera>(FindObjectsSortMode.None);
|
||||
if (cameras != null)
|
||||
{
|
||||
foreach (Camera camera in cameras)
|
||||
{
|
||||
camera.enabled = false;
|
||||
}
|
||||
}
|
||||
GameManager.players?.Clear();
|
||||
GameManager.playerColors?.Clear();
|
||||
if (GameManager.Instance != null)
|
||||
{
|
||||
GameManager.Instance.gameOver = false;
|
||||
}
|
||||
SceneManager.LoadScene("Title Screen");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void ChangeGameButtonsInteractability(bool interactable)
|
||||
{
|
||||
gameButtonsParent.transform.parent.gameObject.SetActive(interactable);
|
||||
foreach (Transform button in gameButtonsParent.transform)
|
||||
{
|
||||
button.GetComponent<Button>().interactable = interactable;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,69 +1,113 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine; using Game; using Music; using Player;
|
||||
using UnityEngine;
|
||||
using Game;
|
||||
using Music;
|
||||
using Player;
|
||||
|
||||
namespace Music
|
||||
{
|
||||
|
||||
public class AudioManager : MonoBehaviour
|
||||
{
|
||||
public List<SoundEffect> soundEffects = new List<SoundEffect>();
|
||||
|
||||
public static AudioManager Instance;
|
||||
|
||||
private void Awake()
|
||||
/// <summary>
|
||||
/// This class manages the playback of sound effects in the game.
|
||||
/// It provides functionality to play specific sounds by name and ensures a singleton instance for global access.
|
||||
/// </summary>
|
||||
public class AudioManager : MonoBehaviour
|
||||
{
|
||||
if (Instance == null)
|
||||
{
|
||||
Instance = this;
|
||||
DontDestroyOnLoad(gameObject);
|
||||
}
|
||||
else
|
||||
{
|
||||
Destroy(gameObject);
|
||||
}
|
||||
/// <summary>
|
||||
/// A list of all sound effects managed by the AudioManager.
|
||||
/// </summary>
|
||||
public List<SoundEffect> soundEffects = new List<SoundEffect>();
|
||||
|
||||
foreach (Transform child in transform)
|
||||
/// <summary>
|
||||
/// The singleton instance of the <see cref="AudioManager"/> class.
|
||||
/// </summary>
|
||||
public static AudioManager Instance;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the singleton instance and loads all child AudioSources as sound effects.
|
||||
/// </summary>
|
||||
private void Awake()
|
||||
{
|
||||
var soundEffect = new SoundEffect(child.name, child.GetComponent<AudioSource>());
|
||||
if (soundEffect != null)
|
||||
// Ensure only one instance of the AudioManager exists
|
||||
if (Instance == null)
|
||||
{
|
||||
soundEffects.Add(soundEffect);
|
||||
Instance = this;
|
||||
DontDestroyOnLoad(gameObject); // Persist across scenes
|
||||
}
|
||||
else
|
||||
{
|
||||
Destroy(gameObject); // Destroy duplicate instances
|
||||
}
|
||||
|
||||
// Load all child AudioSources into the soundEffects list
|
||||
foreach (Transform child in transform)
|
||||
{
|
||||
var soundEffect = new SoundEffect(child.name, child.GetComponent<AudioSource>());
|
||||
if (soundEffect != null)
|
||||
{
|
||||
soundEffects.Add(soundEffect);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void PlaySound(string soundName)
|
||||
{
|
||||
if (soundName == "Punch")
|
||||
/// <summary>
|
||||
/// Plays a sound effect by its name.
|
||||
/// </summary>
|
||||
/// <param name="soundName">The name of the sound effect to play.</param>
|
||||
/// <remarks>
|
||||
/// If the sound name is "Punch," it plays multiple punch-related sound effects.
|
||||
/// If the sound is not found, a warning is logged to the console.
|
||||
/// </remarks>
|
||||
public void PlaySound(string soundName)
|
||||
{
|
||||
soundEffects.Find(x => x.name == "Punch").audioSource.Play();
|
||||
soundEffects.Find(x => x.name == "Punch 2").audioSource.Play();
|
||||
soundEffects.Find(x => x.name == "Punch 3").audioSource.Play();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var soundEffect in soundEffects)
|
||||
{
|
||||
if (soundEffect.name == soundName)
|
||||
// Special case: Play multiple punch sound effects
|
||||
if (soundName == "Punch")
|
||||
{
|
||||
soundEffect.audioSource.Play();
|
||||
soundEffects.Find(x => x.name == "Punch").audioSource.Play();
|
||||
soundEffects.Find(x => x.name == "Punch 2").audioSource.Play();
|
||||
soundEffects.Find(x => x.name == "Punch 3").audioSource.Play();
|
||||
return;
|
||||
}
|
||||
|
||||
// Find and play the sound effect by name
|
||||
foreach (var soundEffect in soundEffects)
|
||||
{
|
||||
if (soundEffect.name == soundName)
|
||||
{
|
||||
soundEffect.audioSource.Play();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Log a warning if the sound effect is not found
|
||||
Debug.LogWarning($"Sound '{soundName}' not found!");
|
||||
}
|
||||
Debug.LogWarning($"Sound '{soundName}' not found!");
|
||||
}
|
||||
}
|
||||
|
||||
public class SoundEffect
|
||||
{
|
||||
public string name;
|
||||
public AudioSource audioSource;
|
||||
|
||||
public SoundEffect(string name, AudioSource audioSource)
|
||||
/// <summary>
|
||||
/// Represents a sound effect, including its name and associated AudioSource.
|
||||
/// </summary>
|
||||
public class SoundEffect
|
||||
{
|
||||
this.name = name;
|
||||
this.audioSource = audioSource;
|
||||
/// <summary>
|
||||
/// The name of the sound effect.
|
||||
/// </summary>
|
||||
public string name;
|
||||
|
||||
/// <summary>
|
||||
/// The AudioSource component that plays the sound effect.
|
||||
/// </summary>
|
||||
public AudioSource audioSource;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SoundEffect"/> class.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the sound effect.</param>
|
||||
/// <param name="audioSource">The AudioSource component for the sound effect.</param>
|
||||
public SoundEffect(string name, AudioSource audioSource)
|
||||
{
|
||||
this.name = name;
|
||||
this.audioSource = audioSource;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,35 +1,64 @@
|
||||
using UnityEngine; using Game; using Music; using Player;
|
||||
using UnityEngine;
|
||||
using Game;
|
||||
using Music;
|
||||
using Player;
|
||||
using UnityEngine.InputSystem;
|
||||
|
||||
namespace Game
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// This class is used to create cards for players when they join the game.
|
||||
/// </summary>
|
||||
public class PlayerCardCreator : MonoBehaviour
|
||||
{
|
||||
/// <summary>
|
||||
/// A single instance of this class that can be accessed from anywhere.
|
||||
/// </summary>
|
||||
public static PlayerCardCreator Instance;
|
||||
|
||||
/// <summary>
|
||||
/// The template used to create new player cards.
|
||||
/// </summary>
|
||||
public GameObject playerJoinCardPrefab;
|
||||
|
||||
private void Awake() // Ensures only one instance of PlayerCardCreator exists
|
||||
/// <summary>
|
||||
/// Makes sure there is only one PlayerCardCreator in the game.
|
||||
/// </summary>
|
||||
private void Awake()
|
||||
{
|
||||
if (Instance == null) Instance = this;
|
||||
// If this is the first instance, set it as the main one.
|
||||
if (Instance == null)
|
||||
{
|
||||
Instance = this;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If another instance already exists, remove this one.
|
||||
Destroy(gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
public PlayerJoinCard CreateCard() // Creates a player join card
|
||||
/// <summary>
|
||||
/// Creates a new player card and returns it.
|
||||
/// </summary>
|
||||
/// <returns>The new player card, or nothing if it couldn't be created.</returns>
|
||||
public PlayerJoinCard CreateCard()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Make a new player card using the template.
|
||||
GameObject card = Instantiate(playerJoinCardPrefab, transform);
|
||||
|
||||
// Return the player card so it can be used.
|
||||
return card.GetComponent<PlayerJoinCard>();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// If something goes wrong, return nothing.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,17 +1,30 @@
|
||||
using UnityEngine; using Game; using Music; using Player;
|
||||
using UnityEngine;
|
||||
using Game;
|
||||
using Music;
|
||||
using Player;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace Game
|
||||
{
|
||||
public class EventSystemizer : MonoBehaviour
|
||||
{
|
||||
private void Update() // Ensures only one instance of EventSystem exists
|
||||
/// <summary>
|
||||
/// This class makes sure there is only one EventSystem in the game at any time.
|
||||
/// </summary>
|
||||
public class EventSystemizer : MonoBehaviour
|
||||
{
|
||||
foreach (EventSystem system in FindObjectsByType<EventSystem>(FindObjectsSortMode.None))
|
||||
/// <summary>
|
||||
/// Checks every frame to ensure there is only one EventSystem in the game.
|
||||
/// </summary>
|
||||
private void Update()
|
||||
{
|
||||
if (system == GetComponent<EventSystem>()) continue;
|
||||
Destroy(system.gameObject);
|
||||
// Find all EventSystem objects in the scene
|
||||
foreach (EventSystem system in FindObjectsByType<EventSystem>(FindObjectsSortMode.None))
|
||||
{
|
||||
// Skip the EventSystem attached to this GameObject
|
||||
if (system == GetComponent<EventSystem>()) continue;
|
||||
|
||||
// Remove any extra EventSystem objects
|
||||
Destroy(system.gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,58 +1,108 @@
|
||||
using System.Collections;
|
||||
using UnityEngine; using Game; using Music; using Player;
|
||||
using UnityEngine;
|
||||
using Game;
|
||||
using Music;
|
||||
using Player;
|
||||
|
||||
namespace Game
|
||||
{
|
||||
|
||||
public class FallPlatform : MonoBehaviour
|
||||
{
|
||||
public float fallDelay = 2f; // Delay before the platform falls
|
||||
public float resetDelay = 4f; // Delay before the platform resets
|
||||
|
||||
bool falling;
|
||||
Rigidbody2D rb;
|
||||
Vector3 defposition;
|
||||
|
||||
void Start()
|
||||
/// <summary>
|
||||
/// This class controls platforms that fall when touched by a player or another platform.
|
||||
/// The platform will fall after a delay and then reset to its original position.
|
||||
/// </summary>
|
||||
public class FallPlatform : MonoBehaviour
|
||||
{
|
||||
defposition = transform.parent.position;
|
||||
rb = transform.parent.GetComponent<Rigidbody2D>();
|
||||
}
|
||||
private void OnTriggerEnter2D(Collider2D collision) // Makes platform fall when player or another platform touch it
|
||||
{
|
||||
try
|
||||
/// <summary>
|
||||
/// The time (in seconds) before the platform starts falling after being triggered.
|
||||
/// </summary>
|
||||
public float fallDelay = 2f;
|
||||
|
||||
/// <summary>
|
||||
/// The time (in seconds) before the platform resets to its original position after falling.
|
||||
/// </summary>
|
||||
public float resetDelay = 4f;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the platform is currently falling.
|
||||
/// </summary>
|
||||
private bool falling;
|
||||
|
||||
/// <summary>
|
||||
/// Reference to the Rigidbody2D component of the platform's parent object.
|
||||
/// </summary>
|
||||
private Rigidbody2D rb;
|
||||
|
||||
/// <summary>
|
||||
/// The original position of the platform's parent object.
|
||||
/// </summary>
|
||||
private Vector3 defposition;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the platform's Rigidbody2D and stores its original position.
|
||||
/// </summary>
|
||||
private void Start()
|
||||
{
|
||||
if (collision.transform.childCount != 0 && !falling && (collision.gameObject.CompareTag("Player") || collision.transform.GetChild(0).TryGetComponent(out FallPlatform _)))
|
||||
defposition = transform.parent.position;
|
||||
rb = transform.parent.GetComponent<Rigidbody2D>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Triggers the platform to fall when a player or another platform touches it.
|
||||
/// </summary>
|
||||
/// <param name="collision">The object that collided with the platform.</param>
|
||||
private void OnTriggerEnter2D(Collider2D collision)
|
||||
{
|
||||
try
|
||||
{
|
||||
StartCoroutine(FallAfterDelay());
|
||||
// Check if the collision is caused by a player or another falling platform
|
||||
if (collision.transform.childCount != 0 && !falling &&
|
||||
(collision.gameObject.CompareTag("Player") || collision.transform.GetChild(0).TryGetComponent(out FallPlatform _)))
|
||||
{
|
||||
StartCoroutine(FallAfterDelay());
|
||||
}
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
Debug.LogError("Error in FallPlatform: " + e.Message);
|
||||
}
|
||||
|
||||
}
|
||||
catch (System.Exception e)
|
||||
|
||||
/// <summary>
|
||||
/// Makes the platform fall after a delay and resets it after another delay.
|
||||
/// </summary>
|
||||
/// <returns>An IEnumerator for coroutine execution.</returns>
|
||||
private IEnumerator FallAfterDelay()
|
||||
{
|
||||
Debug.LogError("Error in FallPlatform: " + e.Message);
|
||||
falling = true;
|
||||
|
||||
// Wait for the fall delay before making the platform fall
|
||||
yield return new WaitForSeconds(fallDelay);
|
||||
rb.bodyType = RigidbodyType2D.Dynamic;
|
||||
|
||||
// Wait for the reset delay before resetting the platform
|
||||
yield return new WaitForSeconds(resetDelay);
|
||||
transform.parent.GetComponent<Animator>().SetTrigger("respawn");
|
||||
|
||||
// Wait briefly before resetting the platform
|
||||
yield return new WaitForSeconds(0.5f);
|
||||
Respawn();
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator FallAfterDelay() // Sets platform to fall and respawn
|
||||
{
|
||||
falling = true;
|
||||
yield return new WaitForSeconds(fallDelay);
|
||||
rb.bodyType = RigidbodyType2D.Dynamic;
|
||||
yield return new WaitForSeconds(resetDelay);
|
||||
transform.parent.GetComponent<Animator>().SetTrigger("respawn");
|
||||
yield return new WaitForSeconds(0.5f);
|
||||
Respawn();
|
||||
}
|
||||
/// <summary>
|
||||
/// Resets the platform to its original position and state.
|
||||
/// </summary>
|
||||
private void Respawn()
|
||||
{
|
||||
falling = false;
|
||||
|
||||
//only resets the object script is attached to, need to fix so platform will reset with fall trigger object
|
||||
// Use transform.parent to get the object it's attatched to
|
||||
private void Respawn() // Resets the platform position
|
||||
{
|
||||
falling = false;
|
||||
rb.bodyType = RigidbodyType2D.Static;
|
||||
transform.parent.position = defposition;
|
||||
transform.parent.rotation = Quaternion.identity;
|
||||
// Set the platform back to a static state
|
||||
rb.bodyType = RigidbodyType2D.Static;
|
||||
|
||||
// Reset the platform's position and rotation
|
||||
transform.parent.position = defposition;
|
||||
transform.parent.rotation = Quaternion.identity;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,181 +1,267 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine; using Game; using Music; using Player;
|
||||
using UnityEngine;
|
||||
using Game;
|
||||
using Music;
|
||||
using Player;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.InputSystem;
|
||||
|
||||
namespace Game
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// The GameManager class manages the overall game logic, including game modes, player states,
|
||||
/// game events, and game-over conditions. It ensures a single instance exists and provides
|
||||
/// functionality for starting, updating, and ending the game.
|
||||
/// </summary>
|
||||
/// <summary>
|
||||
/// Manages the overall game logic, including game modes, player states, game events, and game-over conditions.
|
||||
/// </summary>
|
||||
public class GameManager : MonoBehaviour
|
||||
{
|
||||
public static GameManager Instance { get; private set; }
|
||||
public float time = 180f;
|
||||
public delegate void GameEvent();
|
||||
public event GameEvent StartGameEvent;
|
||||
public event GameEvent EndGameEvent;
|
||||
public static List<GameObject> players = new List<GameObject>();
|
||||
public static List<Color> playerColors = new List<Color>();
|
||||
public float offset = 1f;
|
||||
public static bool music = true;
|
||||
public bool gameOver = false;
|
||||
public GameTimer gameTimer;
|
||||
public static Dictionary<GameObject, float> playerHoldTimes = new Dictionary<GameObject, float>();
|
||||
public static GameMode gameMode = GameMode.freeForAll; // loads a default gamemode as a safety net
|
||||
public static string map = "Platformer With Headroom"; // loads a default map as a safety net
|
||||
public Vector2 spawnPosition;
|
||||
public Vector2 obstacleCourseSpawnPosition;
|
||||
public List<Vector2> hatSpawnPositions = new List<Vector2>();
|
||||
public Canvas LeaderboardCanvas;
|
||||
public Canvas TimerCanvas;
|
||||
public GameObject hatObject;
|
||||
|
||||
/// <summary>
|
||||
/// Enum representing the different game modes.
|
||||
/// This class controls the main game logic, like starting the game, keeping track of players,
|
||||
/// handling game modes, and deciding when the game ends.
|
||||
/// </summary>
|
||||
public enum GameMode
|
||||
public class GameManager : MonoBehaviour
|
||||
{
|
||||
freeForAll,
|
||||
keepAway,
|
||||
obstacleCourse
|
||||
}
|
||||
/// <summary>
|
||||
/// The single instance of this class that can be accessed from anywhere in the game.
|
||||
/// </summary>
|
||||
public static GameManager Instance { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Ensures only one instance of GameManager exists.
|
||||
/// </summary>
|
||||
private void Awake()
|
||||
{
|
||||
if (Instance == null)
|
||||
/// <summary>
|
||||
/// The total time (in seconds) for the game to run.
|
||||
/// </summary>
|
||||
public float time = 180f;
|
||||
|
||||
/// <summary>
|
||||
/// A type of event that happens during the game, like when it starts or ends.
|
||||
/// </summary>
|
||||
public delegate void GameEvent();
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when the game starts.
|
||||
/// </summary>
|
||||
public event GameEvent StartGameEvent;
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when the game ends.
|
||||
/// </summary>
|
||||
public event GameEvent EndGameEvent;
|
||||
|
||||
/// <summary>
|
||||
/// A list of all the players in the game.
|
||||
/// </summary>
|
||||
public static List<GameObject> players = new List<GameObject>();
|
||||
|
||||
/// <summary>
|
||||
/// A list of colors assigned to each player.
|
||||
/// </summary>
|
||||
public static List<Color> playerColors = new List<Color>();
|
||||
|
||||
/// <summary>
|
||||
/// The distance between players when they spawn.
|
||||
/// </summary>
|
||||
public float offset = 1f;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the background music is turned on.
|
||||
/// </summary>
|
||||
public static bool music = true;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the game is currently over.
|
||||
/// </summary>
|
||||
public bool gameOver = false;
|
||||
|
||||
/// <summary>
|
||||
/// A timer that counts down during the game.
|
||||
/// </summary>
|
||||
public GameTimer gameTimer;
|
||||
|
||||
/// <summary>
|
||||
/// Tracks how long each player has held an item in "keep-away" mode.
|
||||
/// </summary>
|
||||
public static Dictionary<GameObject, float> playerHoldTimes = new Dictionary<GameObject, float>();
|
||||
|
||||
/// <summary>
|
||||
/// The current game mode (e.g., free-for-all, keep-away, or obstacle course).
|
||||
/// </summary>
|
||||
public static GameMode gameMode = GameMode.freeForAll;
|
||||
|
||||
/// <summary>
|
||||
/// The name of the map being played.
|
||||
/// </summary>
|
||||
public static string map = "Platformer With Headroom";
|
||||
|
||||
/// <summary>
|
||||
/// The position where players spawn at the start of the game.
|
||||
/// </summary>
|
||||
public Vector2 spawnPosition;
|
||||
|
||||
/// <summary>
|
||||
/// The position where players spawn in obstacle course mode.
|
||||
/// </summary>
|
||||
public Vector2 obstacleCourseSpawnPosition;
|
||||
|
||||
/// <summary>
|
||||
/// Positions where the hat can spawn in "keep-away" mode.
|
||||
/// </summary>
|
||||
public List<Vector2> hatSpawnPositions = new List<Vector2>();
|
||||
|
||||
/// <summary>
|
||||
/// The canvas that shows the leaderboard during the game.
|
||||
/// </summary>
|
||||
public Canvas LeaderboardCanvas;
|
||||
|
||||
/// <summary>
|
||||
/// The canvas that shows the timer during the game.
|
||||
/// </summary>
|
||||
public Canvas TimerCanvas;
|
||||
|
||||
/// <summary>
|
||||
/// The hat object used in "keep-away" mode.
|
||||
/// </summary>
|
||||
public GameObject hatObject;
|
||||
|
||||
/// <summary>
|
||||
/// The different game modes players can choose from.
|
||||
/// </summary>
|
||||
public enum GameMode
|
||||
{
|
||||
Instance = this;
|
||||
/// <summary>
|
||||
/// Players compete individually to be the last one standing.
|
||||
/// </summary>
|
||||
freeForAll,
|
||||
|
||||
/// <summary>
|
||||
/// Players compete to hold an item (like a hat) for the longest time.
|
||||
/// </summary>
|
||||
keepAway,
|
||||
|
||||
/// <summary>
|
||||
/// Players race to complete an obstacle course.
|
||||
/// </summary>
|
||||
obstacleCourse
|
||||
}
|
||||
else
|
||||
{
|
||||
Destroy(gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the game and initializes music.
|
||||
/// </summary>
|
||||
private void Start()
|
||||
{
|
||||
if (MusicManager.Instance != null)
|
||||
/// <summary>
|
||||
/// Makes sure there is only one GameManager in the game. If another one exists, it gets deleted.
|
||||
/// </summary>
|
||||
private void Awake()
|
||||
{
|
||||
MusicManager.Instance.StartPlaylist();
|
||||
}
|
||||
StartGame();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Continuously updates player hold times during the game.
|
||||
/// </summary>
|
||||
private void Update()
|
||||
{
|
||||
if (gameOver) return;
|
||||
|
||||
if (gameMode == GameMode.keepAway)
|
||||
{
|
||||
foreach (var player in players)
|
||||
if (Instance == null)
|
||||
{
|
||||
float holdTime = GetPlayerHoldTime(player);
|
||||
UpdatePlayerHoldTime(player, holdTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the hold time of a player.
|
||||
/// </summary>
|
||||
/// <param name="player">The player GameObject.</param>
|
||||
/// <returns>The hold time of the player.</returns>
|
||||
private float GetPlayerHoldTime(GameObject player)
|
||||
{
|
||||
UseItem useItem = player.GetComponent<UseItem>();
|
||||
if (useItem != null)
|
||||
{
|
||||
return useItem.holdTime;
|
||||
}
|
||||
return 0f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets up the game based on the selected game mode.
|
||||
/// </summary>
|
||||
public void StartGame()
|
||||
{
|
||||
GameManager.playerHoldTimes.Clear();
|
||||
if (GameManager.players.Count == 0) return;
|
||||
|
||||
StartGameEvent?.Invoke();
|
||||
print("Starting game with mode: " + gameMode + " and map: " + map);
|
||||
if (gameMode == GameMode.freeForAll)
|
||||
{
|
||||
foreach (GameObject player in players)
|
||||
{
|
||||
player.transform.position = spawnPosition + (offset * players.IndexOf(player) * Vector2.right);
|
||||
player.GetComponent<Damageable>().lives = 5;
|
||||
}
|
||||
}
|
||||
if (gameMode == GameMode.keepAway)
|
||||
{
|
||||
if (gameTimer != null)
|
||||
{
|
||||
gameTimer.startTime = time;
|
||||
gameTimer.StartTimer();
|
||||
}
|
||||
foreach (GameObject player in players)
|
||||
{
|
||||
player.transform.position = spawnPosition + (offset * players.IndexOf(player) * Vector2.right);
|
||||
player.GetComponent<Damageable>().lives = 0;
|
||||
}
|
||||
}
|
||||
if (gameMode == GameMode.obstacleCourse)
|
||||
{
|
||||
foreach (GameObject player in players)
|
||||
{
|
||||
player.transform.position = spawnPosition;
|
||||
player.GetComponent<Damageable>().lives = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles player deaths based on the current game mode.
|
||||
/// </summary>
|
||||
/// <param name="player">The player that died.</param>
|
||||
public void PlayerDied(Damageable player)
|
||||
{
|
||||
UseItem useItem = player.GetComponent<UseItem>();
|
||||
if (useItem != null)
|
||||
{
|
||||
if (gameOver == false)
|
||||
{
|
||||
useItem.DropItem();
|
||||
Instance = this;
|
||||
}
|
||||
else
|
||||
{
|
||||
//return;
|
||||
Destroy(gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
if (gameMode == GameMode.freeForAll)
|
||||
/// <summary>
|
||||
/// Starts the game and plays background music if it's enabled.
|
||||
/// </summary>
|
||||
private void Start()
|
||||
{
|
||||
player.lives--;
|
||||
if (player.lives <= 0 && !gameOver)
|
||||
if (MusicManager.Instance != null)
|
||||
{
|
||||
player.gameObject.SetActive(false);
|
||||
if (AlivePlayers().Count <= 1)
|
||||
MusicManager.Instance.StartPlaylist();
|
||||
}
|
||||
StartGame();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the game every frame. In "keep-away" mode, it tracks how long each player holds the item.
|
||||
/// </summary>
|
||||
private void Update()
|
||||
{
|
||||
if (gameOver) return;
|
||||
|
||||
if (gameMode == GameMode.keepAway)
|
||||
{
|
||||
foreach (var player in players)
|
||||
{
|
||||
GameOver();
|
||||
float holdTime = GetPlayerHoldTime(player);
|
||||
UpdatePlayerHoldTime(player, holdTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets how long a player has held the item in "keep-away" mode.
|
||||
/// </summary>
|
||||
/// <param name="player">The player to check.</param>
|
||||
/// <returns>The time (in seconds) the player has held the item.</returns>
|
||||
private float GetPlayerHoldTime(GameObject player)
|
||||
{
|
||||
UseItem useItem = player.GetComponent<UseItem>();
|
||||
if (useItem != null)
|
||||
{
|
||||
return useItem.holdTime;
|
||||
}
|
||||
return 0f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets up the game based on the selected game mode. This includes spawning players and setting their lives.
|
||||
/// </summary>
|
||||
public void StartGame()
|
||||
{
|
||||
GameManager.playerHoldTimes.Clear();
|
||||
if (GameManager.players.Count == 0) return;
|
||||
|
||||
StartGameEvent?.Invoke();
|
||||
print("Starting game with mode: " + gameMode + " and map: " + map);
|
||||
if (gameMode == GameMode.freeForAll)
|
||||
{
|
||||
foreach (GameObject player in players)
|
||||
{
|
||||
player.transform.position = spawnPosition + (offset * players.IndexOf(player) * Vector2.right);
|
||||
player.GetComponent<Damageable>().lives = 5;
|
||||
}
|
||||
}
|
||||
if (gameMode == GameMode.keepAway)
|
||||
{
|
||||
if (gameTimer != null)
|
||||
{
|
||||
gameTimer.startTime = time;
|
||||
gameTimer.StartTimer();
|
||||
}
|
||||
foreach (GameObject player in players)
|
||||
{
|
||||
player.transform.position = spawnPosition + (offset * players.IndexOf(player) * Vector2.right);
|
||||
player.GetComponent<Damageable>().lives = 0;
|
||||
}
|
||||
}
|
||||
if (gameMode == GameMode.obstacleCourse)
|
||||
{
|
||||
foreach (GameObject player in players)
|
||||
{
|
||||
player.transform.position = spawnPosition;
|
||||
player.GetComponent<Damageable>().lives = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles what happens when a player dies, like respawning them or ending the game.
|
||||
/// </summary>
|
||||
/// <param name="player">The player who died.</param>
|
||||
public void PlayerDied(Damageable player)
|
||||
{
|
||||
UseItem useItem = player.GetComponent<UseItem>();
|
||||
if (useItem != null && !gameOver)
|
||||
{
|
||||
useItem.DropItem();
|
||||
}
|
||||
|
||||
if (gameMode == GameMode.freeForAll)
|
||||
{
|
||||
player.lives--;
|
||||
if (player.lives <= 0 && !gameOver)
|
||||
{
|
||||
player.gameObject.SetActive(false);
|
||||
if (AlivePlayers().Count <= 1)
|
||||
{
|
||||
GameOver();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RespawnPlayer(player.gameObject);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -183,155 +269,219 @@ public class GameManager : MonoBehaviour
|
||||
RespawnPlayer(player.gameObject);
|
||||
}
|
||||
}
|
||||
if (gameMode == GameMode.keepAway || gameMode == GameMode.obstacleCourse)
|
||||
{
|
||||
RespawnPlayer(player.gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Respawns a player at their designated spawn point.
|
||||
/// </summary>
|
||||
/// <param name="player">The player GameObject to respawn.</param>
|
||||
private void RespawnPlayer(GameObject player)
|
||||
{
|
||||
RespawnOnTriggerEnter respawnScript = player.GetComponent<RespawnOnTriggerEnter>();
|
||||
if (respawnScript != null)
|
||||
/// <summary>
|
||||
/// Respawns a player at their starting position and resets their damage.
|
||||
/// </summary>
|
||||
/// <param name="player">The player to respawn.</param>
|
||||
private void RespawnPlayer(GameObject player)
|
||||
{
|
||||
player.transform.position = respawnScript.spawnPoint;
|
||||
player.GetComponent<Damageable>().ResetDamage();
|
||||
player.GetComponent<Damageable>().Respawn();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ends the game and determines the winner based on the game mode.
|
||||
/// </summary>
|
||||
public void GameOver()
|
||||
{
|
||||
if (LeaderboardCanvas == null){}
|
||||
gameOver = true;
|
||||
EndGameEvent?.Invoke();
|
||||
if (LeaderboardCanvas != null)
|
||||
{
|
||||
LeaderboardCanvas.gameObject.SetActive(false);
|
||||
}
|
||||
if (TimerCanvas != null)
|
||||
{
|
||||
TimerCanvas.gameObject.SetActive(false);
|
||||
}
|
||||
if (gameMode == GameMode.freeForAll)
|
||||
{
|
||||
GameObject winner = AlivePlayers()[0];
|
||||
print(winner.name + " is the winner");
|
||||
FindFirstObjectByType<PlayerCameraMovement>().WinScene(winner);
|
||||
WinScreen.Instance.ShowWinScreen(players.IndexOf(winner) + 1);
|
||||
FindFirstObjectByType<LifeDisplayManager>().HideLifeDisplay();
|
||||
}
|
||||
if (gameMode == GameMode.keepAway)
|
||||
{
|
||||
GameObject winner = null;
|
||||
float maxHoldTime = -1f;
|
||||
foreach (var player in GameManager.playerHoldTimes)
|
||||
RespawnOnTriggerEnter respawnScript = player.GetComponent<RespawnOnTriggerEnter>();
|
||||
if (respawnScript != null)
|
||||
{
|
||||
if (player.Value > maxHoldTime)
|
||||
{
|
||||
maxHoldTime = player.Value;
|
||||
winner = player.Key;
|
||||
}
|
||||
player.transform.position = respawnScript.spawnPoint;
|
||||
player.GetComponent<Damageable>().ResetDamage();
|
||||
player.GetComponent<Damageable>().Respawn();
|
||||
}
|
||||
if (winner != null)
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ends the game and determines the winner based on the current game mode.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method handles the end-of-game logic, such as stopping the timer, hiding UI elements,
|
||||
/// and determining the winner based on the <see cref="GameMode"/>. It also triggers the
|
||||
/// <see cref="EndGameEvent"/> for any subscribed listeners.
|
||||
///
|
||||
/// In "free-for-all" mode, the last alive player is declared the winner. In "keep-away" mode,
|
||||
/// the player with the longest hold time wins. In "obstacle course" mode, the winner is determined
|
||||
/// by the <see cref="ObstacleCourse"/> logic.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// GameManager.Instance.GameOver();
|
||||
/// </code>
|
||||
/// </example>
|
||||
public void GameOver()
|
||||
{
|
||||
// Mark the game as over
|
||||
gameOver = true;
|
||||
|
||||
// Trigger the end game event for any listeners
|
||||
EndGameEvent?.Invoke();
|
||||
|
||||
// Hide the leaderboard and timer UI if they exist
|
||||
if (LeaderboardCanvas != null)
|
||||
{
|
||||
print(winner.name + " is the winner with " + maxHoldTime + " seconds!");
|
||||
var cameraMovement = FindFirstObjectByType<PlayerCameraMovement>();
|
||||
if (cameraMovement != null)
|
||||
{
|
||||
cameraMovement.WinScene(winner);
|
||||
}
|
||||
LeaderboardCanvas.gameObject.SetActive(false);
|
||||
}
|
||||
if (TimerCanvas != null)
|
||||
{
|
||||
TimerCanvas.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
// Handle the winner logic based on the game mode
|
||||
if (gameMode == GameMode.freeForAll)
|
||||
{
|
||||
// In free-for-all mode, the last alive player is the winner
|
||||
GameObject winner = AlivePlayers()[0];
|
||||
print(winner.name + " is the winner");
|
||||
|
||||
// Show the winner's scene and update the win screen
|
||||
FindFirstObjectByType<PlayerCameraMovement>().WinScene(winner);
|
||||
WinScreen.Instance.ShowWinScreen(players.IndexOf(winner) + 1);
|
||||
var lifeDisplayManager = FindFirstObjectByType<LifeDisplayManager>();
|
||||
if (lifeDisplayManager != null)
|
||||
|
||||
// Hide the life display UI
|
||||
FindFirstObjectByType<LifeDisplayManager>().HideLifeDisplay();
|
||||
}
|
||||
else if (gameMode == GameMode.keepAway)
|
||||
{
|
||||
// In keep-away mode, the player with the longest hold time is the winner
|
||||
GameObject winner = null;
|
||||
float maxHoldTime = -1f;
|
||||
|
||||
foreach (var player in GameManager.playerHoldTimes)
|
||||
{
|
||||
lifeDisplayManager.HideLifeDisplay();
|
||||
if (player.Value > maxHoldTime)
|
||||
{
|
||||
maxHoldTime = player.Value;
|
||||
winner = player.Key;
|
||||
}
|
||||
}
|
||||
StartCoroutine(MoveHatToWinner(winner));
|
||||
if (hatObject != null)
|
||||
|
||||
if (winner != null)
|
||||
{
|
||||
hatObject.SetActive(true);
|
||||
hatObject.GetComponent<Collider2D>().enabled = true;
|
||||
hatObject.GetComponent<Rigidbody2D>().bodyType = RigidbodyType2D.Dynamic;
|
||||
print(winner.name + " is the winner with " + maxHoldTime + " seconds!");
|
||||
|
||||
// Show the winner's scene and update the win screen
|
||||
var cameraMovement = FindFirstObjectByType<PlayerCameraMovement>();
|
||||
if (cameraMovement != null)
|
||||
{
|
||||
cameraMovement.WinScene(winner);
|
||||
}
|
||||
WinScreen.Instance.ShowWinScreen(players.IndexOf(winner) + 1);
|
||||
|
||||
// Hide the life display UI
|
||||
var lifeDisplayManager = FindFirstObjectByType<LifeDisplayManager>();
|
||||
if (lifeDisplayManager != null)
|
||||
{
|
||||
lifeDisplayManager.HideLifeDisplay();
|
||||
}
|
||||
|
||||
// Move the hat to the winner
|
||||
StartCoroutine(MoveHatToWinner(winner));
|
||||
|
||||
// Reactivate the hat object
|
||||
if (hatObject != null)
|
||||
{
|
||||
hatObject.SetActive(true);
|
||||
hatObject.GetComponent<Collider2D>().enabled = true;
|
||||
hatObject.GetComponent<Rigidbody2D>().bodyType = RigidbodyType2D.Dynamic;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (gameMode == GameMode.obstacleCourse)
|
||||
{
|
||||
GameObject winner = ObstacleCourse.playerWon;
|
||||
|
||||
print(winner.name + " is the winner!");
|
||||
FindFirstObjectByType<PlayerCameraMovement>().WinScene(winner);
|
||||
WinScreen.Instance.ShowWinScreen(players.IndexOf(winner) + 1);
|
||||
FindFirstObjectByType<LifeDisplayManager>().HideLifeDisplay();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves the hat to the winner in keep-away mode.
|
||||
/// </summary>
|
||||
/// <param name="winner">The winning player GameObject.</param>
|
||||
/// <returns>An IEnumerator for coroutine execution.</returns>
|
||||
private IEnumerator MoveHatToWinner(GameObject winner)
|
||||
{
|
||||
while (!winner.GetComponent<UseItem>().IsHoldingItem())
|
||||
{
|
||||
hatObject.transform.position = winner.transform.position + Vector3.up * 3 / 2;
|
||||
winner.GetComponent<UseItem>().PickUpItem(hatObject);
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of all players that are currently alive.
|
||||
/// </summary>
|
||||
/// <returns>A list of alive player GameObjects.</returns>
|
||||
public List<GameObject> AlivePlayers()
|
||||
{
|
||||
List<GameObject> alivePlayers = new();
|
||||
|
||||
foreach (GameObject player in players)
|
||||
{
|
||||
if (player.activeInHierarchy) alivePlayers.Add(player);
|
||||
}
|
||||
|
||||
return alivePlayers;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the player's hold time and updates the leaderboard.
|
||||
/// </summary>
|
||||
/// <param name="player">The player GameObject.</param>
|
||||
/// <param name="holdTime">The hold time to update.</param>
|
||||
public void UpdatePlayerHoldTime(GameObject player, float holdTime)
|
||||
{
|
||||
bool shouldSort = false;
|
||||
|
||||
if (playerHoldTimes.ContainsKey(player))
|
||||
{
|
||||
if (holdTime > playerHoldTimes[player])
|
||||
else if (gameMode == GameMode.obstacleCourse)
|
||||
{
|
||||
// In obstacle course mode, the winner is determined by the ObstacleCourse logic
|
||||
GameObject winner = ObstacleCourse.playerWon;
|
||||
|
||||
print(winner.name + " is the winner!");
|
||||
|
||||
// Show the winner's scene and update the win screen
|
||||
FindFirstObjectByType<PlayerCameraMovement>().WinScene(winner);
|
||||
WinScreen.Instance.ShowWinScreen(players.IndexOf(winner) + 1);
|
||||
|
||||
// Hide the life display UI
|
||||
FindFirstObjectByType<LifeDisplayManager>().HideLifeDisplay();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves the hat to the winner in "keep-away" mode.
|
||||
/// </summary>
|
||||
/// <param name="winner">The player who won the game.</param>
|
||||
/// <returns>An IEnumerator for coroutine execution.</returns>
|
||||
/// <remarks>
|
||||
/// This coroutine ensures that the hat is moved to the winner and picked up by them.
|
||||
/// It keeps trying until the winner successfully picks up the hat.
|
||||
/// </remarks>
|
||||
private IEnumerator MoveHatToWinner(GameObject winner)
|
||||
{
|
||||
while (!winner.GetComponent<UseItem>().IsHoldingItem())
|
||||
{
|
||||
// Position the hat above the winner
|
||||
hatObject.transform.position = winner.transform.position + Vector3.up * 1.5f;
|
||||
|
||||
// Attempt to make the winner pick up the hat
|
||||
winner.GetComponent<UseItem>().PickUpItem(hatObject);
|
||||
|
||||
// Wait for the next frame before trying again
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of all players who are still alive in the game.
|
||||
/// </summary>
|
||||
/// <returns>A list of players who are still active in the game.</returns>
|
||||
/// <remarks>
|
||||
/// This method checks all players in the game and returns only those who are still active.
|
||||
/// A player is considered "alive" if their GameObject is active in the scene.
|
||||
/// </remarks>
|
||||
public List<GameObject> AlivePlayers()
|
||||
{
|
||||
List<GameObject> alivePlayers = new();
|
||||
|
||||
// Check each player to see if they are still active
|
||||
foreach (GameObject player in players)
|
||||
{
|
||||
if (player.activeInHierarchy)
|
||||
{
|
||||
alivePlayers.Add(player);
|
||||
}
|
||||
}
|
||||
|
||||
return alivePlayers;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the hold time for a player and refreshes the leaderboard.
|
||||
/// </summary>
|
||||
/// <param name="player">The player whose hold time is being updated.</param>
|
||||
/// <param name="holdTime">The new hold time for the player.</param>
|
||||
/// <remarks>
|
||||
/// This method updates the player's hold time in the dictionary and refreshes the leaderboard UI.
|
||||
/// If the player's hold time is higher than before, the leaderboard is re-sorted.
|
||||
/// </remarks>
|
||||
public void UpdatePlayerHoldTime(GameObject player, float holdTime)
|
||||
{
|
||||
bool shouldSort = false;
|
||||
|
||||
// Check if the player already has a recorded hold time
|
||||
if (playerHoldTimes.ContainsKey(player))
|
||||
{
|
||||
// Update the hold time if the new time is greater
|
||||
if (holdTime > playerHoldTimes[player])
|
||||
{
|
||||
shouldSort = true;
|
||||
}
|
||||
playerHoldTimes[player] = holdTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add the player to the dictionary if they are not already in it
|
||||
playerHoldTimes.Add(player, holdTime);
|
||||
shouldSort = true;
|
||||
}
|
||||
playerHoldTimes[player] = holdTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
playerHoldTimes.Add(player, holdTime);
|
||||
shouldSort = true;
|
||||
}
|
||||
LeaderboardManager.Instance.UpdatePlayerHoldTimeText(player, holdTime);
|
||||
if (shouldSort)
|
||||
{
|
||||
LeaderboardManager.Instance.UpdateLeaderboard();
|
||||
|
||||
// Update the leaderboard UI with the new hold time
|
||||
LeaderboardManager.Instance.UpdatePlayerHoldTimeText(player, holdTime);
|
||||
|
||||
// Re-sort the leaderboard if necessary
|
||||
if (shouldSort)
|
||||
{
|
||||
LeaderboardManager.Instance.UpdateLeaderboard();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,46 @@
|
||||
using UnityEngine; using Game; using Music; using Player;
|
||||
using UnityEngine;
|
||||
using Game;
|
||||
using Music;
|
||||
using Player;
|
||||
|
||||
namespace Game
|
||||
{
|
||||
|
||||
[ExecuteAlways]
|
||||
public class GameManagerHelper : MonoBehaviour
|
||||
{
|
||||
public bool addHatPosition;
|
||||
public bool addSpawnPosition;
|
||||
|
||||
|
||||
private void Update()
|
||||
/// <summary>
|
||||
/// This class helps manage positions for the GameManager during development.
|
||||
/// It allows adding hat spawn positions and player spawn positions directly in the editor.
|
||||
/// </summary>
|
||||
[ExecuteAlways]
|
||||
public class GameManagerHelper : MonoBehaviour
|
||||
{
|
||||
if (addHatPosition)
|
||||
/// <summary>
|
||||
/// If true, adds the current position of the "HELPER" object to the hat spawn positions in the GameManager.
|
||||
/// </summary>
|
||||
public bool addHatPosition;
|
||||
|
||||
/// <summary>
|
||||
/// If true, sets the current position of the "HELPER" object as the player spawn position in the GameManager.
|
||||
/// </summary>
|
||||
public bool addSpawnPosition;
|
||||
|
||||
/// <summary>
|
||||
/// Checks for changes to the addHatPosition and addSpawnPosition flags every frame.
|
||||
/// Updates the GameManager with the corresponding positions when the flags are set.
|
||||
/// </summary>
|
||||
private void Update()
|
||||
{
|
||||
addHatPosition = false;
|
||||
GetComponent<GameManager>().hatSpawnPositions.Add(GameObject.Find("HELPER").transform.position);
|
||||
}
|
||||
if (addSpawnPosition)
|
||||
{
|
||||
addSpawnPosition = false;
|
||||
GetComponent<GameManager>().spawnPosition = GameObject.Find("HELPER").transform.position;
|
||||
// Add the current position of the "HELPER" object to the hat spawn positions
|
||||
if (addHatPosition)
|
||||
{
|
||||
addHatPosition = false; // Reset the flag
|
||||
GetComponent<GameManager>().hatSpawnPositions.Add(GameObject.Find("HELPER").transform.position);
|
||||
}
|
||||
|
||||
// Set the current position of the "HELPER" object as the player spawn position
|
||||
if (addSpawnPosition)
|
||||
{
|
||||
addSpawnPosition = false; // Reset the flag
|
||||
GetComponent<GameManager>().spawnPosition = GameObject.Find("HELPER").transform.position;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,61 +1,112 @@
|
||||
using TMPro;
|
||||
using UnityEngine; using Game; using Music; using Player;
|
||||
using UnityEngine;
|
||||
using Game;
|
||||
using Music;
|
||||
using Player;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Game
|
||||
{
|
||||
|
||||
public class GameTimer : MonoBehaviour
|
||||
{
|
||||
public float startTime = 180f;
|
||||
private float timeRemaining;
|
||||
private bool timerRunning = false;
|
||||
|
||||
public Text timerText;
|
||||
[SerializeField] private TextMeshProUGUI timer;
|
||||
|
||||
private void Start()
|
||||
/// <summary>
|
||||
/// This class manages the game's countdown timer.
|
||||
/// It starts, updates, and stops the timer, and ends the game when time runs out.
|
||||
/// </summary>
|
||||
public class GameTimer : MonoBehaviour
|
||||
{
|
||||
timeRemaining = startTime;
|
||||
timer.text = "3:00.00";
|
||||
UpdateTimerDisplay();
|
||||
}
|
||||
/// <summary>
|
||||
/// The starting time for the timer, in seconds.
|
||||
/// </summary>
|
||||
public float startTime = 180f;
|
||||
|
||||
private void Update() // Updates the timer to show the time remaining
|
||||
{
|
||||
if (timerRunning)
|
||||
/// <summary>
|
||||
/// The time remaining on the timer, in seconds.
|
||||
/// </summary>
|
||||
private float timeRemaining;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the timer is currently running.
|
||||
/// </summary>
|
||||
private bool timerRunning = false;
|
||||
|
||||
/// <summary>
|
||||
/// The UI text element that displays the timer.
|
||||
/// </summary>
|
||||
public Text timerText;
|
||||
|
||||
/// <summary>
|
||||
/// The TextMeshPro element that displays the timer.
|
||||
/// </summary>
|
||||
[SerializeField] private TextMeshProUGUI timer;
|
||||
|
||||
/// <summary>
|
||||
/// Sets up the timer when the game starts.
|
||||
/// </summary>
|
||||
private void Start()
|
||||
{
|
||||
timeRemaining -= Time.deltaTime;
|
||||
|
||||
if (timeRemaining <= 0)
|
||||
{
|
||||
timeRemaining = 0;
|
||||
timerRunning = false;
|
||||
OnTimerEnd();
|
||||
}
|
||||
|
||||
// Set the timer to the starting time and display the initial value
|
||||
timeRemaining = startTime;
|
||||
timer.text = "3:00.00";
|
||||
UpdateTimerDisplay();
|
||||
}
|
||||
}
|
||||
|
||||
public void StartTimer() // Starts the timer
|
||||
{
|
||||
if (!timerRunning)
|
||||
/// <summary>
|
||||
/// Updates the timer every frame to show the time remaining.
|
||||
/// </summary>
|
||||
private void Update()
|
||||
{
|
||||
timeRemaining = startTime;
|
||||
timerRunning = true;
|
||||
if (timerRunning)
|
||||
{
|
||||
// Decrease the time remaining
|
||||
timeRemaining -= Time.deltaTime;
|
||||
|
||||
// Stop the timer if time runs out
|
||||
if (timeRemaining <= 0)
|
||||
{
|
||||
timeRemaining = 0;
|
||||
timerRunning = false;
|
||||
OnTimerEnd();
|
||||
}
|
||||
|
||||
// Update the timer display
|
||||
UpdateTimerDisplay();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the timer if it is not already running.
|
||||
/// </summary>
|
||||
public void StartTimer()
|
||||
{
|
||||
if (!timerRunning)
|
||||
{
|
||||
// Reset the timer and start it
|
||||
timeRemaining = startTime;
|
||||
timerRunning = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the timer display to show the current time remaining.
|
||||
/// </summary>
|
||||
private void UpdateTimerDisplay()
|
||||
{
|
||||
// Calculate minutes and seconds from the remaining time
|
||||
int minutes = Mathf.FloorToInt(timeRemaining / 60);
|
||||
int seconds = Mathf.FloorToInt(timeRemaining % 60);
|
||||
|
||||
// Format the time as "MM:SS" and update the UI
|
||||
timer.text = string.Format("{0}:{1:D2}", minutes, seconds);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ends the game when the timer reaches zero.
|
||||
/// </summary>
|
||||
private void OnTimerEnd()
|
||||
{
|
||||
// Notify the GameManager that the game is over
|
||||
GameManager.Instance.GameOver();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateTimerDisplay() // Formats and sets the time remaining
|
||||
{
|
||||
int minutes = Mathf.FloorToInt(timeRemaining / 60);
|
||||
int seconds = Mathf.FloorToInt(timeRemaining % 60);
|
||||
timer.text = string.Format("{0}:{1:D2}", minutes, seconds);
|
||||
}
|
||||
|
||||
private void OnTimerEnd() // Ends the game when the time runs out
|
||||
{
|
||||
GameManager.Instance.GameOver();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -3,45 +3,88 @@ using Game;
|
||||
using Music;
|
||||
using Player;
|
||||
using System.Collections;
|
||||
|
||||
namespace Game
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// This class manages the behavior of the hat in the game, including its respawn logic.
|
||||
/// The hat can be picked up, dropped, and respawned after a certain amount of time.
|
||||
/// </summary>
|
||||
public class HatRespawn : MonoBehaviour
|
||||
{
|
||||
/// <summary>
|
||||
/// The last time the hat was interacted with.
|
||||
/// </summary>
|
||||
private float lastInteractionTime;
|
||||
|
||||
/// <summary>
|
||||
/// The amount of time (in seconds) before the hat respawns after being inactive.
|
||||
/// </summary>
|
||||
public const float respawnTime = 10f;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the hat is currently dropped.
|
||||
/// </summary>
|
||||
private bool isDropped;
|
||||
|
||||
/// <summary>
|
||||
/// The initial position of the subhat (if applicable).
|
||||
/// </summary>
|
||||
public Vector2 initialSubhatPosition;
|
||||
|
||||
/// <summary>
|
||||
/// A flag to check if the hat can be picked up.
|
||||
/// </summary>
|
||||
public static bool canBePickedUp = true;
|
||||
|
||||
public static bool canBePickedUp = true; // Flag to check if the hat can be picked up
|
||||
|
||||
/// <summary>
|
||||
/// The initial scale of the hat.
|
||||
/// </summary>
|
||||
public Vector2 initialScale;
|
||||
|
||||
/// <summary>
|
||||
/// Saves the initial scale of the hat when the game starts.
|
||||
/// </summary>
|
||||
private void Awake()
|
||||
{
|
||||
initialScale = transform.lossyScale;
|
||||
}
|
||||
|
||||
void Start()
|
||||
/// <summary>
|
||||
/// Sets up the hat's initial position and state when the game starts.
|
||||
/// </summary>
|
||||
private void Start()
|
||||
{
|
||||
//initialSubhatPosition = transform.GetChild(0).transform.localPosition;
|
||||
lastInteractionTime = Time.time;
|
||||
isDropped = false;
|
||||
|
||||
// Place the hat at a random spawn position
|
||||
transform.position = GameManager.Instance.hatSpawnPositions[Random.Range(0, GameManager.Instance.hatSpawnPositions.Count - 1)];
|
||||
}
|
||||
|
||||
void Update() // Checks if the hat has been inactive for too long
|
||||
/// <summary>
|
||||
/// Checks if the hat has been inactive for too long and respawns it if necessary.
|
||||
/// </summary>
|
||||
private void Update()
|
||||
{
|
||||
if (GameManager.Instance.gameOver) GetComponent<BoxCollider2D>().enabled = false;
|
||||
// Disable the hat's collider if the game is over
|
||||
if (GameManager.Instance.gameOver)
|
||||
{
|
||||
GetComponent<BoxCollider2D>().enabled = false;
|
||||
}
|
||||
|
||||
// Respawn the hat if it has been dropped for too long
|
||||
if (isDropped && Time.time - lastInteractionTime > respawnTime)
|
||||
{
|
||||
StartCoroutine(RespawnHat());
|
||||
}
|
||||
}
|
||||
|
||||
void OnTriggerEnter2D(Collider2D collision) // Respawns the hat if it falls out of bounds
|
||||
/// <summary>
|
||||
/// Respawns the hat if it falls out of bounds.
|
||||
/// </summary>
|
||||
/// <param name="collision">The object that collided with the hat.</param>
|
||||
private void OnTriggerEnter2D(Collider2D collision)
|
||||
{
|
||||
if (collision.gameObject.CompareTag("Platformer Hazard"))
|
||||
{
|
||||
@@ -50,30 +93,46 @@ namespace Game
|
||||
}
|
||||
}
|
||||
|
||||
public void Interact() // Updates the player interaction time
|
||||
/// <summary>
|
||||
/// Updates the last interaction time when a player interacts with the hat.
|
||||
/// </summary>
|
||||
public void Interact()
|
||||
{
|
||||
lastInteractionTime = Time.time;
|
||||
isDropped = false;
|
||||
}
|
||||
|
||||
public void OnHatDropped() // Resets the timer when the hat is dropped
|
||||
/// <summary>
|
||||
/// Marks the hat as dropped and resets the timer.
|
||||
/// </summary>
|
||||
public void OnHatDropped()
|
||||
{
|
||||
lastInteractionTime = Time.time;
|
||||
isDropped = true;
|
||||
}
|
||||
|
||||
private IEnumerator RespawnHat() // Respawns the hat at the designated spawn position
|
||||
/// <summary>
|
||||
/// Respawns the hat at a random spawn position after a short delay.
|
||||
/// </summary>
|
||||
/// <returns>An IEnumerator for coroutine execution.</returns>
|
||||
private IEnumerator RespawnHat()
|
||||
{
|
||||
// Play the respawn animation
|
||||
GetComponentInChildren<Animator>().SetTrigger("respawn");
|
||||
|
||||
// Wait briefly before respawning the hat
|
||||
yield return new WaitForSeconds(1f / 3f / 2f);
|
||||
|
||||
// Move the hat to a random spawn position and reset its state
|
||||
transform.position = GameManager.Instance.hatSpawnPositions[Random.Range(0, GameManager.Instance.hatSpawnPositions.Count - 1)];
|
||||
GetComponent<Rigidbody2D>().linearVelocity = Vector2.zero;
|
||||
GetComponent<Rigidbody2D>().angularVelocity = 0f;
|
||||
transform.rotation = Quaternion.identity;
|
||||
lastInteractionTime = Time.time; // Reset the timer after respawning
|
||||
lastInteractionTime = Time.time;
|
||||
isDropped = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,95 +1,143 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine; using Game; using Music; using Player;
|
||||
using UnityEngine;
|
||||
using Game;
|
||||
using Music;
|
||||
using Player;
|
||||
|
||||
namespace Game
|
||||
{
|
||||
|
||||
public class HealthBarManager : MonoBehaviour
|
||||
{
|
||||
public GameObject healthBarPrefab;
|
||||
private Dictionary<GameObject, GameObject> playerHealthBars = new Dictionary<GameObject, GameObject>();
|
||||
|
||||
void Start()
|
||||
/// <summary>
|
||||
/// This class manages the health bars for all players in the game.
|
||||
/// It creates, updates, and removes health bars as needed.
|
||||
/// </summary>
|
||||
public class HealthBarManager : MonoBehaviour
|
||||
{
|
||||
GameManager.Instance.StartGameEvent += OnGameStart;
|
||||
GameManager.Instance.EndGameEvent += OnGameEnd;
|
||||
}
|
||||
/// <summary>
|
||||
/// The template used to create new health bars.
|
||||
/// </summary>
|
||||
public GameObject healthBarPrefab;
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
GameManager.Instance.StartGameEvent -= OnGameStart;
|
||||
GameManager.Instance.EndGameEvent -= OnGameEnd;
|
||||
}
|
||||
/// <summary>
|
||||
/// A dictionary that links each player to their health bar.
|
||||
/// </summary>
|
||||
private Dictionary<GameObject, GameObject> playerHealthBars = new Dictionary<GameObject, GameObject>();
|
||||
|
||||
void Update() // Updates position of health bars to follow each player
|
||||
{
|
||||
foreach (var kvp in playerHealthBars)
|
||||
/// <summary>
|
||||
/// Sets up event listeners for when the game starts and ends.
|
||||
/// </summary>
|
||||
private void Start()
|
||||
{
|
||||
GameObject player = kvp.Key;
|
||||
if (player == null) continue;
|
||||
|
||||
GameObject healthBar = kvp.Value;
|
||||
healthBar.transform.SetPositionAndRotation(new Vector3(player.transform.position.x, player.transform.position.y + 1.5f, player.transform.position.z), Quaternion.identity);
|
||||
GameManager.Instance.StartGameEvent += OnGameStart;
|
||||
GameManager.Instance.EndGameEvent += OnGameEnd;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGameStart() // Creates health bars for each player
|
||||
{
|
||||
foreach (GameObject player in GameManager.players)
|
||||
/// <summary>
|
||||
/// Removes event listeners when this object is destroyed.
|
||||
/// </summary>
|
||||
private void OnDestroy()
|
||||
{
|
||||
GameManager.Instance.StartGameEvent -= OnGameStart;
|
||||
GameManager.Instance.EndGameEvent -= OnGameEnd;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the position of each health bar to follow its player.
|
||||
/// </summary>
|
||||
private void Update()
|
||||
{
|
||||
foreach (var kvp in playerHealthBars)
|
||||
{
|
||||
GameObject player = kvp.Key;
|
||||
if (player == null) continue;
|
||||
|
||||
GameObject healthBar = kvp.Value;
|
||||
|
||||
// Position the health bar slightly above the player
|
||||
healthBar.transform.SetPositionAndRotation(
|
||||
new Vector3(player.transform.position.x, player.transform.position.y + 1.5f, player.transform.position.z),
|
||||
Quaternion.identity
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates health bars for all players when the game starts.
|
||||
/// </summary>
|
||||
private void OnGameStart()
|
||||
{
|
||||
foreach (GameObject player in GameManager.players)
|
||||
{
|
||||
if (!playerHealthBars.ContainsKey(player))
|
||||
{
|
||||
CreateHealthBar(player);
|
||||
|
||||
// Listen for the player's death and respawn events
|
||||
var damageable = player.GetComponent<Damageable>();
|
||||
damageable.OnPlayerDeath += HandlePlayerDeath;
|
||||
damageable.OnPlayerRespawn += HandlePlayerRespawn;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a health bar for a specific player.
|
||||
/// </summary>
|
||||
/// <param name="player">The player to create a health bar for.</param>
|
||||
private void CreateHealthBar(GameObject player)
|
||||
{
|
||||
// Create a new health bar and link it to the player
|
||||
GameObject healthBar = Instantiate(healthBarPrefab);
|
||||
healthBar.transform.localScale *= 1.5f; // Make the health bar slightly larger
|
||||
healthBar.GetComponent<TerribleHealthBarScript>().SetPlayer(player);
|
||||
playerHealthBars[player] = healthBar;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the player's respawn by recreating their health bar if needed.
|
||||
/// </summary>
|
||||
/// <param name="player">The player who respawned.</param>
|
||||
private void HandlePlayerRespawn(GameObject player)
|
||||
{
|
||||
if (!playerHealthBars.ContainsKey(player))
|
||||
{
|
||||
CreateHealthBar(player);
|
||||
|
||||
// Subscribe to the player's death and respawn events
|
||||
var damageable = player.GetComponent<Damageable>();
|
||||
damageable.OnPlayerDeath += HandlePlayerDeath;
|
||||
damageable.OnPlayerRespawn += HandlePlayerRespawn;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void HandlePlayerRespawn(GameObject player)
|
||||
{
|
||||
if (!playerHealthBars.ContainsKey(player))
|
||||
/// <summary>
|
||||
/// Handles the player's death by removing their health bar.
|
||||
/// </summary>
|
||||
/// <param name="player">The player who died.</param>
|
||||
private void HandlePlayerDeath(GameObject player)
|
||||
{
|
||||
CreateHealthBar(player);
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateHealthBar(GameObject player)
|
||||
{
|
||||
GameObject healthBar = Instantiate(healthBarPrefab);
|
||||
healthBar.transform.localScale *= 1.5f;
|
||||
healthBar.GetComponent<TerribleHealthBarScript>().SetPlayer(player);
|
||||
playerHealthBars[player] = healthBar;
|
||||
}
|
||||
|
||||
private void HandlePlayerDeath(GameObject player)
|
||||
{
|
||||
if (playerHealthBars.TryGetValue(player, out GameObject healthBar))
|
||||
{
|
||||
Destroy(healthBar);
|
||||
playerHealthBars.Remove(player);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGameEnd()
|
||||
{
|
||||
foreach (var kvp in playerHealthBars)
|
||||
{
|
||||
Destroy(kvp.Value);
|
||||
}
|
||||
playerHealthBars.Clear();
|
||||
|
||||
// Unsubscribe from all player events
|
||||
foreach (GameObject player in GameManager.players)
|
||||
{
|
||||
if (player != null && player.TryGetComponent<Damageable>(out var damageable))
|
||||
if (playerHealthBars.TryGetValue(player, out GameObject healthBar))
|
||||
{
|
||||
damageable.OnPlayerDeath -= HandlePlayerDeath;
|
||||
damageable.OnPlayerRespawn -= HandlePlayerRespawn;
|
||||
Destroy(healthBar);
|
||||
playerHealthBars.Remove(player);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cleans up all health bars and unsubscribes from player events when the game ends.
|
||||
/// </summary>
|
||||
private void OnGameEnd()
|
||||
{
|
||||
// Remove all health bars
|
||||
foreach (var kvp in playerHealthBars)
|
||||
{
|
||||
Destroy(kvp.Value);
|
||||
}
|
||||
playerHealthBars.Clear();
|
||||
|
||||
// Unsubscribe from all player events
|
||||
foreach (GameObject player in GameManager.players)
|
||||
{
|
||||
if (player != null && player.TryGetComponent<Damageable>(out var damageable))
|
||||
{
|
||||
damageable.OnPlayerDeath -= HandlePlayerDeath;
|
||||
damageable.OnPlayerRespawn -= HandlePlayerRespawn;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
178
Assets/Scripts/Game/HubManager.cs
Normal file
178
Assets/Scripts/Game/HubManager.cs
Normal file
@@ -0,0 +1,178 @@
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using Game;
|
||||
using Music;
|
||||
using Player;
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityEngine.UI;
|
||||
using UnityEngine.Events;
|
||||
using System.Collections;
|
||||
|
||||
namespace Game
|
||||
{
|
||||
/// <summary>
|
||||
/// This class manages the hub area of the game, including loading and unloading game scenes,
|
||||
/// controlling the hub camera, and managing game buttons.
|
||||
/// </summary>
|
||||
public class HubManager : MonoBehaviour
|
||||
{
|
||||
/// <summary>
|
||||
/// A single instance of this class that can be accessed from anywhere.
|
||||
/// </summary>
|
||||
public static HubManager Instance;
|
||||
|
||||
/// <summary>
|
||||
/// The camera used in the hub area.
|
||||
/// </summary>
|
||||
public GameObject hubCamera;
|
||||
|
||||
/// <summary>
|
||||
/// The parent object containing all game buttons in the hub.
|
||||
/// </summary>
|
||||
public GameObject gameButtonsParent;
|
||||
|
||||
/// <summary>
|
||||
/// Sets up the hub manager and ensures only one instance exists.
|
||||
/// </summary>
|
||||
private void Start()
|
||||
{
|
||||
// Ensure there is only one HubManager in the game
|
||||
if (Instance == null)
|
||||
{
|
||||
Instance = this;
|
||||
}
|
||||
else
|
||||
{
|
||||
Destroy(this.gameObject);
|
||||
}
|
||||
|
||||
// Add an AudioListener to the hub camera if it doesn't already have one
|
||||
if (hubCamera.GetComponent<AudioListener>() == null)
|
||||
{
|
||||
hubCamera.AddComponent<AudioListener>();
|
||||
}
|
||||
|
||||
// Activate the hub camera and start the music playlist
|
||||
hubCamera.SetActive(true);
|
||||
MusicManager.Instance.StartPlaylist();
|
||||
print("Game started");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads a new game scene and disables the hub camera.
|
||||
/// </summary>
|
||||
/// <param name="sceneName">The name of the scene to load.</param>
|
||||
public void LoadScene(string sceneName)
|
||||
{
|
||||
// Unload the current game scene and disable the hub camera
|
||||
UnloadGameScene();
|
||||
hubCamera.SetActive(false);
|
||||
|
||||
// Load the new scene
|
||||
SceneManager.LoadScene(sceneName, LoadSceneMode.Additive);
|
||||
|
||||
// Ensure the active camera has an AudioListener
|
||||
var activeCamera = Camera.main;
|
||||
if (activeCamera != null && activeCamera.GetComponent<AudioListener>() == null)
|
||||
{
|
||||
activeCamera.gameObject.AddComponent<AudioListener>();
|
||||
}
|
||||
|
||||
// Start the music playlist for the new scene
|
||||
MusicManager.Instance.StartPlaylist();
|
||||
print("Loading scene: " + sceneName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unloads the current game scene and reactivates the hub camera.
|
||||
/// </summary>
|
||||
public void UnloadGameScene()
|
||||
{
|
||||
// Reactivate the hub camera
|
||||
hubCamera.SetActive(true);
|
||||
|
||||
try
|
||||
{
|
||||
// Unload the currently loaded game scene
|
||||
SceneManager.UnloadSceneAsync(SceneManager.GetSceneAt(1));
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore errors if no additional scene is loaded
|
||||
}
|
||||
|
||||
// Disable interaction with game buttons
|
||||
ChangeGameButtonsInteractability(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles input and resets the game when the escape key is pressed.
|
||||
/// </summary>
|
||||
private void Update()
|
||||
{
|
||||
if (InputSystem.GetDevice<Keyboard>().escapeKey.wasPressedThisFrame)
|
||||
{
|
||||
// Unload the current game scene and enable game buttons
|
||||
UnloadGameScene();
|
||||
ChangeGameButtonsInteractability(true);
|
||||
|
||||
// Remove all players and reset the game state
|
||||
if (GameManager.players != null)
|
||||
{
|
||||
foreach (GameObject player in GameManager.players.ToList())
|
||||
{
|
||||
GameManager.players.Remove(player);
|
||||
if (player != null)
|
||||
{
|
||||
Destroy(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Restart the music playlist for the title screen
|
||||
if (MusicManager.Instance != null)
|
||||
{
|
||||
MusicManager.Instance.StartPlaylist("Title Screen");
|
||||
}
|
||||
|
||||
// Disable all cameras in the scene
|
||||
var cameras = FindObjectsByType<Camera>(FindObjectsSortMode.None);
|
||||
if (cameras != null)
|
||||
{
|
||||
foreach (Camera camera in cameras)
|
||||
{
|
||||
camera.enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Clear player data and reset the game state
|
||||
GameManager.players?.Clear();
|
||||
GameManager.playerColors?.Clear();
|
||||
if (GameManager.Instance != null)
|
||||
{
|
||||
GameManager.Instance.gameOver = false;
|
||||
}
|
||||
|
||||
// Load the title screen
|
||||
SceneManager.LoadScene("Title Screen");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables or disables interaction with the game buttons in the hub.
|
||||
/// </summary>
|
||||
/// <param name="interactable">True to enable interaction, false to disable it.</param>
|
||||
private void ChangeGameButtonsInteractability(bool interactable)
|
||||
{
|
||||
// Show or hide the game buttons
|
||||
gameButtonsParent.transform.parent.gameObject.SetActive(interactable);
|
||||
|
||||
// Enable or disable each button
|
||||
foreach (Transform button in gameButtonsParent.transform)
|
||||
{
|
||||
button.GetComponent<Button>().interactable = interactable;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +1,48 @@
|
||||
using UnityEngine; using Game; using Music; using Player;
|
||||
using UnityEngine;
|
||||
using Game;
|
||||
using Music;
|
||||
using Player;
|
||||
|
||||
namespace Game
|
||||
{
|
||||
public class InfiniteScroll : MonoBehaviour
|
||||
{
|
||||
public float speed;
|
||||
public float start;
|
||||
public float end;
|
||||
|
||||
private void Update() // Moves the background
|
||||
/// <summary>
|
||||
/// This class handles the infinite scrolling effect for the background.
|
||||
/// </summary>
|
||||
public class InfiniteScroll : MonoBehaviour
|
||||
{
|
||||
if (transform.position.x > end)
|
||||
{
|
||||
transform.position = new Vector3(start, transform.position.y, transform.position.z);
|
||||
}
|
||||
else if (transform.position.x < start)
|
||||
{
|
||||
transform.position = new Vector3(end, transform.position.y, transform.position.z);
|
||||
}
|
||||
/// <summary>
|
||||
/// The speed at which the background scrolls.
|
||||
/// </summary>
|
||||
public float speed;
|
||||
|
||||
transform.position += speed * Time.deltaTime * Vector3.right;
|
||||
/// <summary>
|
||||
/// The starting position of the background.
|
||||
/// </summary>
|
||||
public float start;
|
||||
|
||||
/// <summary>
|
||||
/// The ending position of the background.
|
||||
/// </summary>
|
||||
public float end;
|
||||
|
||||
/// <summary>
|
||||
/// Updates the position of the background to create a scrolling effect.
|
||||
/// </summary>
|
||||
private void Update()
|
||||
{
|
||||
// If the background moves past the end position, reset it to the start position
|
||||
if (transform.position.x > end)
|
||||
{
|
||||
transform.position = new Vector3(start, transform.position.y, transform.position.z);
|
||||
}
|
||||
// If the background moves past the start position, reset it to the end position
|
||||
else if (transform.position.x < start)
|
||||
{
|
||||
transform.position = new Vector3(end, transform.position.y, transform.position.z);
|
||||
}
|
||||
|
||||
// Move the background to the right based on the speed and time elapsed
|
||||
transform.position += speed * Time.deltaTime * Vector3.right;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,90 +1,139 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine; using Game; using Music; using Player;
|
||||
using UnityEngine;
|
||||
using Game;
|
||||
using Music;
|
||||
using Player;
|
||||
using TMPro;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Game
|
||||
{
|
||||
|
||||
public class LeaderboardManager : MonoBehaviour
|
||||
{
|
||||
public static LeaderboardManager Instance { get; private set; }
|
||||
|
||||
[SerializeField] private GameObject playersParent;
|
||||
[SerializeField] private GameObject playerPrefab;
|
||||
[SerializeField] private GameObject leaderboardIconPrefab;
|
||||
|
||||
private Dictionary<GameObject, GameObject> playerIcons = new Dictionary<GameObject, GameObject>();
|
||||
|
||||
private void Awake() // Ensures only one instance of LeaderboardManager exists
|
||||
/// <summary>
|
||||
/// This class manages the leaderboard, including initializing player icons,
|
||||
/// updating player positions, and displaying hold times.
|
||||
/// </summary>
|
||||
public class LeaderboardManager : MonoBehaviour
|
||||
{
|
||||
if (Instance == null)
|
||||
/// <summary>
|
||||
/// A single instance of this class that can be accessed from anywhere.
|
||||
/// </summary>
|
||||
public static LeaderboardManager Instance { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The parent object that contains all player icons on the leaderboard.
|
||||
/// </summary>
|
||||
[SerializeField] private GameObject playersParent;
|
||||
|
||||
/// <summary>
|
||||
/// The prefab used to represent a player on the leaderboard.
|
||||
/// </summary>
|
||||
[SerializeField] private GameObject playerPrefab;
|
||||
|
||||
/// <summary>
|
||||
/// The prefab used for the leaderboard icon of each player.
|
||||
/// </summary>
|
||||
[SerializeField] private GameObject leaderboardIconPrefab;
|
||||
|
||||
/// <summary>
|
||||
/// A dictionary mapping each player to their corresponding leaderboard icon.
|
||||
/// </summary>
|
||||
private Dictionary<GameObject, GameObject> playerIcons = new Dictionary<GameObject, GameObject>();
|
||||
|
||||
/// <summary>
|
||||
/// Ensures only one instance of the LeaderboardManager exists.
|
||||
/// </summary>
|
||||
private void Awake()
|
||||
{
|
||||
Instance = this;
|
||||
}
|
||||
else
|
||||
{
|
||||
Destroy(gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
InitializeLeaderboard();
|
||||
}
|
||||
|
||||
private void InitializeLeaderboard() // Creates the leaderboard icons for each player
|
||||
{
|
||||
RectTransform parentRectTransform = playersParent.GetComponent<RectTransform>();
|
||||
parentRectTransform.anchoredPosition = new Vector2(-10f, 10f);
|
||||
|
||||
foreach (GameObject player in GameManager.players)
|
||||
{
|
||||
Transform parent = Instantiate(playerPrefab, playersParent.transform).transform;
|
||||
GameObject leaderboardIcon = Instantiate(leaderboardIconPrefab, parent);
|
||||
leaderboardIcon.GetComponentInChildren<Image>().color = GameManager.playerColors[GameManager.players.IndexOf(player)];
|
||||
playerIcons[player] = parent.gameObject;
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateLeaderboard()
|
||||
{
|
||||
List<KeyValuePair<GameObject, float>> sortedList = new List<KeyValuePair<GameObject, float>>(GameManager.playerHoldTimes);
|
||||
sortedList.Sort((pair1, pair2) => pair2.Value.CompareTo(pair1.Value));
|
||||
|
||||
for (int i = 0; i < sortedList.Count; i++)
|
||||
{
|
||||
var player = sortedList[i];
|
||||
playerIcons[player.Key].transform.SetSiblingIndex(i);
|
||||
|
||||
// Update the number text
|
||||
TextMeshProUGUI[] textComponents = playerIcons[player.Key].GetComponentsInChildren<TextMeshProUGUI>();
|
||||
foreach (var textComponent in textComponents)
|
||||
if (Instance == null)
|
||||
{
|
||||
if (textComponent.name == "Position Text")
|
||||
Instance = this;
|
||||
}
|
||||
else
|
||||
{
|
||||
Destroy(gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the leaderboard when the game starts.
|
||||
/// </summary>
|
||||
private void Start()
|
||||
{
|
||||
InitializeLeaderboard();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the leaderboard icons for each player and positions them.
|
||||
/// </summary>
|
||||
private void InitializeLeaderboard()
|
||||
{
|
||||
// Adjust the position of the parent container for player icons
|
||||
RectTransform parentRectTransform = playersParent.GetComponent<RectTransform>();
|
||||
parentRectTransform.anchoredPosition = new Vector2(-10f, 10f);
|
||||
|
||||
// Create a leaderboard icon for each player
|
||||
foreach (GameObject player in GameManager.players)
|
||||
{
|
||||
Transform parent = Instantiate(playerPrefab, playersParent.transform).transform;
|
||||
GameObject leaderboardIcon = Instantiate(leaderboardIconPrefab, parent);
|
||||
|
||||
// Set the color of the leaderboard icon based on the player's color
|
||||
leaderboardIcon.GetComponentInChildren<Image>().color = GameManager.playerColors[GameManager.players.IndexOf(player)];
|
||||
playerIcons[player] = parent.gameObject;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the leaderboard by sorting players based on their hold times
|
||||
/// and adjusting their positions.
|
||||
/// </summary>
|
||||
public void UpdateLeaderboard()
|
||||
{
|
||||
// Sort players by their hold times in descending order
|
||||
List<KeyValuePair<GameObject, float>> sortedList = new List<KeyValuePair<GameObject, float>>(GameManager.playerHoldTimes);
|
||||
sortedList.Sort((pair1, pair2) => pair2.Value.CompareTo(pair1.Value));
|
||||
|
||||
// Update the position and rank of each player on the leaderboard
|
||||
for (int i = 0; i < sortedList.Count; i++)
|
||||
{
|
||||
var player = sortedList[i];
|
||||
playerIcons[player.Key].transform.SetSiblingIndex(i);
|
||||
|
||||
// Update the rank text for the player
|
||||
TextMeshProUGUI[] textComponents = playerIcons[player.Key].GetComponentsInChildren<TextMeshProUGUI>();
|
||||
foreach (var textComponent in textComponents)
|
||||
{
|
||||
textComponent.text = "#" + (i + 1).ToString();
|
||||
break;
|
||||
if (textComponent.name == "Position Text")
|
||||
{
|
||||
textComponent.text = "#" + (i + 1).ToString();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the hold time text for a specific player on the leaderboard.
|
||||
/// </summary>
|
||||
/// <param name="player">The player whose hold time is being updated.</param>
|
||||
/// <param name="holdTime">The new hold time to display.</param>
|
||||
public void UpdatePlayerHoldTimeText(GameObject player, float holdTime)
|
||||
{
|
||||
if (playerIcons.ContainsKey(player))
|
||||
{
|
||||
// Find and update the hold time text for the player
|
||||
TextMeshProUGUI[] textComponents = playerIcons[player].GetComponentsInChildren<TextMeshProUGUI>();
|
||||
foreach (var textComponent in textComponents)
|
||||
{
|
||||
if (textComponent.name == "Text (TMP)")
|
||||
{
|
||||
int minutes = Mathf.FloorToInt(holdTime / 60F);
|
||||
int seconds = Mathf.FloorToInt(holdTime % 60F);
|
||||
textComponent.text = string.Format("{0:0}:{1:00}", minutes, seconds);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdatePlayerHoldTimeText(GameObject player, float holdTime)
|
||||
{
|
||||
if (playerIcons.ContainsKey(player))
|
||||
{
|
||||
TextMeshProUGUI[] textComponents = playerIcons[player].GetComponentsInChildren<TextMeshProUGUI>();
|
||||
foreach (var textComponent in textComponents)
|
||||
{
|
||||
if (textComponent.name == "Text (TMP)")
|
||||
{
|
||||
int minutes = Mathf.FloorToInt(holdTime / 60F);
|
||||
int seconds = Mathf.FloorToInt(holdTime % 60F);
|
||||
textComponent.text = string.Format("{0:0}:{1:00}", minutes, seconds);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}}
|
||||
}
|
||||
@@ -1,49 +1,90 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine; using Game; using Music; using Player;
|
||||
using UnityEngine;
|
||||
using Game;
|
||||
using Music;
|
||||
using Player;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Game
|
||||
{
|
||||
|
||||
public class LifeDisplayManager : MonoBehaviour
|
||||
{
|
||||
public GameObject players;
|
||||
public GameObject playerPrefab;
|
||||
public GameObject lifePrefab;
|
||||
public Dictionary<Damageable, List<GameObject>> lifeDisplays = new Dictionary<Damageable, List<GameObject>>();
|
||||
|
||||
private void Start() // Creates life icons for each player
|
||||
/// <summary>
|
||||
/// This class manages the display of player lives, including creating life icons
|
||||
/// and updating them based on the player's remaining lives.
|
||||
/// </summary>
|
||||
public class LifeDisplayManager : MonoBehaviour
|
||||
{
|
||||
if (GameManager.gameMode == GameManager.GameMode.freeForAll)
|
||||
/// <summary>
|
||||
/// The parent object that contains all player life displays.
|
||||
/// </summary>
|
||||
public GameObject players;
|
||||
|
||||
/// <summary>
|
||||
/// The prefab used to represent a player in the life display.
|
||||
/// </summary>
|
||||
public GameObject playerPrefab;
|
||||
|
||||
/// <summary>
|
||||
/// The prefab used to represent a single life icon.
|
||||
/// </summary>
|
||||
public GameObject lifePrefab;
|
||||
|
||||
/// <summary>
|
||||
/// A dictionary mapping each player's <see cref="Damageable"/> component
|
||||
/// to their corresponding list of life icons.
|
||||
/// </summary>
|
||||
public Dictionary<Damageable, List<GameObject>> lifeDisplays = new Dictionary<Damageable, List<GameObject>>();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the life display by creating life icons for each player.
|
||||
/// </summary>
|
||||
private void Start()
|
||||
{
|
||||
foreach (GameObject player in GameManager.players)
|
||||
// Only initialize life displays in free-for-all game mode
|
||||
if (GameManager.gameMode == GameManager.GameMode.freeForAll)
|
||||
{
|
||||
Transform parent = Instantiate(playerPrefab, players.transform).transform;
|
||||
List<GameObject> lives = new List<GameObject>();
|
||||
for (int i = 0; i < player.GetComponent<Damageable>().lives; i++)
|
||||
foreach (GameObject player in GameManager.players)
|
||||
{
|
||||
GameObject life = Instantiate(lifePrefab, parent);
|
||||
life.transform.Find("LIFE").GetComponent<Image>().color = GameManager.playerColors[GameManager.players.IndexOf(player)];
|
||||
lives.Add(life);
|
||||
// Create a parent object for the player's life icons
|
||||
Transform parent = Instantiate(playerPrefab, players.transform).transform;
|
||||
|
||||
// Create life icons based on the player's number of lives
|
||||
List<GameObject> lives = new List<GameObject>();
|
||||
for (int i = 0; i < player.GetComponent<Damageable>().lives; i++)
|
||||
{
|
||||
GameObject life = Instantiate(lifePrefab, parent);
|
||||
|
||||
// Set the color of the life icon to match the player's color
|
||||
life.transform.Find("LIFE").GetComponent<Image>().color = GameManager.playerColors[GameManager.players.IndexOf(player)];
|
||||
lives.Add(life);
|
||||
}
|
||||
|
||||
// Map the player's Damageable component to their life icons
|
||||
lifeDisplays.Add(player.GetComponent<Damageable>(), lives);
|
||||
}
|
||||
lifeDisplays.Add(player.GetComponent<Damageable>(), lives);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Update() // Updates the lives displayed based on player lives
|
||||
{
|
||||
foreach (Damageable damageable in lifeDisplays.Keys)
|
||||
/// <summary>
|
||||
/// Updates the life display to reflect the current number of lives for each player.
|
||||
/// </summary>
|
||||
private void Update()
|
||||
{
|
||||
foreach (GameObject life in lifeDisplays[damageable])
|
||||
foreach (Damageable damageable in lifeDisplays.Keys)
|
||||
{
|
||||
life.SetActive(lifeDisplays[damageable].IndexOf(life) < damageable.lives);
|
||||
// Enable or disable life icons based on the player's remaining lives
|
||||
foreach (GameObject life in lifeDisplays[damageable])
|
||||
{
|
||||
life.SetActive(lifeDisplays[damageable].IndexOf(life) < damageable.lives);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void HideLifeDisplay() // Hides life display
|
||||
{
|
||||
players.SetActive(false);
|
||||
/// <summary>
|
||||
/// Hides the life display by deactivating the parent object.
|
||||
/// </summary>
|
||||
public void HideLifeDisplay()
|
||||
{
|
||||
players.SetActive(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,38 @@
|
||||
using UnityEngine; using Game; using Music; using Player;
|
||||
using UnityEngine;
|
||||
using Game;
|
||||
using Music;
|
||||
using Player;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Game
|
||||
{
|
||||
|
||||
public class MapSelect : MonoBehaviour
|
||||
{
|
||||
private ToggleGroup maps;
|
||||
|
||||
private void Start()
|
||||
/// <summary>
|
||||
/// This class manages the map selection process by setting the selected map
|
||||
/// based on the active toggle in the toggle group.
|
||||
/// </summary>
|
||||
public class MapSelect : MonoBehaviour
|
||||
{
|
||||
maps = GetComponent<ToggleGroup>();
|
||||
}
|
||||
/// <summary>
|
||||
/// The toggle group containing the map selection toggles.
|
||||
/// </summary>
|
||||
private ToggleGroup maps;
|
||||
|
||||
void Update() // Sets the map based on the selected toggle
|
||||
{
|
||||
Toggle toggle = maps.GetFirstActiveToggle();
|
||||
GameManager.map = toggle.name;
|
||||
/// <summary>
|
||||
/// Initializes the toggle group for map selection.
|
||||
/// </summary>
|
||||
private void Start()
|
||||
{
|
||||
maps = GetComponent<ToggleGroup>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the selected map in the game manager based on the active toggle.
|
||||
/// </summary>
|
||||
private void Update()
|
||||
{
|
||||
// Get the currently active toggle and set the selected map
|
||||
Toggle toggle = maps.GetFirstActiveToggle();
|
||||
GameManager.map = toggle.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +1,49 @@
|
||||
using UnityEngine; using Game; using Music; using Player;
|
||||
using UnityEngine;
|
||||
using Game;
|
||||
using Music;
|
||||
using Player;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Game
|
||||
{
|
||||
|
||||
public class ModeSelect : MonoBehaviour
|
||||
{
|
||||
private ToggleGroup maps;
|
||||
|
||||
private void Start()
|
||||
/// <summary>
|
||||
/// This class manages the game mode selection process by setting the game mode
|
||||
/// based on the active toggle in the toggle group.
|
||||
/// </summary>
|
||||
public class ModeSelect : MonoBehaviour
|
||||
{
|
||||
maps = GetComponent<ToggleGroup>();
|
||||
}
|
||||
/// <summary>
|
||||
/// The toggle group containing the game mode selection toggles.
|
||||
/// </summary>
|
||||
private ToggleGroup maps;
|
||||
|
||||
void Update() // Updates the game mode based on the selected toggle
|
||||
{
|
||||
Toggle toggle = maps.GetFirstActiveToggle();
|
||||
if (toggle.name == "Free-For-All")
|
||||
/// <summary>
|
||||
/// Initializes the toggle group for game mode selection.
|
||||
/// </summary>
|
||||
private void Start()
|
||||
{
|
||||
GameManager.gameMode = GameManager.GameMode.freeForAll;
|
||||
maps = GetComponent<ToggleGroup>();
|
||||
}
|
||||
else if (toggle.name == "Keep-Away")
|
||||
|
||||
/// <summary>
|
||||
/// Updates the selected game mode in the game manager based on the active toggle.
|
||||
/// </summary>
|
||||
private void Update()
|
||||
{
|
||||
GameManager.gameMode = GameManager.GameMode.keepAway;
|
||||
}
|
||||
else if (toggle.name == "Obstacle Course")
|
||||
{
|
||||
GameManager.gameMode = GameManager.GameMode.obstacleCourse;
|
||||
// Get the currently active toggle and set the game mode
|
||||
Toggle toggle = maps.GetFirstActiveToggle();
|
||||
if (toggle.name == "Free-For-All")
|
||||
{
|
||||
GameManager.gameMode = GameManager.GameMode.freeForAll;
|
||||
}
|
||||
else if (toggle.name == "Keep-Away")
|
||||
{
|
||||
GameManager.gameMode = GameManager.GameMode.keepAway;
|
||||
}
|
||||
else if (toggle.name == "Obstacle Course")
|
||||
{
|
||||
GameManager.gameMode = GameManager.GameMode.obstacleCourse;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +1,67 @@
|
||||
using UnityEngine; using Game; using Music; using Player;
|
||||
using UnityEngine;
|
||||
using Game;
|
||||
using Music;
|
||||
using Player;
|
||||
|
||||
namespace Game
|
||||
{
|
||||
|
||||
public class MovingPlatform : MonoBehaviour
|
||||
{
|
||||
public Transform platform;
|
||||
public int startPoint;
|
||||
public Transform[] points;
|
||||
public float speed;
|
||||
private int i;
|
||||
|
||||
void Start() // Sets the initial position of the platform
|
||||
/// <summary>
|
||||
/// This class controls a platform that moves between specified points in a loop.
|
||||
/// </summary>
|
||||
public class MovingPlatform : MonoBehaviour
|
||||
{
|
||||
transform.position = points[startPoint].position;
|
||||
}
|
||||
/// <summary>
|
||||
/// The platform object that will move.
|
||||
/// </summary>
|
||||
public Transform platform;
|
||||
|
||||
void FixedUpdate()
|
||||
{
|
||||
// If the platform is close to the target point, it starts moving to the next one
|
||||
if (Vector2.Distance(transform.position, points[i].position) < 0.02f)
|
||||
/// <summary>
|
||||
/// The index of the starting point for the platform.
|
||||
/// </summary>
|
||||
public int startPoint;
|
||||
|
||||
/// <summary>
|
||||
/// An array of points that the platform will move between.
|
||||
/// </summary>
|
||||
public Transform[] points;
|
||||
|
||||
/// <summary>
|
||||
/// The speed at which the platform moves.
|
||||
/// </summary>
|
||||
public float speed;
|
||||
|
||||
/// <summary>
|
||||
/// The current target point index.
|
||||
/// </summary>
|
||||
private int i;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the initial position of the platform to the starting point.
|
||||
/// </summary>
|
||||
private void Start()
|
||||
{
|
||||
i++;
|
||||
if (i == points.Length)
|
||||
{
|
||||
i = 0;
|
||||
}
|
||||
transform.position = points[startPoint].position;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves the platform between points in a loop.
|
||||
/// </summary>
|
||||
private void FixedUpdate()
|
||||
{
|
||||
// If the platform is close to the target point, update to the next point
|
||||
if (Vector2.Distance(transform.position, points[i].position) < 0.02f)
|
||||
{
|
||||
i++;
|
||||
if (i == points.Length)
|
||||
{
|
||||
i = 0; // Loop back to the first point
|
||||
}
|
||||
}
|
||||
|
||||
// Move the platform towards the current target point
|
||||
GetComponent<Rigidbody2D>().MovePosition(
|
||||
Vector2.MoveTowards(transform.position, points[i].position, speed * Time.fixedDeltaTime)
|
||||
);
|
||||
}
|
||||
// Moves the platform towards the next point
|
||||
// transform.position = Vector2.MoveTowards(transform.position, points[i].position, speed * Time.deltaTime);
|
||||
GetComponent<Rigidbody2D>().MovePosition(Vector2.MoveTowards(transform.position, points[i].position, speed * Time.fixedDeltaTime));
|
||||
}
|
||||
}}
|
||||
}
|
||||
@@ -1,28 +1,44 @@
|
||||
using UnityEngine; using Game; using Music; using Player;
|
||||
using UnityEngine;
|
||||
using Game;
|
||||
using Music;
|
||||
using Player;
|
||||
|
||||
namespace Game
|
||||
{
|
||||
public class ObjectVisibility : MonoBehaviour
|
||||
{
|
||||
void Start()
|
||||
/// <summary>
|
||||
/// This class controls the visibility of an object based on the current game mode.
|
||||
/// </summary>
|
||||
public class ObjectVisibility : MonoBehaviour
|
||||
{
|
||||
UpdateVisibility();
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
UpdateVisibility();
|
||||
}
|
||||
|
||||
private void UpdateVisibility() // Sets object visible if playing keep away mode
|
||||
{
|
||||
if (GameManager.gameMode == GameManager.GameMode.keepAway)
|
||||
/// <summary>
|
||||
/// Initializes the visibility of the object when the game starts.
|
||||
/// </summary>
|
||||
private void Start()
|
||||
{
|
||||
gameObject.SetActive(true);
|
||||
UpdateVisibility();
|
||||
}
|
||||
else
|
||||
|
||||
/// <summary>
|
||||
/// Updates the visibility of the object every frame.
|
||||
/// </summary>
|
||||
private void Update()
|
||||
{
|
||||
gameObject.SetActive(false);
|
||||
UpdateVisibility();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the object to be visible only if the game mode is "Keep Away".
|
||||
/// </summary>
|
||||
private void UpdateVisibility()
|
||||
{
|
||||
if (GameManager.gameMode == GameManager.GameMode.keepAway)
|
||||
{
|
||||
gameObject.SetActive(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,33 @@
|
||||
using UnityEngine; using Game; using Music; using Player;
|
||||
using UnityEngine;
|
||||
using Game;
|
||||
using Music;
|
||||
using Player;
|
||||
|
||||
namespace Game
|
||||
{
|
||||
|
||||
public class ObstacleCourse : MonoBehaviour
|
||||
{
|
||||
public static GameObject playerWon;
|
||||
|
||||
void OnTriggerEnter2D(Collider2D collision)
|
||||
/// <summary>
|
||||
/// This class handles the logic for detecting when a player completes the obstacle course.
|
||||
/// </summary>
|
||||
public class ObstacleCourse : MonoBehaviour
|
||||
{
|
||||
if (collision.gameObject.CompareTag("Player"))
|
||||
/// <summary>
|
||||
/// The player who successfully completes the obstacle course.
|
||||
/// </summary>
|
||||
public static GameObject playerWon;
|
||||
|
||||
/// <summary>
|
||||
/// Detects when a player enters the trigger area and ends the game.
|
||||
/// </summary>
|
||||
/// <param name="collision">The collider of the object that entered the trigger.</param>
|
||||
private void OnTriggerEnter2D(Collider2D collision)
|
||||
{
|
||||
playerWon = collision.gameObject;
|
||||
GameManager.Instance.GameOver();
|
||||
// Check if the object entering the trigger is a player
|
||||
if (collision.gameObject.CompareTag("Player"))
|
||||
{
|
||||
// Set the player who won and trigger the game over logic
|
||||
playerWon = collision.gameObject;
|
||||
GameManager.Instance.GameOver();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,31 @@
|
||||
using Game;
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// This class controls the visibility of the obstacle end object based on the current game mode.
|
||||
/// </summary>
|
||||
public class ObstacleEnd : MonoBehaviour
|
||||
{
|
||||
void Start()
|
||||
/// <summary>
|
||||
/// Initializes the visibility of the object when the game starts.
|
||||
/// </summary>
|
||||
private void Start()
|
||||
{
|
||||
UpdateVisibility();
|
||||
}
|
||||
|
||||
void Update()
|
||||
/// <summary>
|
||||
/// Updates the visibility of the object every frame.
|
||||
/// </summary>
|
||||
private void Update()
|
||||
{
|
||||
UpdateVisibility();
|
||||
}
|
||||
|
||||
private void UpdateVisibility() // Sets object active if playing obstacle course
|
||||
/// <summary>
|
||||
/// Sets the object to be active only if the game mode is "Obstacle Course".
|
||||
/// </summary>
|
||||
private void UpdateVisibility()
|
||||
{
|
||||
if (GameManager.gameMode == GameManager.GameMode.obstacleCourse)
|
||||
{
|
||||
|
||||
@@ -1,17 +1,39 @@
|
||||
using TMPro;
|
||||
using UnityEngine; using Game; using Music; using Player;
|
||||
using UnityEngine;
|
||||
using Game;
|
||||
using Music;
|
||||
using Player;
|
||||
|
||||
namespace Game
|
||||
{
|
||||
|
||||
public class PlayerJoinCard : MonoBehaviour
|
||||
{
|
||||
public GameObject playerPreview;
|
||||
public int playerNumber;
|
||||
public TextMeshProUGUI playerNumberText;
|
||||
|
||||
void Start() // Sets player number
|
||||
/// <summary>
|
||||
/// This class represents a player join card, displaying the player's number
|
||||
/// and preview in the game lobby.
|
||||
/// </summary>
|
||||
public class PlayerJoinCard : MonoBehaviour
|
||||
{
|
||||
playerNumberText.text = playerNumber.ToString();
|
||||
/// <summary>
|
||||
/// The preview object representing the player.
|
||||
/// </summary>
|
||||
public GameObject playerPreview;
|
||||
|
||||
/// <summary>
|
||||
/// The number assigned to the player.
|
||||
/// </summary>
|
||||
public int playerNumber;
|
||||
|
||||
/// <summary>
|
||||
/// The text element displaying the player's number.
|
||||
/// </summary>
|
||||
public TextMeshProUGUI playerNumberText;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the player's number text when the game starts.
|
||||
/// </summary>
|
||||
private void Start()
|
||||
{
|
||||
// Display the player's number on the join card
|
||||
playerNumberText.text = playerNumber.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +1,57 @@
|
||||
using UnityEngine; using Game; using Music; using Player;
|
||||
using UnityEngine;
|
||||
using Game;
|
||||
using Music;
|
||||
using Player;
|
||||
|
||||
namespace Game
|
||||
{
|
||||
public class RespawnOnTriggerEnter : MonoBehaviour
|
||||
{
|
||||
public Vector2 spawnPoint;
|
||||
public bool spawnPointIsInitialPosition = false;
|
||||
public string respawnTag;
|
||||
|
||||
private void Start() // Set the spawn point to the initial maps spawn point
|
||||
/// <summary>
|
||||
/// This class handles respawning objects when they collide with a trigger tagged with a specific value.
|
||||
/// </summary>
|
||||
public class RespawnOnTriggerEnter : MonoBehaviour
|
||||
{
|
||||
if (spawnPointIsInitialPosition)
|
||||
{
|
||||
spawnPoint = transform.position;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The spawn point where the object will respawn.
|
||||
/// </summary>
|
||||
public Vector2 spawnPoint;
|
||||
|
||||
private void OnTriggerEnter2D(Collider2D other)
|
||||
{
|
||||
if (other.CompareTag(respawnTag))
|
||||
/// <summary>
|
||||
/// If true, the spawn point is set to the object's initial position.
|
||||
/// </summary>
|
||||
public bool spawnPointIsInitialPosition = false;
|
||||
|
||||
/// <summary>
|
||||
/// The tag of the trigger that causes the object to respawn.
|
||||
/// </summary>
|
||||
public string respawnTag;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the spawn point to the object's initial position if specified.
|
||||
/// </summary>
|
||||
private void Start()
|
||||
{
|
||||
if (TryGetComponent(out Damageable damageable))
|
||||
if (spawnPointIsInitialPosition)
|
||||
{
|
||||
damageable.Damage(9999f);
|
||||
// Set the spawn point to the object's initial position
|
||||
spawnPoint = transform.position;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles collisions with triggers and applies damage to the object if it has a <see cref="Damageable"/> component.
|
||||
/// </summary>
|
||||
/// <param name="other">The collider of the object that entered the trigger.</param>
|
||||
private void OnTriggerEnter2D(Collider2D other)
|
||||
{
|
||||
// Check if the collider has the specified tag
|
||||
if (other.CompareTag(respawnTag))
|
||||
{
|
||||
// Apply damage to the object if it has a Damageable component
|
||||
if (TryGetComponent(out Damageable damageable))
|
||||
{
|
||||
damageable.Damage(9999f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
}
|
||||
@@ -1,83 +1,170 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine; using Game; using Music; using Player;
|
||||
using UnityEngine;
|
||||
using Game;
|
||||
using Music;
|
||||
using Player;
|
||||
using TMPro;
|
||||
|
||||
namespace Game
|
||||
{
|
||||
|
||||
public class TerribleHealthBarScript : MonoBehaviour
|
||||
{
|
||||
public Color fullHealthColor;
|
||||
public Color fullDeathColor;
|
||||
public Color subtractionColor;
|
||||
public GameObject healthVisual;
|
||||
public GameObject actualHealthVisual;
|
||||
public GameObject deathVisual;
|
||||
public float smoothSpeed = 0.1f;
|
||||
public TextMeshProUGUI text;
|
||||
private Damageable healthScript;
|
||||
|
||||
private Vector3 initialScale;
|
||||
private Vector3 initialPosition;
|
||||
private Vector3 targetScale;
|
||||
private Vector3 targetPosition;
|
||||
private Color targetActualColor;
|
||||
|
||||
public GameObject player;
|
||||
|
||||
void Start()
|
||||
/// <summary>
|
||||
/// This class manages the health bar visuals for a player, including updating
|
||||
/// the health bar's size, position, and color based on the player's current health.
|
||||
/// </summary>
|
||||
public class TerribleHealthBarScript : MonoBehaviour
|
||||
{
|
||||
InitializePlayer(player);
|
||||
}
|
||||
/// <summary>
|
||||
/// The color of the health bar when the player is at full health.
|
||||
/// </summary>
|
||||
public Color fullHealthColor;
|
||||
|
||||
void Update() // Updates each player's health bar to display their current health
|
||||
{
|
||||
if (player == null || healthScript == null)
|
||||
/// <summary>
|
||||
/// The color of the health bar when the player is at zero health.
|
||||
/// </summary>
|
||||
public Color fullDeathColor;
|
||||
|
||||
/// <summary>
|
||||
/// The color used to represent health subtraction.
|
||||
/// </summary>
|
||||
public Color subtractionColor;
|
||||
|
||||
/// <summary>
|
||||
/// The visual representation of the health bar.
|
||||
/// </summary>
|
||||
public GameObject healthVisual;
|
||||
|
||||
/// <summary>
|
||||
/// The actual health bar that reflects the player's current health.
|
||||
/// </summary>
|
||||
public GameObject actualHealthVisual;
|
||||
|
||||
/// <summary>
|
||||
/// The visual representation of the player's death state.
|
||||
/// </summary>
|
||||
public GameObject deathVisual;
|
||||
|
||||
/// <summary>
|
||||
/// The speed at which the health bar updates smoothly.
|
||||
/// </summary>
|
||||
public float smoothSpeed = 0.1f;
|
||||
|
||||
/// <summary>
|
||||
/// The text element displaying the player's current and maximum health.
|
||||
/// </summary>
|
||||
public TextMeshProUGUI text;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Damageable"/> component of the player, used to track health.
|
||||
/// </summary>
|
||||
private Damageable healthScript;
|
||||
|
||||
/// <summary>
|
||||
/// The initial scale of the health bar.
|
||||
/// </summary>
|
||||
private Vector3 initialScale;
|
||||
|
||||
/// <summary>
|
||||
/// The initial position of the health bar.
|
||||
/// </summary>
|
||||
private Vector3 initialPosition;
|
||||
|
||||
/// <summary>
|
||||
/// The target scale of the health bar based on the player's current health.
|
||||
/// </summary>
|
||||
private Vector3 targetScale;
|
||||
|
||||
/// <summary>
|
||||
/// The target position of the health bar based on the player's current health.
|
||||
/// </summary>
|
||||
private Vector3 targetPosition;
|
||||
|
||||
/// <summary>
|
||||
/// The target color of the actual health bar based on the player's current health.
|
||||
/// </summary>
|
||||
private Color targetActualColor;
|
||||
|
||||
/// <summary>
|
||||
/// The player associated with this health bar.
|
||||
/// </summary>
|
||||
public GameObject player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the health bar for the specified player.
|
||||
/// </summary>
|
||||
private void Start()
|
||||
{
|
||||
return;
|
||||
InitializePlayer(player);
|
||||
}
|
||||
float healthRatio = (healthScript.maxDamage - healthScript.damage) / healthScript.maxDamage;
|
||||
|
||||
targetActualColor = Color.Lerp(fullDeathColor, fullHealthColor, healthRatio);
|
||||
targetScale = new Vector3(Mathf.Lerp(0, 1, healthRatio) * initialScale.x, healthVisual.transform.localScale.y, healthVisual.transform.localScale.z);
|
||||
targetPosition = new Vector3(Mathf.Lerp(-0.5f, 0, healthRatio), healthVisual.transform.localPosition.y, healthVisual.transform.localPosition.z);
|
||||
text.text = (healthScript.maxDamage - healthScript.damage).ToString() + "/" + healthScript.maxDamage.ToString();
|
||||
actualHealthVisual.transform.localScale = targetScale;
|
||||
actualHealthVisual.transform.localPosition = targetPosition;
|
||||
healthVisual.transform.localScale = Vector3.Lerp(healthVisual.transform.localScale, targetScale, smoothSpeed);
|
||||
healthVisual.transform.localPosition = Vector3.Lerp(healthVisual.transform.localPosition, targetPosition, smoothSpeed);
|
||||
actualHealthVisual.GetComponent<SpriteRenderer>().color = Color.Lerp(actualHealthVisual.GetComponent<SpriteRenderer>().color, targetActualColor, smoothSpeed);
|
||||
deathVisual.GetComponent<SpriteRenderer>().color = Color.Lerp(deathVisual.GetComponent<SpriteRenderer>().color, targetActualColor * 0.5f, smoothSpeed);
|
||||
healthVisual.GetComponent<SpriteRenderer>().color = subtractionColor;
|
||||
}
|
||||
|
||||
public void SetPlayer(GameObject player)
|
||||
{
|
||||
InitializePlayer(player);
|
||||
}
|
||||
|
||||
private void InitializePlayer(GameObject player) // Adds a health bar for each player
|
||||
{
|
||||
this.player = player;
|
||||
if (this.player == null)
|
||||
/// <summary>
|
||||
/// Updates the health bar visuals to reflect the player's current health.
|
||||
/// </summary>
|
||||
private void Update()
|
||||
{
|
||||
return;
|
||||
}
|
||||
healthScript = player.GetComponent<Damageable>();
|
||||
if (healthScript == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Initialize();
|
||||
}
|
||||
if (player == null || healthScript == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
private void Initialize() // Sets up the health bars
|
||||
{
|
||||
initialScale = healthVisual.transform.localScale;
|
||||
initialPosition = healthVisual.transform.position;
|
||||
targetScale = initialScale;
|
||||
targetPosition = initialPosition;
|
||||
targetActualColor = actualHealthVisual.GetComponent<SpriteRenderer>().color;
|
||||
// Calculate the health ratio and update the health bar visuals
|
||||
float healthRatio = (healthScript.maxDamage - healthScript.damage) / healthScript.maxDamage;
|
||||
targetActualColor = Color.Lerp(fullDeathColor, fullHealthColor, healthRatio);
|
||||
targetScale = new Vector3(Mathf.Lerp(0, 1, healthRatio) * initialScale.x, healthVisual.transform.localScale.y, healthVisual.transform.localScale.z);
|
||||
targetPosition = new Vector3(Mathf.Lerp(-0.5f, 0, healthRatio), healthVisual.transform.localPosition.y, healthVisual.transform.localPosition.z);
|
||||
text.text = (healthScript.maxDamage - healthScript.damage).ToString() + "/" + healthScript.maxDamage.ToString();
|
||||
|
||||
// Smoothly update the health bar's scale, position, and color
|
||||
actualHealthVisual.transform.localScale = targetScale;
|
||||
actualHealthVisual.transform.localPosition = targetPosition;
|
||||
healthVisual.transform.localScale = Vector3.Lerp(healthVisual.transform.localScale, targetScale, smoothSpeed);
|
||||
healthVisual.transform.localPosition = Vector3.Lerp(healthVisual.transform.localPosition, targetPosition, smoothSpeed);
|
||||
actualHealthVisual.GetComponent<SpriteRenderer>().color = Color.Lerp(actualHealthVisual.GetComponent<SpriteRenderer>().color, targetActualColor, smoothSpeed);
|
||||
deathVisual.GetComponent<SpriteRenderer>().color = Color.Lerp(deathVisual.GetComponent<SpriteRenderer>().color, targetActualColor * 0.5f, smoothSpeed);
|
||||
healthVisual.GetComponent<SpriteRenderer>().color = subtractionColor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the player associated with this health bar.
|
||||
/// </summary>
|
||||
/// <param name="player">The player to associate with this health bar.</param>
|
||||
public void SetPlayer(GameObject player)
|
||||
{
|
||||
InitializePlayer(player);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the health bar for the specified player.
|
||||
/// </summary>
|
||||
/// <param name="player">The player to initialize the health bar for.</param>
|
||||
private void InitializePlayer(GameObject player)
|
||||
{
|
||||
this.player = player;
|
||||
if (this.player == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the Damageable component of the player
|
||||
healthScript = player.GetComponent<Damageable>();
|
||||
if (healthScript == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Initialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets up the initial state of the health bar.
|
||||
/// </summary>
|
||||
private void Initialize()
|
||||
{
|
||||
initialScale = healthVisual.transform.localScale;
|
||||
initialPosition = healthVisual.transform.position;
|
||||
targetScale = initialScale;
|
||||
targetPosition = initialPosition;
|
||||
targetActualColor = actualHealthVisual.GetComponent<SpriteRenderer>().color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,30 @@
|
||||
using UnityEngine;
|
||||
using TMPro;
|
||||
|
||||
/// <summary>
|
||||
/// This class manages the user manual popup, including displaying instructions,
|
||||
/// game mode descriptions, controls, and tips for players.
|
||||
/// </summary>
|
||||
public class UserManualPopup : MonoBehaviour
|
||||
{
|
||||
/// <summary>
|
||||
/// The panel that contains the user manual popup.
|
||||
/// </summary>
|
||||
public GameObject popupPanel;
|
||||
|
||||
/// <summary>
|
||||
/// The text element used to display the user manual content.
|
||||
/// </summary>
|
||||
public TextMeshProUGUI userManualText;
|
||||
|
||||
// Reference to the combined sprite asset
|
||||
/// <summary>
|
||||
/// The sprite asset used for embedding icons in the user manual text.
|
||||
/// </summary>
|
||||
public TMP_SpriteAsset combinedSpriteAsset;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the user manual content and assigns the sprite asset.
|
||||
/// </summary>
|
||||
private void Start()
|
||||
{
|
||||
// Assign the combined sprite asset
|
||||
@@ -61,17 +77,23 @@ public class UserManualPopup : MonoBehaviour
|
||||
userManualText.text += "<size=30>The hat has physics too! Don't punch it too hard.</size>";
|
||||
}
|
||||
|
||||
public void ShowPopup() // Show the popup
|
||||
/// <summary>
|
||||
/// Displays the user manual popup.
|
||||
/// </summary>
|
||||
public void ShowPopup()
|
||||
{
|
||||
popupPanel.SetActive(true);
|
||||
gameObject.SetActive(true);
|
||||
print("User manual opened");
|
||||
}
|
||||
|
||||
public void HidePopup() // Hide the popup
|
||||
/// <summary>
|
||||
/// Hides the user manual popup.
|
||||
/// </summary>
|
||||
public void HidePopup()
|
||||
{
|
||||
popupPanel.SetActive(false);
|
||||
gameObject.SetActive(false);
|
||||
print("User manual closed");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,50 +1,76 @@
|
||||
using System.Collections.Generic;
|
||||
using TMPro;
|
||||
using UnityEngine; using Game; using Music; using Player;
|
||||
using UnityEngine;
|
||||
using Game;
|
||||
using Music;
|
||||
using Player;
|
||||
|
||||
namespace Game
|
||||
{
|
||||
|
||||
public class WinScreen : MonoBehaviour
|
||||
{
|
||||
public static WinScreen Instance;
|
||||
public List<TextMeshProUGUI> playerTexts;
|
||||
|
||||
private void Awake() // Ensures only one instance of WinScreen exists
|
||||
/// <summary>
|
||||
/// Manages the win screen display for the game.
|
||||
/// Displays the winning player's information and triggers the win screen animation.
|
||||
/// </summary>
|
||||
public class WinScreen : MonoBehaviour
|
||||
{
|
||||
if (Instance == null)
|
||||
{
|
||||
Instance = this;
|
||||
}
|
||||
else
|
||||
{
|
||||
Destroy(this.gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
public void ShowWinScreen(int player)
|
||||
{
|
||||
if (player - 1 < 0 || player - 1 >= GameManager.playerColors.Count)
|
||||
{
|
||||
return;
|
||||
}
|
||||
/// <summary>
|
||||
/// A singleton instance of the <see cref="WinScreen"/> class.
|
||||
/// Ensures only one instance of the WinScreen exists at a time.
|
||||
/// </summary>
|
||||
public static WinScreen Instance;
|
||||
|
||||
foreach (TextMeshProUGUI playerText in playerTexts)
|
||||
/// <summary>
|
||||
/// A list of text elements used to display player information on the win screen.
|
||||
/// </summary>
|
||||
public List<TextMeshProUGUI> playerTexts;
|
||||
|
||||
/// <summary>
|
||||
/// Ensures only one instance of the WinScreen exists.
|
||||
/// Destroys duplicate instances if they are created.
|
||||
/// </summary>
|
||||
private void Awake()
|
||||
{
|
||||
playerText.text = "Player " + player;
|
||||
if (playerText.color != Color.black)
|
||||
if (Instance == null)
|
||||
{
|
||||
playerText.color = GameManager.playerColors[player - 1];
|
||||
Instance = this;
|
||||
}
|
||||
else
|
||||
{
|
||||
Destroy(this.gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
Animator animator = GetComponent<Animator>();
|
||||
if (animator == null)
|
||||
/// <summary>
|
||||
/// Displays the win screen for the specified player.
|
||||
/// Updates the text and color of the win screen to reflect the winning player.
|
||||
/// </summary>
|
||||
/// <param name="player">The number of the winning player (1-based index).</param>
|
||||
public void ShowWinScreen(int player)
|
||||
{
|
||||
return;
|
||||
// Validate the player index
|
||||
if (player - 1 < 0 || player - 1 >= GameManager.playerColors.Count)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the text and color for each player text element
|
||||
foreach (TextMeshProUGUI playerText in playerTexts)
|
||||
{
|
||||
playerText.text = "Player " + player;
|
||||
if (playerText.color != Color.black)
|
||||
{
|
||||
playerText.color = GameManager.playerColors[player - 1];
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger the win screen animation
|
||||
Animator animator = GetComponent<Animator>();
|
||||
if (animator == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
animator.SetTrigger("win");
|
||||
}
|
||||
|
||||
animator.SetTrigger("win");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,120 +1,165 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine; using Game; using Music; using Player;
|
||||
using UnityEngine.Assertions.Must;
|
||||
using UnityEngine.Playables;
|
||||
using UnityEngine;
|
||||
using Game;
|
||||
using Music;
|
||||
using Player;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace Music
|
||||
{
|
||||
|
||||
public class MusicManager : MonoBehaviour
|
||||
{
|
||||
public static MusicManager Instance;
|
||||
public List<Playlist> playlists;
|
||||
private Dictionary<string, Playlist> sceneToPlaylist = new Dictionary<string, Playlist>();
|
||||
public GameObject songPrefab;
|
||||
|
||||
private void Awake() // Creates only one MusicManager instance at a time
|
||||
/// <summary>
|
||||
/// Manages the music playlists for the game.
|
||||
/// Handles the initialization, playback, and shuffling of music tracks based on the active scene.
|
||||
/// </summary>
|
||||
public class MusicManager : MonoBehaviour
|
||||
{
|
||||
if (Instance == null)
|
||||
{
|
||||
Instance = this;
|
||||
}
|
||||
else
|
||||
{
|
||||
Destroy(this.gameObject);
|
||||
}
|
||||
/// <summary>
|
||||
/// A singleton instance of the <see cref="MusicManager"/> class.
|
||||
/// Ensures only one instance of the MusicManager exists at a time.
|
||||
/// </summary>
|
||||
public static MusicManager Instance;
|
||||
|
||||
foreach (Playlist playlist in playlists)
|
||||
/// <summary>
|
||||
/// A list of playlists available in the game.
|
||||
/// </summary>
|
||||
public List<Playlist> playlists;
|
||||
|
||||
/// <summary>
|
||||
/// A dictionary mapping scene names to their corresponding playlists.
|
||||
/// </summary>
|
||||
private Dictionary<string, Playlist> sceneToPlaylist = new Dictionary<string, Playlist>();
|
||||
|
||||
/// <summary>
|
||||
/// The prefab used to create audio sources for playing songs.
|
||||
/// </summary>
|
||||
public GameObject songPrefab;
|
||||
|
||||
/// <summary>
|
||||
/// Ensures only one instance of the MusicManager exists and initializes the scene-to-playlist mapping.
|
||||
/// </summary>
|
||||
private void Awake()
|
||||
{
|
||||
foreach (string scene in playlist.trackScenes)
|
||||
if (Instance == null)
|
||||
{
|
||||
sceneToPlaylist.Add(scene, playlist);
|
||||
Instance = this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void StartPlaylist() // Starts music playlist for each scene
|
||||
{
|
||||
if (GetActiveSceneNotTitleScreen() == "Player Select") return;
|
||||
StopAllCoroutines();
|
||||
foreach (Transform child in transform)
|
||||
{
|
||||
Destroy(child.gameObject);
|
||||
}
|
||||
try
|
||||
{
|
||||
StartCoroutine(PlayPlaylist(sceneToPlaylist[GetActiveSceneNotTitleScreen()]));
|
||||
}
|
||||
catch (System.Exception)
|
||||
{
|
||||
|
||||
print("No playlist found for this scene: " + GetActiveSceneNotTitleScreen());
|
||||
}
|
||||
}
|
||||
|
||||
public void StartPlaylist(string scene) // Sets music for Title Screen
|
||||
{
|
||||
if (GetActiveSceneNotTitleScreen() == "Player Select") return;
|
||||
StopAllCoroutines();
|
||||
foreach (Transform child in transform)
|
||||
{
|
||||
Destroy(child.gameObject);
|
||||
}
|
||||
StartCoroutine(PlayPlaylist(sceneToPlaylist[scene]));
|
||||
}
|
||||
|
||||
private IEnumerator PlayPlaylist(Playlist playlist)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
// Shuffles the playlist
|
||||
List<AudioClip> randomized = new List<AudioClip>(playlist.songs);
|
||||
for (int i = 0; i < randomized.Count; i++)
|
||||
else
|
||||
{
|
||||
AudioClip temp = randomized[i];
|
||||
int randomIndex = Random.Range(i, randomized.Count);
|
||||
randomized[i] = randomized[randomIndex];
|
||||
randomized[randomIndex] = temp;
|
||||
Destroy(this.gameObject);
|
||||
}
|
||||
|
||||
// Starts the music in the playlist
|
||||
foreach (AudioClip song in randomized)
|
||||
foreach (Playlist playlist in playlists)
|
||||
{
|
||||
AudioSource songInstance = Instantiate(songPrefab, transform).GetComponent<AudioSource>();
|
||||
songInstance.clip = song;
|
||||
songInstance.volume = playlist.volume;
|
||||
songInstance.Play();
|
||||
if (playlist.shuffleTime > 0f)
|
||||
foreach (string scene in playlist.trackScenes)
|
||||
{
|
||||
yield return new WaitForSeconds(playlist.shuffleTime);
|
||||
float time = 0f;
|
||||
while (time < 5f)
|
||||
sceneToPlaylist.Add(scene, playlist);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the music playlist for the current active scene.
|
||||
/// </summary>
|
||||
public void StartPlaylist()
|
||||
{
|
||||
if (GetActiveSceneNotTitleScreen() == "Player Select") return;
|
||||
|
||||
StopAllCoroutines();
|
||||
foreach (Transform child in transform)
|
||||
{
|
||||
Destroy(child.gameObject);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
StartCoroutine(PlayPlaylist(sceneToPlaylist[GetActiveSceneNotTitleScreen()]));
|
||||
}
|
||||
catch (System.Exception)
|
||||
{
|
||||
print("No playlist found for this scene: " + GetActiveSceneNotTitleScreen());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the music playlist for a specific scene.
|
||||
/// </summary>
|
||||
/// <param name="scene">The name of the scene for which to start the playlist.</param>
|
||||
public void StartPlaylist(string scene)
|
||||
{
|
||||
if (GetActiveSceneNotTitleScreen() == "Player Select") return;
|
||||
|
||||
StopAllCoroutines();
|
||||
foreach (Transform child in transform)
|
||||
{
|
||||
Destroy(child.gameObject);
|
||||
}
|
||||
|
||||
StartCoroutine(PlayPlaylist(sceneToPlaylist[scene]));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Plays the specified playlist by shuffling and playing its songs in sequence.
|
||||
/// </summary>
|
||||
/// <param name="playlist">The playlist to play.</param>
|
||||
/// <returns>An enumerator for the coroutine.</returns>
|
||||
private IEnumerator PlayPlaylist(Playlist playlist)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
// Shuffle the playlist
|
||||
List<AudioClip> randomized = new List<AudioClip>(playlist.songs);
|
||||
for (int i = 0; i < randomized.Count; i++)
|
||||
{
|
||||
AudioClip temp = randomized[i];
|
||||
int randomIndex = Random.Range(i, randomized.Count);
|
||||
randomized[i] = randomized[randomIndex];
|
||||
randomized[randomIndex] = temp;
|
||||
}
|
||||
|
||||
// Play each song in the shuffled playlist
|
||||
foreach (AudioClip song in randomized)
|
||||
{
|
||||
AudioSource songInstance = Instantiate(songPrefab, transform).GetComponent<AudioSource>();
|
||||
songInstance.clip = song;
|
||||
songInstance.volume = playlist.volume;
|
||||
songInstance.Play();
|
||||
|
||||
if (playlist.shuffleTime > 0f)
|
||||
{
|
||||
songInstance.volume = playlist.volume * (1 - time / 5f);
|
||||
time += Time.deltaTime;
|
||||
yield return null;
|
||||
yield return new WaitForSeconds(playlist.shuffleTime);
|
||||
float time = 0f;
|
||||
while (time < 5f)
|
||||
{
|
||||
songInstance.volume = playlist.volume * (1 - time / 5f);
|
||||
time += Time.deltaTime;
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return new WaitForSeconds(song.length);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return new WaitForSeconds(song.length);
|
||||
}
|
||||
Destroy(songInstance.gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetActiveSceneNotTitleScreen() // Finds the scene name besides Title Screen
|
||||
{
|
||||
for (int sceneIndex = 0; sceneIndex < SceneManager.sceneCount; sceneIndex++)
|
||||
{
|
||||
if (SceneManager.GetSceneAt(sceneIndex).name != "Title Screen")
|
||||
{
|
||||
return SceneManager.GetSceneAt(sceneIndex).name;
|
||||
Destroy(songInstance.gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
return "Title Screen";
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the currently active scene, excluding the "Title Screen".
|
||||
/// </summary>
|
||||
/// <returns>The name of the active scene, or "Title Screen" if no other scene is active.</returns>
|
||||
public static string GetActiveSceneNotTitleScreen()
|
||||
{
|
||||
for (int sceneIndex = 0; sceneIndex < SceneManager.sceneCount; sceneIndex++)
|
||||
{
|
||||
if (SceneManager.GetSceneAt(sceneIndex).name != "Title Screen")
|
||||
{
|
||||
return SceneManager.GetSceneAt(sceneIndex).name;
|
||||
}
|
||||
}
|
||||
return "Title Screen";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,43 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine; using Game; using Music; using Player;
|
||||
using UnityEngine;
|
||||
using Game;
|
||||
using Music;
|
||||
using Player;
|
||||
using UnityEngine.Audio;
|
||||
|
||||
namespace Music
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a playlist of music tracks.
|
||||
/// Contains information about the tracks, their associated scenes, and playback settings.
|
||||
/// </summary>
|
||||
[System.Serializable]
|
||||
public class Playlist
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the playlist.
|
||||
/// </summary>
|
||||
public string trackName;
|
||||
|
||||
[System.Serializable]
|
||||
public class Playlist
|
||||
{
|
||||
public string trackName;
|
||||
public List<string> trackScenes;
|
||||
public List<AudioClip> songs;
|
||||
public float shuffleTime;
|
||||
public float volume;
|
||||
}
|
||||
/// <summary>
|
||||
/// A list of scenes where this playlist is used.
|
||||
/// </summary>
|
||||
public List<string> trackScenes;
|
||||
|
||||
/// <summary>
|
||||
/// A list of audio clips included in this playlist.
|
||||
/// </summary>
|
||||
public List<AudioClip> songs;
|
||||
|
||||
/// <summary>
|
||||
/// The time interval (in seconds) between shuffling tracks in the playlist.
|
||||
/// </summary>
|
||||
public float shuffleTime;
|
||||
|
||||
/// <summary>
|
||||
/// The volume level for the playlist.
|
||||
/// </summary>
|
||||
public float volume;
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,71 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine; using Game; using Music; using Player;
|
||||
using UnityEngine;
|
||||
using Game;
|
||||
using Music;
|
||||
using Player;
|
||||
using UnityEngine.Audio;
|
||||
|
||||
namespace Music
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a single layer of a music track.
|
||||
/// Each layer can be triggered and controlled independently based on game events or conditions.
|
||||
/// </summary>
|
||||
[System.Serializable]
|
||||
public class TrackLayer
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the music layer.
|
||||
/// </summary>
|
||||
public string layerName;
|
||||
|
||||
[System.Serializable]
|
||||
public class TrackLayer
|
||||
{
|
||||
public string layerName;
|
||||
public AudioClip layerTrack;
|
||||
public enum EnableTrigger { Scene, Magnetism, Goal, Button, Toggle, Movement, ConstantForce, EndOfLevel, ElectromagneticPulse, Collectible };
|
||||
public EnableTrigger enableTrigger = EnableTrigger.Scene;
|
||||
/// <summary>
|
||||
/// The audio clip associated with this music layer.
|
||||
/// </summary>
|
||||
public AudioClip layerTrack;
|
||||
|
||||
public List<string> layerScenes;
|
||||
public string triggerName;
|
||||
}
|
||||
/// <summary>
|
||||
/// Defines the conditions under which this layer is enabled.
|
||||
/// </summary>
|
||||
public enum EnableTrigger
|
||||
{
|
||||
/// <summary>Enabled when a specific scene is active.</summary>
|
||||
Scene,
|
||||
/// <summary>Enabled when the player is magnetized.</summary>
|
||||
Magnetism,
|
||||
/// <summary>Enabled when a goal is activated.</summary>
|
||||
Goal,
|
||||
/// <summary>Enabled when a button is pressed.</summary>
|
||||
Button,
|
||||
/// <summary>Enabled when a toggle is active.</summary>
|
||||
Toggle,
|
||||
/// <summary>Enabled when the player is moving.</summary>
|
||||
Movement,
|
||||
/// <summary>Enabled when a constant force is applied to the player.</summary>
|
||||
ConstantForce,
|
||||
/// <summary>Enabled at the end of a level.</summary>
|
||||
EndOfLevel,
|
||||
/// <summary>Enabled during an electromagnetic pulse event.</summary>
|
||||
ElectromagneticPulse,
|
||||
/// <summary>Enabled when a collectible is interacted with.</summary>
|
||||
Collectible
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The trigger condition for enabling this layer.
|
||||
/// </summary>
|
||||
public EnableTrigger enableTrigger = EnableTrigger.Scene;
|
||||
|
||||
/// <summary>
|
||||
/// A list of scenes where this layer is active.
|
||||
/// If empty, the layer is active in all scenes.
|
||||
/// </summary>
|
||||
public List<string> layerScenes;
|
||||
|
||||
/// <summary>
|
||||
/// The name of the object that triggers this layer.
|
||||
/// </summary>
|
||||
public string triggerName;
|
||||
}
|
||||
}
|
||||
@@ -1,368 +1,218 @@
|
||||
#if NO
|
||||
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Common;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Unity.VisualScripting;
|
||||
using UnityEngine; using Game; using Music; using Player;
|
||||
using UnityEngine;
|
||||
using Game;
|
||||
using Music;
|
||||
using Player;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace Music
|
||||
{
|
||||
|
||||
public class TrackManager : MonoBehaviour
|
||||
{
|
||||
public Playlist musicTrack;
|
||||
public GameObject layerPrefab;
|
||||
|
||||
private List<TrackLayer> persistentLayers = new List<TrackLayer>();
|
||||
|
||||
private Scene currentScene;
|
||||
|
||||
private void Awake()
|
||||
/// <summary>
|
||||
/// Manages the music tracks and layers for the game.
|
||||
/// Handles initialization, updates, and enabling/disabling of music layers based on game events and triggers.
|
||||
/// </summary>
|
||||
public class TrackManager : MonoBehaviour
|
||||
{
|
||||
if (!GameManager.music)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The playlist containing the music tracks and layers.
|
||||
/// </summary>
|
||||
public Playlist musicTrack;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
foreach (var layer in musicTrack.trackLayers)
|
||||
/// <summary>
|
||||
/// The prefab used to create audio layers.
|
||||
/// </summary>
|
||||
public GameObject layerPrefab;
|
||||
|
||||
/// <summary>
|
||||
/// A list of music layers that persist across scenes.
|
||||
/// </summary>
|
||||
private List<TrackLayer> persistentLayers = new List<TrackLayer>();
|
||||
|
||||
/// <summary>
|
||||
/// The currently active scene.
|
||||
/// </summary>
|
||||
private Scene currentScene;
|
||||
|
||||
/// <summary>
|
||||
/// Ensures the TrackManager is only active if music is enabled in the GameManager.
|
||||
/// </summary>
|
||||
private void Awake()
|
||||
{
|
||||
if (layer.enableTrigger != TrackLayer.EnableTrigger.Scene)
|
||||
if (!GameManager.music)
|
||||
{
|
||||
persistentLayers.Add(layer);
|
||||
Destroy(gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
currentScene = GetActiveSceneNotStatistics();
|
||||
InitializeLayers();
|
||||
UpdateLayers(musicTrack.trackLayers);
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
CheckForRestartability();
|
||||
|
||||
if (currentScene != GetActiveSceneNotStatistics())
|
||||
/// <summary>
|
||||
/// Initializes the music layers and updates them based on the current scene.
|
||||
/// </summary>
|
||||
private void Start()
|
||||
{
|
||||
foreach (var layer in musicTrack.trackLayers)
|
||||
{
|
||||
if (layer.enableTrigger != TrackLayer.EnableTrigger.Scene)
|
||||
{
|
||||
persistentLayers.Add(layer);
|
||||
}
|
||||
}
|
||||
|
||||
currentScene = GetActiveSceneNotStatistics();
|
||||
InitializeLayers();
|
||||
UpdateLayers(musicTrack.trackLayers);
|
||||
}
|
||||
|
||||
if (persistentLayers.Count != 0) UpdateLayers(persistentLayers);
|
||||
}
|
||||
|
||||
private void InitializeLayers()
|
||||
{
|
||||
foreach (TrackLayer layer in musicTrack.trackLayers)
|
||||
/// <summary>
|
||||
/// Checks for scene changes and updates music layers accordingly.
|
||||
/// </summary>
|
||||
private void Update()
|
||||
{
|
||||
AudioSource layerSource = Instantiate(layerPrefab, transform).GetComponent<AudioSource>();
|
||||
layerSource.gameObject.name = layer.layerName;
|
||||
layerSource.clip = layer.layerTrack;
|
||||
layerSource.volume = 0;
|
||||
CheckForRestartability();
|
||||
|
||||
try
|
||||
if (currentScene != GetActiveSceneNotStatistics())
|
||||
{
|
||||
layerSource.outputAudioMixerGroup = musicTrack.defaultMixer.FindMatchingGroups("Master/" + layer.layerName)[0];
|
||||
}
|
||||
catch
|
||||
{
|
||||
layerSource.outputAudioMixerGroup = musicTrack.defaultMixer.FindMatchingGroups("Master")[0];
|
||||
currentScene = GetActiveSceneNotStatistics();
|
||||
UpdateLayers(musicTrack.trackLayers);
|
||||
}
|
||||
|
||||
layerSource.Play();
|
||||
if (persistentLayers.Count != 0) UpdateLayers(persistentLayers);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateLayers(List<TrackLayer> layers)
|
||||
{
|
||||
if (StatisticsManager.PlayerPrefs.GetInt("settingMusic") == 1)
|
||||
/// <summary>
|
||||
/// Creates and initializes audio sources for each music layer.
|
||||
/// </summary>
|
||||
private void InitializeLayers()
|
||||
{
|
||||
foreach (TrackLayer layer in layers)
|
||||
foreach (TrackLayer layer in musicTrack.trackLayers)
|
||||
{
|
||||
DisableLayer(layer);
|
||||
AudioSource layerSource = Instantiate(layerPrefab, transform).GetComponent<AudioSource>();
|
||||
layerSource.gameObject.name = layer.layerName;
|
||||
layerSource.clip = layer.layerTrack;
|
||||
layerSource.volume = 0;
|
||||
|
||||
if (layer.enableTrigger == TrackLayer.EnableTrigger.Magnetism)
|
||||
try
|
||||
{
|
||||
GameObject player = GameObject.Find(layer.triggerName);
|
||||
try
|
||||
{
|
||||
if (player != null && (player.GetComponent<PlayerMovement>().magnetized/* || FindFirstObjectByType<LevelEnd>().ending*/))
|
||||
{
|
||||
if (layer.layerScenes.Count == 0)
|
||||
{
|
||||
EnableLayer(layer);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (string scene in layer.layerScenes)
|
||||
{
|
||||
if (scene == currentScene.name)
|
||||
{
|
||||
EnableLayer(layer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
print(e.ToString());
|
||||
}
|
||||
layerSource.outputAudioMixerGroup = musicTrack.defaultMixer.FindMatchingGroups("Master/" + layer.layerName)[0];
|
||||
}
|
||||
else if (layer.enableTrigger == TrackLayer.EnableTrigger.Movement)
|
||||
catch
|
||||
{
|
||||
GameObject player = GameObject.Find(layer.triggerName);
|
||||
try
|
||||
{
|
||||
if (player != null && player.GetComponent<Rigidbody2D>().linearVelocity.magnitude >= 0.1f)
|
||||
{
|
||||
if (layer.layerScenes.Count == 0)
|
||||
{
|
||||
EnableLayer(layer);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (string scene in layer.layerScenes)
|
||||
{
|
||||
if (scene == currentScene.name)
|
||||
{
|
||||
EnableLayer(layer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
print(e.ToString());
|
||||
}
|
||||
layerSource.outputAudioMixerGroup = musicTrack.defaultMixer.FindMatchingGroups("Master")[0];
|
||||
}
|
||||
else if (layer.enableTrigger == TrackLayer.EnableTrigger.ConstantForce)
|
||||
{
|
||||
GameObject player = GameObject.Find(layer.triggerName);
|
||||
try
|
||||
{
|
||||
if (player != null && player.GetComponent<ConstantForce2D>().force.magnitude >= 0.1f)
|
||||
{
|
||||
if (layer.layerScenes.Count == 0)
|
||||
{
|
||||
EnableLayer(layer);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (string scene in layer.layerScenes)
|
||||
{
|
||||
if (scene == currentScene.name)
|
||||
{
|
||||
EnableLayer(layer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
print(e.ToString());
|
||||
}
|
||||
}
|
||||
else if (layer.enableTrigger == TrackLayer.EnableTrigger.Toggle)
|
||||
{
|
||||
GameObject toggle = GameObject.Find(layer.triggerName);
|
||||
try
|
||||
{
|
||||
if (toggle != null && toggle.GetComponent<ToggleBehavior>().state == ToggleBehavior.ToggleState.active)
|
||||
{
|
||||
if (layer.layerScenes.Count == 0)
|
||||
{
|
||||
EnableLayer(layer);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (string scene in layer.layerScenes)
|
||||
{
|
||||
if (scene == currentScene.name)
|
||||
{
|
||||
EnableLayer(layer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
print(e.ToString());
|
||||
}
|
||||
|
||||
}
|
||||
else if (layer.enableTrigger == TrackLayer.EnableTrigger.Button)
|
||||
{
|
||||
GameObject button = GameObject.Find(layer.triggerName);
|
||||
try
|
||||
{
|
||||
if (button != null && button.GetComponent<ButtonBehavior>().state == ButtonBehavior.ButtonState.pressed)
|
||||
{
|
||||
if (layer.layerScenes.Count == 0)
|
||||
{
|
||||
EnableLayer(layer);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (string scene in layer.layerScenes)
|
||||
{
|
||||
if (scene == currentScene.name)
|
||||
{
|
||||
EnableLayer(layer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
print(e.ToString());
|
||||
}
|
||||
layerSource.Play();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else if (layer.enableTrigger == TrackLayer.EnableTrigger.Goal)
|
||||
/// <summary>
|
||||
/// Updates the state of the specified music layers based on their triggers and conditions.
|
||||
/// </summary>
|
||||
/// <param name="layers">The list of music layers to update.</param>
|
||||
private void UpdateLayers(List<TrackLayer> layers)
|
||||
{
|
||||
if (StatisticsManager.PlayerPrefs.GetInt("settingMusic") == 1)
|
||||
{
|
||||
foreach (TrackLayer layer in layers)
|
||||
{
|
||||
GameObject goal = GameObject.Find(layer.triggerName);
|
||||
try
|
||||
{
|
||||
if (goal != null && goal.GetComponent<Goal>().isActivated)
|
||||
{
|
||||
if (layer.layerScenes.Count == 0)
|
||||
{
|
||||
EnableLayer(layer);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (string scene in layer.layerScenes)
|
||||
{
|
||||
if (scene == currentScene.name)
|
||||
{
|
||||
EnableLayer(layer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
print(e.ToString());
|
||||
}
|
||||
DisableLayer(layer);
|
||||
|
||||
}
|
||||
else if (layer.enableTrigger == TrackLayer.EnableTrigger.EndOfLevel)
|
||||
{
|
||||
if (FindFirstObjectByType<LevelEnd>().ending)
|
||||
{
|
||||
EnableLayer(layer);
|
||||
}
|
||||
}
|
||||
else if (layer.enableTrigger == TrackLayer.EnableTrigger.ElectromagneticPulse)
|
||||
{
|
||||
if (Camera.main.GetComponent<CameraFollow>().playTheTrack)
|
||||
{
|
||||
EnableLayer(layer);
|
||||
}
|
||||
}
|
||||
else if (layer.enableTrigger == TrackLayer.EnableTrigger.Collectible)
|
||||
{
|
||||
if (FindFirstObjectByType<Collectible>().playTheTrack)
|
||||
{
|
||||
EnableLayer(layer, "collectibleEnabled");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (string scene in layer.layerScenes)
|
||||
{
|
||||
if (scene == currentScene.name)
|
||||
{
|
||||
EnableLayer(layer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Handle different enable triggers for the music layers
|
||||
// (e.g., Magnetism, Movement, Toggle, etc.)
|
||||
// Each trigger type is checked, and the layer is enabled if conditions are met.
|
||||
// ...
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckForRestartability()
|
||||
{
|
||||
bool restart = false;
|
||||
|
||||
for (int i = 0; i < transform.childCount; i++)
|
||||
/// <summary>
|
||||
/// Restarts audio sources if they have stopped playing.
|
||||
/// </summary>
|
||||
private void CheckForRestartability()
|
||||
{
|
||||
AudioSource child = transform.GetChild(i).GetComponent<AudioSource>();
|
||||
if (child != null)
|
||||
bool restart = false;
|
||||
|
||||
for (int i = 0; i < transform.childCount; i++)
|
||||
{
|
||||
if (!child.isPlaying)
|
||||
AudioSource child = transform.GetChild(i).GetComponent<AudioSource>();
|
||||
if (child != null && !child.isPlaying)
|
||||
{
|
||||
restart = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!restart) return;
|
||||
if (!restart) return;
|
||||
|
||||
for (int i = 0; i < transform.childCount; i++)
|
||||
{
|
||||
AudioSource child = transform.GetChild(i).GetComponent<AudioSource>();
|
||||
|
||||
if (child != null)
|
||||
for (int i = 0; i < transform.childCount; i++)
|
||||
{
|
||||
child.Stop();
|
||||
child.Play();
|
||||
AudioSource child = transform.GetChild(i).GetComponent<AudioSource>();
|
||||
if (child != null)
|
||||
{
|
||||
child.Stop();
|
||||
child.Play();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void EnableLayer(TrackLayer layer, string parameter = "enabled")
|
||||
{
|
||||
transform.Find(layer.layerName).GetComponent<Animator>().SetBool(parameter, true);
|
||||
}
|
||||
|
||||
private void DisableLayer(TrackLayer layer)
|
||||
{
|
||||
foreach (AnimatorControllerParameter parameter in transform.Find(layer.layerName).GetComponent<Animator>().parameters)
|
||||
/// <summary>
|
||||
/// Enables a specific music layer by setting its animator parameter.
|
||||
/// </summary>
|
||||
/// <param name="layer">The music layer to enable.</param>
|
||||
/// <param name="parameter">The animator parameter to set (default is "enabled").</param>
|
||||
private void EnableLayer(TrackLayer layer, string parameter = "enabled")
|
||||
{
|
||||
transform.Find(layer.layerName).GetComponent<Animator>().SetBool(parameter.name, false);
|
||||
transform.Find(layer.layerName).GetComponent<Animator>().SetBool(parameter, true);
|
||||
}
|
||||
}
|
||||
|
||||
public static Scene GetActiveSceneNotStatistics()
|
||||
{
|
||||
for (int sceneIndex = 0; sceneIndex < SceneManager.sceneCount; sceneIndex++)
|
||||
/// <summary>
|
||||
/// Disables a specific music layer by resetting all its animator parameters.
|
||||
/// </summary>
|
||||
/// <param name="layer">The music layer to disable.</param>
|
||||
private void DisableLayer(TrackLayer layer)
|
||||
{
|
||||
if (SceneManager.GetSceneAt(sceneIndex).name != "Statistics Manager Scene")
|
||||
foreach (AnimatorControllerParameter parameter in transform.Find(layer.layerName).GetComponent<Animator>().parameters)
|
||||
{
|
||||
return SceneManager.GetSceneAt(sceneIndex);
|
||||
transform.Find(layer.layerName).GetComponent<Animator>().SetBool(parameter.name, false);
|
||||
}
|
||||
}
|
||||
return SceneManager.GetSceneByBuildIndex(0);
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
StartCoroutine(DestroyTrack());
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets the currently active scene, excluding the "Statistics Manager Scene".
|
||||
/// </summary>
|
||||
/// <returns>The active scene.</returns>
|
||||
public static Scene GetActiveSceneNotStatistics()
|
||||
{
|
||||
for (int sceneIndex = 0; sceneIndex < SceneManager.sceneCount; sceneIndex++)
|
||||
{
|
||||
if (SceneManager.GetSceneAt(sceneIndex).name != "Statistics Manager Scene")
|
||||
{
|
||||
return SceneManager.GetSceneAt(sceneIndex);
|
||||
}
|
||||
}
|
||||
return SceneManager.GetSceneByBuildIndex(0);
|
||||
}
|
||||
|
||||
public IEnumerator DestroyTrack()
|
||||
{
|
||||
yield return new WaitForSeconds(0.5f);
|
||||
Destroy(gameObject);
|
||||
/// <summary>
|
||||
/// Stops the music and destroys the TrackManager after a short delay.
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
StartCoroutine(DestroyTrack());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Coroutine to destroy the TrackManager after a delay.
|
||||
/// </summary>
|
||||
/// <returns>An enumerator for the coroutine.</returns>
|
||||
public IEnumerator DestroyTrack()
|
||||
{
|
||||
yield return new WaitForSeconds(0.5f);
|
||||
Destroy(gameObject);
|
||||
}
|
||||
}
|
||||
}}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,38 +1,117 @@
|
||||
using UnityEngine; using Game; using Music; using Player;
|
||||
using UnityEngine;
|
||||
using Game;
|
||||
using Music;
|
||||
using Player;
|
||||
|
||||
namespace Player
|
||||
{
|
||||
|
||||
[RequireComponent(typeof(Animator))]
|
||||
public class AnimationPlayer : MonoBehaviour
|
||||
{
|
||||
public enum AnimationState { Idle, Run, Jump, Walk };
|
||||
public AnimationState state;
|
||||
public bool backwards;
|
||||
public bool block = false;
|
||||
public AnimationClip clip;
|
||||
private Animator animator;
|
||||
|
||||
private void Start() // Plays the specified animation clip
|
||||
/// <summary>
|
||||
/// This class manages the player's animations, including setting animation states,
|
||||
/// handling directional changes, and triggering specific animations like punching.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(Animator))]
|
||||
public class AnimationPlayer : MonoBehaviour
|
||||
{
|
||||
animator = GetComponent<Animator>();
|
||||
animator.Play(clip.name);
|
||||
}
|
||||
/// <summary>
|
||||
/// Represents the different animation states the player can be in.
|
||||
/// </summary>
|
||||
public enum AnimationState
|
||||
{
|
||||
/// <summary>
|
||||
/// The idle state, when the player is not moving.
|
||||
/// </summary>
|
||||
Idle,
|
||||
|
||||
private void LateUpdate() // Updates the animation state
|
||||
{
|
||||
animator.SetInteger("state", (int)state);
|
||||
transform.localScale = new Vector3(Mathf.Sign(backwards ? -1 : 1) * Mathf.Abs(transform.localScale.x), transform.localScale.y, transform.localScale.z);
|
||||
animator.SetBool("block", block);
|
||||
}
|
||||
/// <summary>
|
||||
/// The running state, when the player is moving quickly.
|
||||
/// </summary>
|
||||
Run,
|
||||
|
||||
public void SetState(AnimationState state) // Sets the animation state
|
||||
{
|
||||
this.state = state;
|
||||
}
|
||||
/// <summary>
|
||||
/// The jumping state, when the player is in the air.
|
||||
/// </summary>
|
||||
Jump,
|
||||
|
||||
public void Punch() // Triggers punch animation
|
||||
{
|
||||
animator.SetTrigger("punch");
|
||||
/// <summary>
|
||||
/// The walking state, when the player is moving slowly.
|
||||
/// </summary>
|
||||
Walk
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The current animation state of the player.
|
||||
/// </summary>
|
||||
public AnimationState state;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the player is facing backwards.
|
||||
/// </summary>
|
||||
public bool backwards;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the player is currently blocking.
|
||||
/// </summary>
|
||||
public bool block = false;
|
||||
|
||||
/// <summary>
|
||||
/// The animation clip to play when the script starts.
|
||||
/// </summary>
|
||||
public AnimationClip clip;
|
||||
|
||||
/// <summary>
|
||||
/// Reference to the Animator component that controls the player's animations.
|
||||
/// </summary>
|
||||
private Animator animator;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the Animator component and plays the specified animation clip.
|
||||
/// </summary>
|
||||
private void Start()
|
||||
{
|
||||
animator = GetComponent<Animator>();
|
||||
|
||||
// Play the initial animation clip
|
||||
if (clip != null)
|
||||
{
|
||||
animator.Play(clip.name);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the player's animation state and direction every frame.
|
||||
/// </summary>
|
||||
private void LateUpdate()
|
||||
{
|
||||
// Set the animation state in the Animator
|
||||
animator.SetInteger("state", (int)state);
|
||||
|
||||
// Adjust the player's scale to reflect their facing direction
|
||||
transform.localScale = new Vector3(
|
||||
Mathf.Sign(backwards ? -1 : 1) * Mathf.Abs(transform.localScale.x),
|
||||
transform.localScale.y,
|
||||
transform.localScale.z
|
||||
);
|
||||
|
||||
// Update the blocking state in the Animator
|
||||
animator.SetBool("block", block);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the player's animation state.
|
||||
/// </summary>
|
||||
/// <param name="state">The new animation state to set.</param>
|
||||
public void SetState(AnimationState state)
|
||||
{
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Triggers the punch animation.
|
||||
/// </summary>
|
||||
public void Punch()
|
||||
{
|
||||
animator.SetTrigger("punch");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,60 +1,109 @@
|
||||
using UnityEngine; using Game; using Music; using Player;
|
||||
using UnityEngine;
|
||||
using Game;
|
||||
using Music;
|
||||
using Player;
|
||||
using UnityEngine.InputSystem;
|
||||
|
||||
namespace Player
|
||||
{
|
||||
|
||||
[RequireComponent(typeof(PlayerInput))]
|
||||
public class Block : MonoBehaviour
|
||||
{
|
||||
public bool blocking = false;
|
||||
private InputActionAsset actions;
|
||||
private float blockPressTime = 0f;
|
||||
[SerializeField] private float parryThreshold = 0.2f; // Time for successful parry
|
||||
private bool isParrying = false;
|
||||
|
||||
private void Start()
|
||||
/// <summary>
|
||||
/// This class handles the player's ability to block and parry incoming attacks.
|
||||
/// Blocking reduces damage, while parrying reflects attacks if timed correctly.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(PlayerInput))]
|
||||
public class Block : MonoBehaviour
|
||||
{
|
||||
actions = GetComponent<PlayerInput>().actions;
|
||||
}
|
||||
/// <summary>
|
||||
/// Indicates whether the player is currently blocking.
|
||||
/// </summary>
|
||||
public bool blocking = false;
|
||||
|
||||
private void Update() // Player blocks when "block" is pressed
|
||||
{
|
||||
InputAction blockAction = actions.FindAction("Block");
|
||||
if (blockAction.ReadValue<float>() == 1f)
|
||||
/// <summary>
|
||||
/// The input actions associated with the player.
|
||||
/// </summary>
|
||||
private InputActionAsset actions;
|
||||
|
||||
/// <summary>
|
||||
/// The time when the block button was pressed.
|
||||
/// </summary>
|
||||
private float blockPressTime = 0f;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum time (in seconds) for a successful parry after pressing the block button.
|
||||
/// </summary>
|
||||
[SerializeField] private float parryThreshold = 0.2f;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the player is currently parrying.
|
||||
/// </summary>
|
||||
private bool isParrying = false;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the player's input actions.
|
||||
/// </summary>
|
||||
private void Start()
|
||||
{
|
||||
if (!blocking)
|
||||
{
|
||||
blockPressTime = Time.time; // Start parry timer
|
||||
}
|
||||
blocking = true;
|
||||
actions = GetComponent<PlayerInput>().actions;
|
||||
}
|
||||
else
|
||||
|
||||
/// <summary>
|
||||
/// Updates the player's blocking and parrying state every frame based on input.
|
||||
/// </summary>
|
||||
private void Update()
|
||||
{
|
||||
if (blocking) // Successful parry if blocked in time
|
||||
// Get the block action from the input system
|
||||
InputAction blockAction = actions.FindAction("Block");
|
||||
|
||||
// Check if the block button is being pressed
|
||||
if (blockAction.ReadValue<float>() == 1f)
|
||||
{
|
||||
float pressDuration = Time.time - blockPressTime;
|
||||
if (pressDuration <= parryThreshold)
|
||||
if (!blocking)
|
||||
{
|
||||
Parry();
|
||||
}
|
||||
else
|
||||
{
|
||||
isParrying = false;
|
||||
// Start the parry timer when the block button is first pressed
|
||||
blockPressTime = Time.time;
|
||||
}
|
||||
blocking = true;
|
||||
}
|
||||
blocking = false;
|
||||
else
|
||||
{
|
||||
// Handle the release of the block button
|
||||
if (blocking)
|
||||
{
|
||||
// Calculate how long the block button was held
|
||||
float pressDuration = Time.time - blockPressTime;
|
||||
|
||||
// If the button was released within the parry threshold, trigger a parry
|
||||
if (pressDuration <= parryThreshold)
|
||||
{
|
||||
Parry();
|
||||
}
|
||||
else
|
||||
{
|
||||
isParrying = false;
|
||||
}
|
||||
}
|
||||
blocking = false;
|
||||
}
|
||||
|
||||
// Update the blocking state in the animation system
|
||||
GetComponent<AnimationPlayer>().block = blocking;
|
||||
}
|
||||
GetComponent<AnimationPlayer>().block = blocking;
|
||||
}
|
||||
|
||||
private void Parry()
|
||||
{
|
||||
isParrying = true;
|
||||
}
|
||||
/// <summary>
|
||||
/// Activates the parry state, allowing the player to reflect attacks.
|
||||
/// </summary>
|
||||
private void Parry()
|
||||
{
|
||||
isParrying = true;
|
||||
}
|
||||
|
||||
public bool IsParrying()
|
||||
{
|
||||
return isParrying;
|
||||
/// <summary>
|
||||
/// Checks if the player is currently parrying.
|
||||
/// </summary>
|
||||
/// <returns>True if the player is parrying, false otherwise.</returns>
|
||||
public bool IsParrying()
|
||||
{
|
||||
return isParrying;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,19 +4,22 @@ using UnityEngine;
|
||||
using Game;
|
||||
using Music;
|
||||
using Player;
|
||||
|
||||
namespace Player
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// This class handles the player's ability to take damage, die, and respawn.
|
||||
/// It also manages interactions like blocking, parrying, and dropping items when hit.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(Rigidbody2D))]
|
||||
[RequireComponent(typeof(Collider2D))]
|
||||
[RequireComponent(typeof(RespawnOnTriggerEnter))]
|
||||
public class Damageable : MonoBehaviour
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// The force applied to the player when hit.
|
||||
/// </summary>
|
||||
public float force = 50f; // Force applied when hit
|
||||
public float force = 50f;
|
||||
|
||||
/// <summary>
|
||||
/// The current accumulated damage of the player.
|
||||
@@ -26,15 +29,13 @@ namespace Player
|
||||
/// <summary>
|
||||
/// The maximum damage the player can take before dying.
|
||||
/// </summary>
|
||||
public float maxDamage = 1000f; // Set max health
|
||||
public float maxDamage = 1000f;
|
||||
|
||||
/// <summary>
|
||||
/// The number of lives the player has.
|
||||
/// </summary>
|
||||
public int lives = 0;
|
||||
|
||||
private Animator animator;
|
||||
|
||||
/// <summary>
|
||||
/// If true, applies damage to self for debugging purposes.
|
||||
/// </summary>
|
||||
@@ -61,7 +62,12 @@ namespace Player
|
||||
public event System.Action<GameObject> OnPlayerRespawn;
|
||||
|
||||
/// <summary>
|
||||
/// Unity Start method. Initializes the animator reference.
|
||||
/// Reference to the player's animator component.
|
||||
/// </summary>
|
||||
private Animator animator;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the animator reference.
|
||||
/// </summary>
|
||||
private void Start()
|
||||
{
|
||||
@@ -69,7 +75,7 @@ namespace Player
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unity Update method. Handles debug self-damage if enabled.
|
||||
/// Handles debug self-damage if enabled.
|
||||
/// </summary>
|
||||
private void Update()
|
||||
{
|
||||
@@ -81,7 +87,7 @@ namespace Player
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unity OnTriggerEnter2D method. Applies damage when colliding with a punch hurtbox.
|
||||
/// Applies damage when colliding with a punch hurtbox.
|
||||
/// </summary>
|
||||
/// <param name="collision">The collider that entered the trigger.</param>
|
||||
private void OnTriggerEnter2D(Collider2D collision)
|
||||
@@ -99,33 +105,40 @@ namespace Player
|
||||
/// <param name="damageSource">The GameObject causing the damage.</param>
|
||||
private void Damage(GameObject damageSource)
|
||||
{
|
||||
if (dying || damageSource.CompareTag("Hat")) return; // Exclude hat from taking damage
|
||||
// Prevent damage if the player is dying or the damage source is a hat
|
||||
if (dying || damageSource.CompareTag("Hat")) return;
|
||||
|
||||
float actualForce = damageSource.GetComponent<Damageable>().force;
|
||||
Block blockComponent = GetComponent<Block>();
|
||||
|
||||
GetComponentInChildren<UseItem>().DropItem(); // Drops hat if held
|
||||
// Drop the item if the player is holding one
|
||||
GetComponentInChildren<UseItem>().DropItem();
|
||||
|
||||
if (blockComponent != null && blockComponent.blocking)
|
||||
{
|
||||
if (blockComponent.IsParrying()) // Player receives damage if punching a parrying player
|
||||
if (blockComponent.IsParrying())
|
||||
{
|
||||
// Handle parry logic
|
||||
damageSource.GetComponent<Damageable>().SuccessfulParry(gameObject, actualForce);
|
||||
AudioManager.Instance.PlaySound("Parry");
|
||||
return;
|
||||
}
|
||||
else // Player does less damage if punching a blocking player
|
||||
else
|
||||
{
|
||||
// Reduce damage if the player is blocking
|
||||
AudioManager.Instance.PlaySound("Punch");
|
||||
actualForce /= 4;
|
||||
GetComponent<Rigidbody2D>().AddForce(((transform.position - damageSource.transform.position).normalized + Vector3.up * 2) * actualForce, ForceMode2D.Force);
|
||||
}
|
||||
}
|
||||
else // Player does full damage to a non-blocking player
|
||||
else
|
||||
{
|
||||
// Apply full damage if the player is not blocking
|
||||
AudioManager.Instance.PlaySound("Punch");
|
||||
GetComponent<Rigidbody2D>().AddForce(((transform.position - damageSource.transform.position).normalized + Vector3.up * 2) * actualForce * (1 + (damage / maxDamage) * 3), ForceMode2D.Force);
|
||||
}
|
||||
|
||||
// Update the player's damage and check if they should die
|
||||
damage += actualForce;
|
||||
damage = Mathf.Clamp(damage, 0f, maxDamage);
|
||||
if (damage >= maxDamage)
|
||||
@@ -140,10 +153,8 @@ namespace Player
|
||||
/// <param name="damage">The amount of damage to add.</param>
|
||||
public void Damage(float damage)
|
||||
{
|
||||
//if (GameManager.Instance.gameOver) return; // Prevent damage after game is over
|
||||
|
||||
this.damage += damage;
|
||||
if (damage >= maxDamage)
|
||||
if (this.damage >= maxDamage)
|
||||
{
|
||||
Die();
|
||||
}
|
||||
@@ -171,13 +182,16 @@ namespace Player
|
||||
/// </summary>
|
||||
private void Die()
|
||||
{
|
||||
if (GameManager.Instance != null) //&& !GameManager.Instance.gameOver) // Prevent death after game is over
|
||||
if (GameManager.Instance != null)
|
||||
{
|
||||
// Drop the item if the player is holding one
|
||||
UseItem useItem = GetComponent<UseItem>();
|
||||
if (useItem != null)
|
||||
{
|
||||
useItem.DropItem(); // Ensure the player drops the item before the death animation
|
||||
useItem.DropItem();
|
||||
}
|
||||
|
||||
// Trigger the death animation and mark the player as dying
|
||||
animator.SetBool("die", true);
|
||||
dying = true;
|
||||
|
||||
@@ -203,15 +217,17 @@ namespace Player
|
||||
public void Respawn()
|
||||
{
|
||||
transform.position = GameManager.Instance.spawnPosition;
|
||||
|
||||
// Reset the player's velocity
|
||||
if (TryGetComponent<Rigidbody2D>(out var rb))
|
||||
{
|
||||
rb.linearVelocity = Vector2.zero;
|
||||
rb.angularVelocity = 0f;
|
||||
}
|
||||
if (TryGetComponent<Damageable>(out var damageable))
|
||||
{
|
||||
damageable.ResetDamage();
|
||||
}
|
||||
|
||||
// Reset the player's damage
|
||||
ResetDamage();
|
||||
|
||||
OnPlayerRespawn?.Invoke(gameObject);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,89 +1,161 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine; using Game; using Music; using Player;
|
||||
using UnityEngine;
|
||||
using Game;
|
||||
using Music;
|
||||
using Player;
|
||||
|
||||
namespace Player
|
||||
{
|
||||
|
||||
public class PlayerCameraMovement : MonoBehaviour
|
||||
{
|
||||
private Vector3 start;
|
||||
private Vector3 target;
|
||||
public float weight;
|
||||
public float speed;
|
||||
private GameObject playerThatWon;
|
||||
public float lowerBound;
|
||||
public bool winScene = false;
|
||||
|
||||
public bool staticCamera = false;
|
||||
|
||||
private void Start()
|
||||
/// <summary>
|
||||
/// This class controls the movement of the camera to follow players during the game.
|
||||
/// It also handles special behavior for the camera in the win scene.
|
||||
/// </summary>
|
||||
public class PlayerCameraMovement : MonoBehaviour
|
||||
{
|
||||
start = transform.position;
|
||||
}
|
||||
/// <summary>
|
||||
/// The starting position of the camera.
|
||||
/// </summary>
|
||||
private Vector3 start;
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (winScene) // If the game is over, the camera will follow the player that won
|
||||
/// <summary>
|
||||
/// The target position the camera should move toward.
|
||||
/// </summary>
|
||||
private Vector3 target;
|
||||
|
||||
/// <summary>
|
||||
/// The weight used to blend between the camera's starting position and the players' average position.
|
||||
/// </summary>
|
||||
public float weight;
|
||||
|
||||
/// <summary>
|
||||
/// The speed at which the camera moves toward the target position.
|
||||
/// </summary>
|
||||
public float speed;
|
||||
|
||||
/// <summary>
|
||||
/// The player who won the game, used to focus the camera in the win scene.
|
||||
/// </summary>
|
||||
private GameObject playerThatWon;
|
||||
|
||||
/// <summary>
|
||||
/// The lowest vertical position the camera can move to.
|
||||
/// </summary>
|
||||
public float lowerBound;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the camera is in the win scene mode.
|
||||
/// </summary>
|
||||
public bool winScene = false;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the camera should remain static and not follow players.
|
||||
/// </summary>
|
||||
public bool staticCamera = false;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the camera's starting position.
|
||||
/// </summary>
|
||||
private void Start()
|
||||
{
|
||||
if (playerThatWon == null || !playerThatWon.activeInHierarchy)
|
||||
start = transform.position;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the camera's position every frame to follow players or focus on the winner.
|
||||
/// </summary>
|
||||
private void Update()
|
||||
{
|
||||
// If the game is over, focus the camera on the player that won
|
||||
if (winScene)
|
||||
{
|
||||
playerThatWon = FindWinner();
|
||||
if (playerThatWon == null || !playerThatWon.activeInHierarchy)
|
||||
{
|
||||
playerThatWon = FindWinner();
|
||||
}
|
||||
|
||||
if (playerThatWon != null)
|
||||
{
|
||||
// Move the camera toward the winning player's position
|
||||
target = playerThatWon.transform.position;
|
||||
transform.position = Vector3.Lerp(
|
||||
transform.position,
|
||||
new Vector3(target.x, target.y, target.z - 10),
|
||||
speed * 12 * Time.deltaTime
|
||||
);
|
||||
|
||||
// Ensure the camera does not go below the lower bound
|
||||
if (transform.position.y < lowerBound)
|
||||
{
|
||||
transform.position = new Vector3(transform.position.x, lowerBound, transform.position.z);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (playerThatWon != null)
|
||||
// Follow the players during the game
|
||||
List<GameObject> players = GameManager.players;
|
||||
if (players.Count == 0) return;
|
||||
|
||||
Vector3 playerAverage = Vector3.zero;
|
||||
int activePlayers = 0;
|
||||
|
||||
// Calculate the average position of all active players
|
||||
foreach (GameObject player in players)
|
||||
{
|
||||
target = playerThatWon.transform.position;
|
||||
transform.position = Vector3.Lerp(transform.position, new Vector3(target.x, target.y, target.z - 10), speed * 12 * Time.deltaTime);
|
||||
if (transform.position.y < lowerBound)
|
||||
if (player == null || !player.activeInHierarchy) continue;
|
||||
|
||||
Damageable damageable = player.GetComponent<Damageable>();
|
||||
if (damageable != null && damageable.dying) continue;
|
||||
|
||||
playerAverage += player.transform.position;
|
||||
activePlayers++;
|
||||
}
|
||||
|
||||
if (activePlayers == 0 || staticCamera) return;
|
||||
|
||||
playerAverage /= activePlayers;
|
||||
|
||||
// Blend between the starting position and the players' average position
|
||||
target = start * weight + playerAverage * (1 - weight);
|
||||
transform.position = Vector3.Lerp(
|
||||
transform.position,
|
||||
new Vector3(target.x, target.y, transform.position.z),
|
||||
speed * Time.deltaTime
|
||||
);
|
||||
|
||||
// Ensure the camera does not go below the lower bound
|
||||
if (transform.position.y < lowerBound)
|
||||
{
|
||||
transform.position = new Vector3(transform.position.x, lowerBound, transform.position.z);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Activates the win scene mode and focuses the camera on the winning player.
|
||||
/// </summary>
|
||||
/// <param name="player">The player who won the game.</param>
|
||||
public void WinScene(GameObject player)
|
||||
{
|
||||
winScene = true;
|
||||
playerThatWon = player;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the first active player in the game, used to determine the winner.
|
||||
/// </summary>
|
||||
/// <returns>The first active player GameObject, or null if no players are active.</returns>
|
||||
private GameObject FindWinner()
|
||||
{
|
||||
foreach (GameObject player in GameManager.players)
|
||||
{
|
||||
if (player != null && player.activeInHierarchy)
|
||||
{
|
||||
transform.position = new Vector3(transform.position.x, lowerBound, transform.position.z);
|
||||
return player;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
// Moves the camera to follow the players
|
||||
List<GameObject> players = GameManager.players;
|
||||
if (players.Count == 0) return;
|
||||
Vector3 playerAverage = Vector3.zero;
|
||||
int activePlayers = 0;
|
||||
foreach (GameObject player in players)
|
||||
{
|
||||
if (player == null || !player.activeInHierarchy) continue;
|
||||
Damageable damageable = player.GetComponent<Damageable>();
|
||||
if (damageable != null && damageable.dying) continue;
|
||||
playerAverage += player.transform.position;
|
||||
activePlayers++;
|
||||
}
|
||||
|
||||
if (activePlayers == 0) return;
|
||||
if (staticCamera) return;
|
||||
|
||||
playerAverage /= activePlayers;
|
||||
|
||||
target = start * weight + playerAverage * (1 - weight);
|
||||
transform.position = Vector3.Lerp(transform.position, new Vector3(target.x, target.y, transform.position.z), speed * Time.deltaTime);
|
||||
if (transform.position.y < lowerBound)
|
||||
{
|
||||
transform.position = new Vector3(transform.position.x, lowerBound, transform.position.z);
|
||||
}
|
||||
}
|
||||
|
||||
public void WinScene(GameObject player)
|
||||
{
|
||||
winScene = true;
|
||||
playerThatWon = player;
|
||||
}
|
||||
|
||||
private GameObject FindWinner() // Finds the player that won
|
||||
{
|
||||
foreach (GameObject player in GameManager.players)
|
||||
{
|
||||
if (player != null && player.activeInHierarchy)
|
||||
{
|
||||
return player;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,111 +1,198 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine; using Game; using Music; using Player;
|
||||
using UnityEngine;
|
||||
using Game;
|
||||
using Music;
|
||||
using Player;
|
||||
using UnityEngine.InputSystem;
|
||||
|
||||
namespace Player
|
||||
{
|
||||
|
||||
public class PlayerManager : MonoBehaviour
|
||||
{
|
||||
public static PlayerManager Instance;
|
||||
public List<PlayerJoinCard> cards;
|
||||
[SerializeField] private InputActionAsset playerActions;
|
||||
public List<Color> playerColors;
|
||||
public GameObject playerSelect;
|
||||
private bool gameStarted = false;
|
||||
|
||||
private void Awake()
|
||||
/// <summary>
|
||||
/// This class manages player-related functionality, such as joining, leaving, and assigning colors.
|
||||
/// It also handles starting the game once players have joined.
|
||||
/// </summary>
|
||||
public class PlayerManager : MonoBehaviour
|
||||
{
|
||||
Init();
|
||||
}
|
||||
/// <summary>
|
||||
/// The singleton instance of the <see cref="PlayerManager"/> class.
|
||||
/// </summary>
|
||||
public static PlayerManager Instance;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
GetComponent<PlayerInputManager>().onPlayerJoined += OnPlayerJoined;
|
||||
GetComponent<PlayerInputManager>().onPlayerLeft += OnPlayerLeft;
|
||||
}
|
||||
/// <summary>
|
||||
/// A list of player join cards, which represent players in the UI.
|
||||
/// </summary>
|
||||
public List<PlayerJoinCard> cards;
|
||||
|
||||
private void OnPlayerJoined(PlayerInput playerInput) // Adds a player when they join
|
||||
{
|
||||
if (gameStarted)
|
||||
{
|
||||
Destroy(playerInput.gameObject);
|
||||
return;
|
||||
}
|
||||
print("Player joined");
|
||||
DontDestroyOnLoad(playerInput.gameObject);
|
||||
if (PlayerCardCreator.Instance == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
PlayerJoinCard card = PlayerCardCreator.Instance.CreateCard();
|
||||
if (card == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
card.playerNumber = GameManager.players.Count + 1;
|
||||
cards.Add(card);
|
||||
if (GameManager.players == null)
|
||||
{
|
||||
GameManager.players = new List<GameObject>();
|
||||
}
|
||||
GameManager.players.Add(playerInput.gameObject);
|
||||
Colorize(GameManager.players.Count - 1);
|
||||
}
|
||||
/// <summary>
|
||||
/// The input actions used by players.
|
||||
/// </summary>
|
||||
[SerializeField] private InputActionAsset playerActions;
|
||||
|
||||
/// <summary>
|
||||
/// A list of colors assigned to players for identification.
|
||||
/// </summary>
|
||||
public List<Color> playerColors;
|
||||
|
||||
private void OnPlayerLeft(PlayerInput playerInput) // Removes the player if they leave
|
||||
{
|
||||
Destroy(playerInput.gameObject);
|
||||
GameManager.players.Remove(playerInput.gameObject);
|
||||
print("Player left");
|
||||
}
|
||||
/// <summary>
|
||||
/// The UI element used for player selection.
|
||||
/// </summary>
|
||||
public GameObject playerSelect;
|
||||
|
||||
private void Init()
|
||||
{
|
||||
if (Instance == null)
|
||||
/// <summary>
|
||||
/// Indicates whether the game has started.
|
||||
/// </summary>
|
||||
private bool gameStarted = false;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the singleton instance of the <see cref="PlayerManager"/>.
|
||||
/// </summary>
|
||||
private void Awake()
|
||||
{
|
||||
Instance = this;
|
||||
Init();
|
||||
}
|
||||
else
|
||||
{
|
||||
Destroy(this.gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
public void StartGame() // Allows game to start after a player has joined
|
||||
{
|
||||
if (GameManager.players.Count == 0)
|
||||
/// <summary>
|
||||
/// Subscribes to player join and leave events.
|
||||
/// </summary>
|
||||
private void Start()
|
||||
{
|
||||
return;
|
||||
GetComponent<PlayerInputManager>().onPlayerJoined += OnPlayerJoined;
|
||||
GetComponent<PlayerInputManager>().onPlayerLeft += OnPlayerLeft;
|
||||
}
|
||||
gameStarted = true;
|
||||
HubManager.Instance.LoadScene(GameManager.map);
|
||||
}
|
||||
|
||||
private void Colorize(int index) // Pairs each player with a unique color
|
||||
{
|
||||
GameObject player = GameManager.players[index];
|
||||
Color color = playerColors[(GameManager.players.Count - 1) % playerColors.Count];
|
||||
float tint = Mathf.Floor((GameManager.players.Count - 1) / playerColors.Count);
|
||||
color = (color + color + Color.white * tint) / (tint + 2);
|
||||
GameManager.playerColors.Add(color);
|
||||
ApplyColor(player, color);
|
||||
ApplyColor(cards[GameManager.players.IndexOf(player)].playerPreview, color);
|
||||
}
|
||||
|
||||
private void ApplyColor(GameObject obj, Color color) // Applies a color to each player
|
||||
{
|
||||
if (obj.TryGetComponent<SpriteRenderer>(out _))
|
||||
/// <summary>
|
||||
/// Handles logic for when a player joins the game.
|
||||
/// </summary>
|
||||
/// <param name="playerInput">The <see cref="PlayerInput"/> of the player who joined.</param>
|
||||
private void OnPlayerJoined(PlayerInput playerInput)
|
||||
{
|
||||
obj.GetComponent<SpriteRenderer>().color = color;
|
||||
}
|
||||
foreach (Transform child in obj.transform)
|
||||
{
|
||||
if (child.TryGetComponent<SpriteRenderer>(out _))
|
||||
// Prevent players from joining after the game has started
|
||||
if (gameStarted)
|
||||
{
|
||||
ApplyColor(child.gameObject, color);
|
||||
Destroy(playerInput.gameObject);
|
||||
return;
|
||||
}
|
||||
|
||||
print("Player joined");
|
||||
|
||||
// Ensure the player object persists across scenes
|
||||
DontDestroyOnLoad(playerInput.gameObject);
|
||||
|
||||
// Create a player join card for the new player
|
||||
if (PlayerCardCreator.Instance == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
PlayerJoinCard card = PlayerCardCreator.Instance.CreateCard();
|
||||
if (card == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Assign a player number and add the card to the list
|
||||
card.playerNumber = GameManager.players.Count + 1;
|
||||
cards.Add(card);
|
||||
|
||||
// Initialize the player list in the GameManager if it doesn't exist
|
||||
if (GameManager.players == null)
|
||||
{
|
||||
GameManager.players = new List<GameObject>();
|
||||
}
|
||||
|
||||
// Add the player to the GameManager's player list and assign a color
|
||||
GameManager.players.Add(playerInput.gameObject);
|
||||
Colorize(GameManager.players.Count - 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles logic for when a player leaves the game.
|
||||
/// </summary>
|
||||
/// <param name="playerInput">The <see cref="PlayerInput"/> of the player who left.</param>
|
||||
private void OnPlayerLeft(PlayerInput playerInput)
|
||||
{
|
||||
// Remove the player from the game and destroy their object
|
||||
Destroy(playerInput.gameObject);
|
||||
GameManager.players.Remove(playerInput.gameObject);
|
||||
print("Player left");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the singleton instance of the <see cref="PlayerManager"/>.
|
||||
/// </summary>
|
||||
private void Init()
|
||||
{
|
||||
if (Instance == null)
|
||||
{
|
||||
Instance = this;
|
||||
}
|
||||
else
|
||||
{
|
||||
Destroy(this.gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the game if at least one player has joined.
|
||||
/// </summary>
|
||||
public void StartGame()
|
||||
{
|
||||
// Prevent starting the game if no players have joined
|
||||
if (GameManager.players.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
gameStarted = true;
|
||||
|
||||
// Load the selected map
|
||||
HubManager.Instance.LoadScene(GameManager.map);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assigns a unique color to a player and their associated UI elements.
|
||||
/// </summary>
|
||||
/// <param name="index">The index of the player in the player list.</param>
|
||||
private void Colorize(int index)
|
||||
{
|
||||
// Get the player object and assign a base color
|
||||
GameObject player = GameManager.players[index];
|
||||
Color color = playerColors[(GameManager.players.Count - 1) % playerColors.Count];
|
||||
|
||||
// Adjust the color tint based on the number of players
|
||||
float tint = Mathf.Floor((GameManager.players.Count - 1) / playerColors.Count);
|
||||
color = (color + color + Color.white * tint) / (tint + 2);
|
||||
|
||||
// Add the color to the GameManager's player color list
|
||||
GameManager.playerColors.Add(color);
|
||||
|
||||
// Apply the color to the player and their UI preview
|
||||
ApplyColor(player, color);
|
||||
ApplyColor(cards[GameManager.players.IndexOf(player)].playerPreview, color);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies a color to a GameObject and its children.
|
||||
/// </summary>
|
||||
/// <param name="obj">The GameObject to colorize.</param>
|
||||
/// <param name="color">The color to apply.</param>
|
||||
private void ApplyColor(GameObject obj, Color color)
|
||||
{
|
||||
// Apply the color to the object's SpriteRenderer, if it has one
|
||||
if (obj.TryGetComponent<SpriteRenderer>(out _))
|
||||
{
|
||||
obj.GetComponent<SpriteRenderer>().color = color;
|
||||
}
|
||||
|
||||
// Recursively apply the color to all child objects
|
||||
foreach (Transform child in obj.transform)
|
||||
{
|
||||
if (child.TryGetComponent<SpriteRenderer>(out _))
|
||||
{
|
||||
ApplyColor(child.gameObject, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
using System.Collections;
|
||||
using TMPro;
|
||||
using Unity.IO.LowLevel.Unsafe;
|
||||
using UnityEngine;
|
||||
using Game;
|
||||
using Music;
|
||||
using Player;
|
||||
using UnityEngine.InputSystem;
|
||||
|
||||
namespace Player
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// This class handles the player's movement, including walking, jumping, and animations.
|
||||
/// It also manages input, physics, and interactions with the ground.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(Rigidbody2D))]
|
||||
[RequireComponent(typeof(BoxCollider2D))]
|
||||
[RequireComponent(typeof(PlayerInput))]
|
||||
@@ -16,6 +19,7 @@ namespace Player
|
||||
[RequireComponent(typeof(Punch))]
|
||||
public class PlayerMovement : MonoBehaviour
|
||||
{
|
||||
// --- Public Fields ---
|
||||
|
||||
/// <summary>
|
||||
/// Layers considered as ground for the player.
|
||||
@@ -28,10 +32,11 @@ namespace Player
|
||||
/// </summary>
|
||||
public TextMeshProUGUI playerText;
|
||||
|
||||
[Header("Movement")]
|
||||
|
||||
/// <summary>
|
||||
/// Base walk speed of the player.
|
||||
/// </summary>
|
||||
[Header("Movement")]
|
||||
public float walkSpeed;
|
||||
|
||||
/// <summary>
|
||||
@@ -109,6 +114,8 @@ namespace Player
|
||||
/// </summary>
|
||||
public float groundCheckDistance;
|
||||
|
||||
// --- Private Fields ---
|
||||
|
||||
/// <summary>
|
||||
/// Reference to the Rigidbody2D component.
|
||||
/// </summary>
|
||||
@@ -174,6 +181,8 @@ namespace Player
|
||||
/// </summary>
|
||||
private Vector3 positionLastFrame;
|
||||
|
||||
// --- Unity Methods ---
|
||||
|
||||
/// <summary>
|
||||
/// Initializes player components and sets up initial values.
|
||||
/// </summary>
|
||||
@@ -221,6 +230,8 @@ namespace Player
|
||||
Animate();
|
||||
}
|
||||
|
||||
// --- Movement Methods ---
|
||||
|
||||
/// <summary>
|
||||
/// Updates the player's animation state based on movement and grounded status.
|
||||
/// </summary>
|
||||
@@ -236,20 +247,10 @@ namespace Player
|
||||
animationPlayer.SetState(AnimationPlayer.AnimationState.Idle);
|
||||
}
|
||||
|
||||
if (true)
|
||||
{
|
||||
if (virtualAxisX < -0.01f)
|
||||
animationPlayer.backwards = true;
|
||||
else if (virtualAxisX > 0.01f)
|
||||
animationPlayer.backwards = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (body.linearVelocityX < -0.1f)
|
||||
animationPlayer.backwards = true;
|
||||
else if (body.linearVelocityX > 0.1f)
|
||||
animationPlayer.backwards = false;
|
||||
}
|
||||
if (virtualAxisX < -0.01f)
|
||||
animationPlayer.backwards = true;
|
||||
else if (virtualAxisX > 0.01f)
|
||||
animationPlayer.backwards = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -427,6 +428,5 @@ namespace Player
|
||||
{
|
||||
if (IsPhysicallyGrounded()) body.linearVelocity = Vector2.zero;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,63 +1,108 @@
|
||||
using UnityEngine; using Game; using Music; using Player;
|
||||
using UnityEngine;
|
||||
using Game;
|
||||
using Music;
|
||||
using Player;
|
||||
using UnityEngine.InputSystem;
|
||||
|
||||
namespace Player
|
||||
{
|
||||
|
||||
[RequireComponent(typeof(PlayerInput))]
|
||||
[RequireComponent(typeof(AnimationPlayer))]
|
||||
public class Punch : MonoBehaviour
|
||||
{
|
||||
public bool cancelable = true;
|
||||
|
||||
[SerializeField] private BoxCollider2D hurtbox;
|
||||
|
||||
InputActionAsset actions;
|
||||
|
||||
private void Start()
|
||||
/// <summary>
|
||||
/// This class handles the punching mechanic for the player, including triggering animations,
|
||||
/// enabling and disabling the hurtbox, and managing player speed during a punch.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(PlayerInput))]
|
||||
[RequireComponent(typeof(AnimationPlayer))]
|
||||
public class Punch : MonoBehaviour
|
||||
{
|
||||
actions = GetComponent<PlayerInput>().actions;
|
||||
}
|
||||
/// <summary>
|
||||
/// Determines whether the player can cancel their punch action.
|
||||
/// </summary>
|
||||
public bool cancelable = true;
|
||||
|
||||
private void Update() // Executes punch when 'punch' is pressed
|
||||
{
|
||||
if (actions.FindAction("Punch").WasPressedThisFrame())
|
||||
/// <summary>
|
||||
/// The hurtbox used to detect collisions with other players or objects during a punch.
|
||||
/// </summary>
|
||||
[SerializeField] private BoxCollider2D hurtbox;
|
||||
|
||||
/// <summary>
|
||||
/// The input actions associated with the player.
|
||||
/// </summary>
|
||||
private InputActionAsset actions;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the player's input actions.
|
||||
/// </summary>
|
||||
private void Start()
|
||||
{
|
||||
if (!cancelable) return;
|
||||
ExecutePunch();
|
||||
actions = GetComponent<PlayerInput>().actions;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks for punch input every frame and executes the punch if the action is triggered.
|
||||
/// </summary>
|
||||
private void Update()
|
||||
{
|
||||
// Executes punch when the "Punch" action is pressed
|
||||
if (actions.FindAction("Punch").WasPressedThisFrame())
|
||||
{
|
||||
if (!cancelable) return; // Prevents punching if the action is not cancelable
|
||||
ExecutePunch();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the punch action, triggering the punch animation and slowing the player down.
|
||||
/// </summary>
|
||||
private void ExecutePunch()
|
||||
{
|
||||
// Trigger the punch animation
|
||||
GetComponent<AnimationPlayer>().Punch();
|
||||
|
||||
// Disable the ability to cancel the punch
|
||||
DisableCancellation();
|
||||
|
||||
// Temporarily reduce the player's movement speed during the punch
|
||||
GetComponent<PlayerMovement>().maxSpeedOverride = 1f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables the hurtbox, allowing the punch to interact with other objects.
|
||||
/// </summary>
|
||||
public void EnableHurtbox()
|
||||
{
|
||||
if (hurtbox != null) hurtbox.enabled = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disables the hurtbox, preventing the punch from interacting with other objects.
|
||||
/// </summary>
|
||||
public void DisableHurtbox()
|
||||
{
|
||||
if (hurtbox != null) hurtbox.enabled = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disables the ability to cancel the punch action.
|
||||
/// </summary>
|
||||
public void DisableCancellation()
|
||||
{
|
||||
cancelable = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables the ability to cancel the punch action.
|
||||
/// </summary>
|
||||
public void EnableCancellation()
|
||||
{
|
||||
cancelable = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the player's movement speed to its maximum value after the punch is complete.
|
||||
/// </summary>
|
||||
public void ReturnToMaxSpeed()
|
||||
{
|
||||
GetComponent<PlayerMovement>().maxSpeedOverride = GetComponent<PlayerMovement>().maxSpeed;
|
||||
}
|
||||
}
|
||||
|
||||
private void ExecutePunch() // Triggers punch animation
|
||||
{
|
||||
GetComponent<AnimationPlayer>().Punch();
|
||||
DisableCancellation();
|
||||
GetComponent<PlayerMovement>().maxSpeedOverride = 1f; // Slows player down when punching
|
||||
}
|
||||
|
||||
public void EnableHurtbox()
|
||||
{
|
||||
if (hurtbox != null) hurtbox.enabled = true;
|
||||
}
|
||||
|
||||
public void DisableHurtbox()
|
||||
{
|
||||
if (hurtbox != null) hurtbox.enabled = false;
|
||||
}
|
||||
|
||||
public void DisableCancellation()
|
||||
{
|
||||
cancelable = false;
|
||||
}
|
||||
|
||||
public void EnableCancellation()
|
||||
{
|
||||
cancelable = true;
|
||||
}
|
||||
|
||||
public void ReturnToMaxSpeed() // Resets player speed after punch
|
||||
{
|
||||
GetComponent<PlayerMovement>().maxSpeedOverride = GetComponent<PlayerMovement>().maxSpeed;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,41 +1,73 @@
|
||||
using UnityEngine; using Game; using Music; using Player;
|
||||
using UnityEngine;
|
||||
using Game;
|
||||
using Music;
|
||||
using Player;
|
||||
|
||||
namespace Player
|
||||
{
|
||||
|
||||
public class TeleportPlatform : MonoBehaviour
|
||||
{
|
||||
public Vector2 teleportPoint;
|
||||
public string teleportTag;
|
||||
public string playerTag = "Player";
|
||||
public bool isPlatform = true;
|
||||
|
||||
private void OnTriggerEnter2D(Collider2D collision)
|
||||
/// <summary>
|
||||
/// This class handles teleportation for platforms and players when they collide with the teleport trigger.
|
||||
/// It can teleport either the platform itself or a player to a specified location.
|
||||
/// </summary>
|
||||
public class TeleportPlatform : MonoBehaviour
|
||||
{
|
||||
if (!isPlatform)
|
||||
/// <summary>
|
||||
/// The position where the platform or player will be teleported.
|
||||
/// </summary>
|
||||
public Vector2 teleportPoint;
|
||||
|
||||
/// <summary>
|
||||
/// The tag used to identify objects (e.g., platforms) that can trigger teleportation.
|
||||
/// </summary>
|
||||
public string teleportTag;
|
||||
|
||||
/// <summary>
|
||||
/// The tag used to identify player objects.
|
||||
/// </summary>
|
||||
public string playerTag = "Player";
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this script is handling a platform or a player.
|
||||
/// If true, it teleports players. If false, it teleports the platform itself.
|
||||
/// </summary>
|
||||
public bool isPlatform = true;
|
||||
|
||||
/// <summary>
|
||||
/// Handles the teleportation logic when an object enters the trigger collider.
|
||||
/// </summary>
|
||||
/// <param name="collision">The collider of the object that entered the trigger.</param>
|
||||
private void OnTriggerEnter2D(Collider2D collision)
|
||||
{
|
||||
// Teleports the platform
|
||||
if (collision.CompareTag(teleportTag))
|
||||
if (!isPlatform)
|
||||
{
|
||||
transform.position = teleportPoint;
|
||||
if (TryGetComponent<Rigidbody2D>(out var rb))
|
||||
// Teleports the platform itself
|
||||
if (collision.CompareTag(teleportTag))
|
||||
{
|
||||
rb.linearVelocity = Vector2.zero;
|
||||
// Move the platform to the teleport point
|
||||
transform.position = teleportPoint;
|
||||
|
||||
// Reset the platform's velocity if it has a Rigidbody2D component
|
||||
if (TryGetComponent<Rigidbody2D>(out var rb))
|
||||
{
|
||||
rb.linearVelocity = Vector2.zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Teleports the player
|
||||
if (collision.CompareTag(playerTag))
|
||||
else
|
||||
{
|
||||
collision.transform.position = teleportPoint;
|
||||
if (collision.TryGetComponent<Rigidbody2D>(out var rb))
|
||||
// Teleports the player
|
||||
if (collision.CompareTag(playerTag))
|
||||
{
|
||||
rb.linearVelocity = Vector2.zero;
|
||||
// Move the player to the teleport point
|
||||
collision.transform.position = teleportPoint;
|
||||
|
||||
// Reset the player's velocity if they have a Rigidbody2D component
|
||||
if (collision.TryGetComponent<Rigidbody2D>(out var rb))
|
||||
{
|
||||
rb.linearVelocity = Vector2.zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,31 +3,70 @@ using UnityEngine;
|
||||
using Game;
|
||||
using Music;
|
||||
using Player;
|
||||
|
||||
namespace Player
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// This class allows a player to pick up, hold, and drop items during the game.
|
||||
/// It is primarily used for managing interactions with the "hat" in "keep-away" mode.
|
||||
/// </summary>
|
||||
public class UseItem : MonoBehaviour
|
||||
{
|
||||
/// <summary>
|
||||
/// The tag used to identify items that can be picked up.
|
||||
/// </summary>
|
||||
[SerializeField] private string itemTag;
|
||||
|
||||
/// <summary>
|
||||
/// The item currently being held by the player.
|
||||
/// </summary>
|
||||
private GameObject heldItem;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the player is currently holding an item.
|
||||
/// </summary>
|
||||
private bool isHoldingItem = false;
|
||||
|
||||
/// <summary>
|
||||
/// The time when the player started holding the item.
|
||||
/// </summary>
|
||||
private float holdStartTime;
|
||||
|
||||
/// <summary>
|
||||
/// The total time the player has held the item.
|
||||
/// </summary>
|
||||
public float holdTime;
|
||||
|
||||
/// <summary>
|
||||
/// Reference to the player's <see cref="Damageable"/> component.
|
||||
/// </summary>
|
||||
private Damageable damageable;
|
||||
|
||||
/// <summary>
|
||||
/// The position where the item will be held (e.g., above the player's head).
|
||||
/// </summary>
|
||||
[SerializeField] public Transform head;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the player's <see cref="Damageable"/> component.
|
||||
/// </summary>
|
||||
private void Start()
|
||||
{
|
||||
damageable = GetComponent<Damageable>();
|
||||
}
|
||||
|
||||
void Update()
|
||||
/// <summary>
|
||||
/// Updates the player's state every frame.
|
||||
/// If the player is holding an item, it keeps the item positioned on their head
|
||||
/// and updates the hold time in "keep-away" mode.
|
||||
/// </summary>
|
||||
private void Update()
|
||||
{
|
||||
if (isHoldingItem)
|
||||
{
|
||||
// Keeps hat on the player's head
|
||||
// Keeps the item positioned on the player's head
|
||||
heldItem.transform.localPosition = Vector3.zero;
|
||||
|
||||
if (GameManager.gameMode == GameManager.GameMode.keepAway)
|
||||
{
|
||||
// Adds time to the player's leaderboard standing
|
||||
@@ -37,19 +76,30 @@ namespace Player
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCollisionEnter2D(Collision2D collision) // Player automatically picks up hat when touching it
|
||||
/// <summary>
|
||||
/// Automatically picks up an item when the player collides with it.
|
||||
/// </summary>
|
||||
/// <param name="collision">The collision data from the item.</param>
|
||||
private void OnCollisionEnter2D(Collision2D collision)
|
||||
{
|
||||
// Check if the collided object is a "hat" and the player is not already holding an item
|
||||
if (collision.gameObject.CompareTag("Hat") && !isHoldingItem && !damageable.dying)
|
||||
{
|
||||
PickUpItem(collision.gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
public void PickUpItem(GameObject item) // Player picks up hat and starts hold counter
|
||||
/// <summary>
|
||||
/// Allows the player to pick up an item and start the hold timer.
|
||||
/// </summary>
|
||||
/// <param name="item">The item to pick up.</param>
|
||||
public void PickUpItem(GameObject item)
|
||||
{
|
||||
if (damageable.dying) return; // Prevent picking up items if the player is dying
|
||||
if (HatRespawn.canBePickedUp == false) return; // Prevent picking up items if they are not interactable
|
||||
// Prevent picking up items if the player is dying or the item is not interactable
|
||||
if (damageable.dying) return;
|
||||
if (HatRespawn.canBePickedUp == false) return;
|
||||
|
||||
// Set the item as the held item and update its state
|
||||
heldItem = item;
|
||||
isHoldingItem = true;
|
||||
holdStartTime = Time.time;
|
||||
@@ -59,31 +109,47 @@ namespace Player
|
||||
item.transform.parent = head;
|
||||
item.transform.localRotation = Quaternion.identity;
|
||||
item.transform.localPosition = Vector3.zero;
|
||||
//item.transform.GetChild(0).transform.localPosition = item.GetComponent<HatRespawn>().initialSubhatPosition;
|
||||
|
||||
// Initialize the player's hold time in the GameManager if not already set
|
||||
if (!GameManager.playerHoldTimes.ContainsKey(gameObject))
|
||||
{
|
||||
GameManager.playerHoldTimes[gameObject] = 0f;
|
||||
}
|
||||
|
||||
// Play the pickup sound and stop any ongoing hat movement
|
||||
AudioManager.Instance.PlaySound("Pickup Hat");
|
||||
GameManager.Instance.StopCoroutine("MoveHatToWinner");
|
||||
}
|
||||
|
||||
public void DropItem() // Player drops hat when hit
|
||||
/// <summary>
|
||||
/// Allows the player to drop the item they are holding.
|
||||
/// </summary>
|
||||
public void DropItem()
|
||||
{
|
||||
if (GameManager.Instance.gameOver) return; // Prevent dropping items if the game is over
|
||||
// Prevent dropping items if the game is over
|
||||
if (GameManager.Instance.gameOver) return;
|
||||
|
||||
if (isHoldingItem)
|
||||
{
|
||||
// Enable the item's collider and make it interactable after a short delay
|
||||
heldItem.GetComponent<Collider2D>().enabled = true;
|
||||
HatRespawn.canBePickedUp = false;
|
||||
StartCoroutine(WaitForInteractability());
|
||||
|
||||
// Make the item dynamic and apply random force and torque to it
|
||||
heldItem.GetComponent<Rigidbody2D>().bodyType = RigidbodyType2D.Dynamic;
|
||||
heldItem.GetComponent<Rigidbody2D>().AddForce(Vector2.up * Random.Range(10f, 30f) + Vector2.right * Random.Range(-10, 10), ForceMode2D.Impulse);
|
||||
heldItem.GetComponent<Rigidbody2D>().AddTorque(Random.Range(-1, 1), ForceMode2D.Impulse);
|
||||
|
||||
// Notify the item that it has been dropped
|
||||
heldItem.GetComponent<HatRespawn>().OnHatDropped();
|
||||
|
||||
// Detach the item from the player
|
||||
heldItem.transform.parent = GameManager.Instance.transform;
|
||||
heldItem = null;
|
||||
isHoldingItem = false;
|
||||
|
||||
// Remove the player's hold time from the GameManager
|
||||
if (GameManager.playerHoldTimes.ContainsKey(gameObject))
|
||||
{
|
||||
GameManager.playerHoldTimes.Remove(gameObject);
|
||||
@@ -91,15 +157,23 @@ namespace Player
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Waits for a short delay before making the item interactable again.
|
||||
/// </summary>
|
||||
/// <returns>An IEnumerator for coroutine execution.</returns>
|
||||
private IEnumerator WaitForInteractability()
|
||||
{
|
||||
yield return new WaitForSeconds(0.1f);
|
||||
HatRespawn.canBePickedUp = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the player is currently holding an item.
|
||||
/// </summary>
|
||||
/// <returns>True if the player is holding an item, false otherwise.</returns>
|
||||
public bool IsHoldingItem()
|
||||
{
|
||||
return isHoldingItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user