added comments and organized project files

This commit is contained in:
djkellerman
2025-03-28 17:39:07 -04:00
parent 22f44a3f20
commit a098e8c053
84 changed files with 183 additions and 214 deletions

View File

@@ -0,0 +1,24 @@
using UnityEngine;
using UnityEngine.InputSystem;
public class PlayerCardCreator : MonoBehaviour
{
public static PlayerCardCreator Instance;
public GameObject playerJoinCardPrefab;
private void Awake() // Ensures only one instance of PlayerCardCreator exists
{
if (Instance == null) Instance = this;
else
{
Destroy(gameObject);
}
}
public PlayerJoinCard CreateCard() // Creates a player join card
{
GameObject card = Instantiate(playerJoinCardPrefab, transform);
return card.GetComponent<PlayerJoinCard>();
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 4faf59678eb534fabab364215e093fa0

View File

@@ -0,0 +1,14 @@
using UnityEngine;
using UnityEngine.EventSystems;
public class EventSystemizer : MonoBehaviour
{
private void Update() // Ensures only one instance of EventSystem exists
{
foreach (EventSystem system in FindObjectsByType<EventSystem>(FindObjectsSortMode.None))
{
if (system == GetComponent<EventSystem>()) continue;
Destroy(system.gameObject);
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 305a0eb0ddc9f88438e978efa8dd6f69

View File

@@ -0,0 +1,46 @@
using System.Collections;
using UnityEngine;
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()
{
defposition = transform.parent.position;
rb = transform.parent.GetComponent<Rigidbody2D>();
}
private void OnTriggerEnter2D(Collider2D collision) // Makes platform fall when player or another platform touch it
{
if (!falling && (collision.gameObject.CompareTag("Player") || collision.transform.GetChild(0).TryGetComponent(out FallPlatform _)))
{
StartCoroutine(FallAfterDelay());
}
}
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();
}
//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;
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 6d315bad368a8a247a5df8ac6b2f3ec1

View File

@@ -0,0 +1,180 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.InputSystem;
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 hatSpawnPosition;
public enum GameMode
{
freeForAll,
keepAway,
obstacleCourse
}
private void Awake() // Ensures only one instance of GameManager exists
{
if (Instance == null)
{
Instance = this;
}
else
{
Destroy(gameObject);
}
}
private void Start() // Starts the game and music
{
MusicManager.Instance.StartPlaylist();
StartGame();
}
public void StartGame() // Sets up the proper gamemode
{
GameManager.playerHoldTimes.Clear();
if (GameManager.players.Count == 0) return;
StartGameEvent?.Invoke();
print("Starting game with mode: " + gameMode + " and map: " + map);
if (gameMode == GameMode.freeForAll) // Sets up the game for free for all mode
{
foreach (GameObject player in players)
{
player.transform.position = spawnPosition + (offset * players.IndexOf(player) * Vector2.right);
player.GetComponent<Damageable>().lives = 5;
}
}
if (gameMode == GameMode.keepAway) // Sets up the game for keep away mode
{
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) // Sets up the game for obstacle course mode
{
foreach (GameObject player in players)
{
player.transform.position = spawnPosition;
}
}
}
public void PlayerDied(Damageable player) // Handles player deaths for the respective gamemode
{
if (gameMode == GameMode.freeForAll) // Respawns player if they have lives left
{
player.lives--;
if (player.lives <= 0 && !gameOver)
{
player.gameObject.SetActive(false);
if (AlivePlayers().Count <= 1)
{
GameOver(); // Winner is called when only one player is left
}
}
else
{
RespawnPlayer(player.gameObject);
}
}
if (gameMode == GameMode.keepAway) // Always respawns player regardless of lives
{
RespawnPlayer(player.gameObject);
}
if (gameMode == GameMode.obstacleCourse)
{
}
}
private void RespawnPlayer(GameObject player) // Respawns player at the spawn point and resets health
{
RespawnOnTriggerEnter respawnScript = player.GetComponent<RespawnOnTriggerEnter>();
if (respawnScript != null)
{
player.transform.position = respawnScript.spawnPoint;
player.GetComponent<Damageable>().ResetDamage();
player.GetComponent<Damageable>().Respawn();
}
}
public void GameOver() // Ends game and displays winner
{
gameOver = true;
EndGameEvent?.Invoke();
if (gameMode == GameMode.freeForAll) // Last player alive wins
{
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) // Player with the most time holding the hat wins
{
GameObject winner = null;
float maxHoldTime = 0f;
foreach (var player in GameManager.playerHoldTimes)
{
if (player.Value > maxHoldTime)
{
maxHoldTime = player.Value;
winner = player.Key;
}
}
if (winner != null)
{
print(winner.name + " is the winner with " + maxHoldTime + " seconds!");
FindFirstObjectByType<PlayerCameraMovement>().WinScene(winner);
WinScreen.Instance.ShowWinScreen(players.IndexOf(winner) + 1);
FindFirstObjectByType<LifeDisplayManager>().HideLifeDisplay();
}
}
}
public List<GameObject> AlivePlayers() // Returns a list of all players that are alive
{
List<GameObject> alivePlayers = new();
foreach (GameObject player in players)
{
if (player.activeInHierarchy) alivePlayers.Add(player);
}
return alivePlayers;
}
public void UpdatePlayerHoldTime(GameObject player, float holdTime) // Finds each players hold time and updates the leaderboard
{
if (playerHoldTimes.ContainsKey(player))
{
playerHoldTimes[player] = holdTime;
}
else
{
playerHoldTimes.Add(player, holdTime);
}
LeaderboardManager.Instance.UpdateLeaderboard();
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: d6e4006937b044c9ab3de25a7ab68162

View File

@@ -0,0 +1,58 @@
using TMPro;
using UnityEngine;
using UnityEngine.UI;
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()
{
timeRemaining = startTime;
timer.text = "3:00.00";
UpdateTimerDisplay();
}
private void Update() // Updates the timer to show the time remaining
{
if (timerRunning)
{
timeRemaining -= Time.deltaTime;
if (timeRemaining <= 0)
{
timeRemaining = 0;
timerRunning = false;
OnTimerEnd();
}
UpdateTimerDisplay();
}
}
public void StartTimer() // Starts the timer
{
if (!timerRunning)
{
timeRemaining = startTime;
timerRunning = true;
}
}
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();
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 012d92d911650b340a4561288484ac28

View File

@@ -0,0 +1,14 @@
using UnityEngine;
public class HatRespawn : MonoBehaviour
{
void OnTriggerEnter2D(Collider2D collision) // Respawns the hat to the hat spawn position if it falls out of bounds
{
if (collision.gameObject.CompareTag("Platformer Hazard"))
{
transform.position = GameManager.Instance.hatSpawnPosition;
GetComponent<Rigidbody2D>().linearVelocity = Vector2.zero;
transform.rotation = Quaternion.identity;
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 1803a208ca94f44a189bf03342859d36

View File

@@ -0,0 +1,55 @@
using System.Collections.Generic;
using UnityEngine;
public class HealthBarManager : MonoBehaviour
{
public GameObject healthBarPrefab;
private Dictionary<GameObject, GameObject> playerHealthBars = new Dictionary<GameObject, GameObject>();
void Start()
{
GameManager.Instance.StartGameEvent += OnGameStart;
GameManager.Instance.EndGameEvent += OnGameEnd;
}
void OnDestroy()
{
GameManager.Instance.StartGameEvent -= OnGameStart;
GameManager.Instance.EndGameEvent -= OnGameEnd;
}
void Update() // Updates position of health bars to follow each player
{
foreach (var kvp in playerHealthBars)
{
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);
}
}
private void OnGameStart() // Creates health bars for each player
{
foreach (GameObject player in GameManager.players)
{
if (!playerHealthBars.ContainsKey(player))
{
GameObject healthBar = Instantiate(healthBarPrefab);
healthBar.transform.localScale *= 1.5f;
healthBar.GetComponent<TerribleHealthBarScript>().SetPlayer(player);
playerHealthBars[player] = healthBar;
}
}
}
private void OnGameEnd() // Destroys the health bars when the game ends
{
foreach (var kvp in playerHealthBars)
{
Destroy(kvp.Value);
}
playerHealthBars.Clear();
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: a80b536f823e50142b142b4e0b64ea97

View File

@@ -0,0 +1,22 @@
using UnityEngine;
public class InfiniteScroll : MonoBehaviour
{
public float speed;
public float start;
public float end;
private void Update() // Moves the background
{
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);
}
transform.position += speed * Time.deltaTime * Vector3.right;
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: c0e4599228c7142f9a5d65ab96f9fdc3

View File

@@ -0,0 +1,56 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
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
{
if (Instance == null)
{
Instance = this;
}
else
{
Destroy(gameObject);
}
}
private void Start()
{
InitializeLeaderboard();
}
private void InitializeLeaderboard() // Creates the leaderboard icons for each player
{
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() // Sorts the leaderboard based on player hold times
{
List<KeyValuePair<GameObject, float>> sortedList = new List<KeyValuePair<GameObject, float>>(GameManager.playerHoldTimes);
sortedList.Sort((pair1, pair2) => pair2.Value.CompareTo(pair1.Value));
foreach (var player in sortedList)
{
Debug.Log(player.Key.name + " : " + player.Value);
}
foreach (var player in sortedList)
{
playerIcons[player.Key].transform.SetSiblingIndex(sortedList.IndexOf(player));
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 94998096868b2f543ae7fcd946cc4f14

View File

@@ -0,0 +1,46 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
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
{
if (GameManager.gameMode == GameManager.GameMode.freeForAll)
{
foreach (GameObject player in GameManager.players)
{
Transform parent = Instantiate(playerPrefab, players.transform).transform;
List<GameObject> lives = new List<GameObject>();
for (int i = 0; i < player.GetComponent<Damageable>().lives; i++)
{
GameObject life = Instantiate(lifePrefab, parent);
life.transform.Find("LIFE").GetComponent<Image>().color = GameManager.playerColors[GameManager.players.IndexOf(player)];
lives.Add(life);
}
lifeDisplays.Add(player.GetComponent<Damageable>(), lives);
}
}
}
private void Update() // Updates the lives displayed based on player lives
{
foreach (Damageable damageable in lifeDisplays.Keys)
{
foreach (GameObject life in lifeDisplays[damageable])
{
life.SetActive(lifeDisplays[damageable].IndexOf(life) < damageable.lives);
}
}
}
public void HideLifeDisplay() // Hides life display
{
players.SetActive(false);
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 3e87378682ef9471d82043136357cc02

View File

@@ -0,0 +1,18 @@
using UnityEngine;
using UnityEngine.UI;
public class MapSelect : MonoBehaviour
{
private ToggleGroup maps;
private void Start()
{
maps = GetComponent<ToggleGroup>();
}
void Update() // Sets the map based on the selected toggle
{
Toggle toggle = maps.GetFirstActiveToggle();
GameManager.map = toggle.name;
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 200b7ae572458463f97cb3b2fb349152

View File

@@ -0,0 +1,29 @@
using UnityEngine;
using UnityEngine.UI;
public class ModeSelect : MonoBehaviour
{
private ToggleGroup maps;
private void Start()
{
maps = GetComponent<ToggleGroup>();
}
void Update() // Updates the game mode based on the selected toggle
{
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;
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 234cf5c6ddbad406d9c687f76819ad6a

View File

@@ -0,0 +1,30 @@
using UnityEngine;
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
{
transform.position = points[startPoint].position;
}
void Update()
{
// 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)
{
i++;
if (i == points.Length)
{
i = 0;
}
}
// Moves the platform towards the next point
transform.position = Vector2.MoveTowards(transform.position, points[i].position, speed * Time.deltaTime);
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 15a2da7226a2f054b9f96b2769e449e2

View File

@@ -0,0 +1,26 @@
using UnityEngine;
public class ObjectVisibility : MonoBehaviour
{
void Start()
{
UpdateVisibility();
}
void Update()
{
UpdateVisibility();
}
private void UpdateVisibility() // Sets object visible if playing keep away mode
{
if (GameManager.gameMode == GameManager.GameMode.keepAway)
{
gameObject.SetActive(true);
}
else
{
gameObject.SetActive(false);
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 34a52816a1c62984ea622bbf50849c68

View File

@@ -0,0 +1,14 @@
using TMPro;
using UnityEngine;
public class PlayerJoinCard : MonoBehaviour
{
public GameObject playerPreview;
public int playerNumber;
public TextMeshProUGUI playerNumberText;
void Start() // Sets player number
{
playerNumberText.text = playerNumber.ToString();
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: d858973e2b6054f4281e30a4db77c8fe

View File

@@ -0,0 +1,27 @@
using UnityEngine;
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
{
if (spawnPointIsInitialPosition)
{
spawnPoint = transform.position;
}
}
private void OnTriggerEnter2D(Collider2D other)
{
if (other.CompareTag(respawnTag))
{
if (TryGetComponent(out Damageable damageable))
{
damageable.Damage(9999f);
}
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 8ce1d588594ee416e9ab629d0b8c07dd

View File

@@ -0,0 +1,80 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
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()
{
InitializePlayer(player);
}
void Update() // Updates each player's health bar to display their current health
{
if (player == null || healthScript == null)
{
return;
}
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)
{
return;
}
healthScript = player.GetComponent<Damageable>();
if (healthScript == null)
{
return;
}
Initialize();
}
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;
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: c83fdf078a8b34ef7bb2829a58c1a545

View File

@@ -0,0 +1,35 @@
using System.Collections.Generic;
using TMPro;
using UnityEngine;
public class WinScreen : MonoBehaviour
{
public static WinScreen Instance;
public List<TextMeshProUGUI> playerTexts;
private void Awake() // Ensures only one instance of WinScreen exists
{
if (Instance == null)
{
Instance = this;
}
else
{
Destroy(this.gameObject);
}
}
public void ShowWinScreen(int player) // Triggers the win screen to appear
{
foreach (TextMeshProUGUI playerText in playerTexts)
{
playerText.text = "Player " + player;
if (playerText.color != Color.black)
{
playerText.color = GameManager.playerColors[player - 1];
}
}
GetComponent<Animator>().SetTrigger("win");
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: a15ab2e7ce7b04e19ae259be17276f08