Merge branch 'main' into Dylan

This commit is contained in:
djkellerman
2025-04-08 10:20:36 -04:00
3529 changed files with 257260 additions and 11804 deletions

View File

@@ -1,41 +0,0 @@
using UnityEngine;
[RequireComponent(typeof(Rigidbody2D))]
[RequireComponent(typeof(Collider2D))]
[RequireComponent(typeof(RespawnOnTriggerEnter))]
public class Damageable : MonoBehaviour
{
public float force = 50f;
public float damage = 0f;
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.gameObject.CompareTag("Punch Hurtbox"))
{
if (GetComponent<Block>().blocking)
{
collision.gameObject.GetComponent<Damageable>().Damage(gameObject);
return;
}
Damage(collision.transform.parent.gameObject);
}
}
private void Recoil(GameObject damageSource)
{
GetComponent<Rigidbody2D>().AddForce(((transform.position - damageSource.transform.position).normalized + Vector3.up) * damage, ForceMode2D.Force);
//damageSource.transform.localScale *= 1.1f;
}
public void Damage(GameObject source)
{
damage += force;
Recoil(source);
}
public void ResetDamage()
{
damage = 0f;
//transform.localScale = Vector3.one;
}
}

8
Assets/Scripts/Game.meta Normal file
View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: cb25285062358bf41aa26cc8ca26b120
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

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,54 @@
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
{
try
{
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);
}
}
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,259 @@
using System.Collections;
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 obstacleCourseSpawnPosition;
public List<Vector2> hatSpawnPositions = new List<Vector2>();
public Canvas LeaderboardCanvas;
public Canvas TimerCanvas;
public GameObject hatObject;
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();
}
private void Update() // Continuously updates player hold times
{
if (gameOver) return;
if (gameMode == GameMode.keepAway)
{
foreach (var player in players)
{
float holdTime = GetPlayerHoldTime(player);
UpdatePlayerHoldTime(player, holdTime);
}
}
}
private float GetPlayerHoldTime(GameObject player)
{
UseItem useItem = player.GetComponent<UseItem>();
if (useItem != null)
{
return useItem.holdTime;
}
return 0f;
}
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;
player.GetComponent<Damageable>().lives = 0;
}
}
}
public void PlayerDied(Damageable player) // Handles player deaths for the respective gamemode
{
UseItem useItem = player.GetComponent<UseItem>(); // Drop the item the player is holding
if (useItem != null)
{
if (gameOver == false)
{
useItem.DropItem();
}
else
{
return; // Prevents winner from dropping the item if the game is over
}
}
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)
{
RespawnPlayer(player.gameObject);
}
}
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();
LeaderboardCanvas.gameObject.SetActive(false);
TimerCanvas.gameObject.SetActive(false);
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 = -1f;
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();
StartCoroutine(MoveHatToWinner(winner));
hatObject.SetActive(true);
hatObject.GetComponent<Collider2D>().enabled = true;
hatObject.GetComponent<Rigidbody2D>().bodyType = RigidbodyType2D.Dynamic;
}
}
if (gameMode == GameMode.obstacleCourse) // Player who reached the end first wins
{
GameObject winner = ObstacleCourse.playerWon;
print(winner.name + " is the winner!");
FindFirstObjectByType<PlayerCameraMovement>().WinScene(winner);
WinScreen.Instance.ShowWinScreen(players.IndexOf(winner) + 1);
FindFirstObjectByType<LifeDisplayManager>().HideLifeDisplay();
}
}
private IEnumerator MoveHatToWinner(GameObject winner)
{
while (!winner.GetComponent<UseItem>().IsHoldingItem())
{
hatObject.transform.position = winner.transform.position + Vector3.up * 3/2;
yield return null;
}
}
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) // Updates the player's hold time and leaderboard
{
bool shouldSort = false;
if (playerHoldTimes.ContainsKey(player))
{
if (holdTime > playerHoldTimes[player])
{
shouldSort = true;
}
playerHoldTimes[player] = holdTime;
}
else
{
playerHoldTimes.Add(player, holdTime);
shouldSort = true;
}
LeaderboardManager.Instance.UpdatePlayerHoldTimeText(player, holdTime);
if (shouldSort)
{
LeaderboardManager.Instance.UpdateLeaderboard();
}
}
}

View File

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

View File

@@ -0,0 +1,23 @@
using UnityEngine;
[ExecuteAlways]
public class GameManagerHelper : MonoBehaviour
{
public bool addHatPosition;
public bool addSpawnPosition;
private void Update()
{
if (addHatPosition)
{
addHatPosition = false;
GetComponent<GameManager>().hatSpawnPositions.Add(GameObject.Find("HELPER").transform.position);
}
if (addSpawnPosition)
{
addSpawnPosition = false;
GetComponent<GameManager>().spawnPosition = GameObject.Find("HELPER").transform.position;
}
}
}

View File

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

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,55 @@
using UnityEngine;
public class HatRespawn : MonoBehaviour
{
private float lastInteractionTime;
public const float respawnTime = 10f;
private bool isDropped;
public static bool canBePickedUp = true; // Flag to check if the hat can be picked up
void Start()
{
lastInteractionTime = Time.time;
isDropped = false;
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
{
if (isDropped && Time.time - lastInteractionTime > respawnTime)
{
RespawnHat();
}
}
void OnTriggerEnter2D(Collider2D collision) // Respawns the hat if it falls out of bounds
{
if (collision.gameObject.CompareTag("Platformer Hazard"))
{
RespawnHat();
}
}
public void Interact() // Updates the player interaction time
{
lastInteractionTime = Time.time;
isDropped = false;
}
public void OnHatDropped() // Resets the timer when the hat is dropped
{
lastInteractionTime = Time.time;
isDropped = true;
}
private void RespawnHat() // Respawns the hat at the designated spawn position
{
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
isDropped = false;
}
}

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,88 @@
using System.Collections.Generic;
using UnityEngine;
using TMPro;
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
{
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 (textComponent.name == "Position Text")
{
textComponent.text = "#" + (i + 1).ToString();
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;
}
}
}
}
}

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

@@ -6,7 +6,7 @@ public class RespawnOnTriggerEnter : MonoBehaviour
public bool spawnPointIsInitialPosition = false;
public string respawnTag;
private void Start()
private void Start() // Set the spawn point to the initial maps spawn point
{
if (spawnPointIsInitialPosition)
{
@@ -18,15 +18,10 @@ public class RespawnOnTriggerEnter : MonoBehaviour
{
if (other.CompareTag(respawnTag))
{
transform.position = spawnPoint;
if (TryGetComponent<Rigidbody2D>(out var rb))
if (TryGetComponent(out Damageable damageable))
{
rb.linearVelocity = Vector2.zero;
}
if (TryGetComponent<Damageable>(out var damageable))
{
damageable.ResetDamage();
damageable.Damage(9999f);
}
}
}
}
}

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

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e5d30ab9d20fa416dbd26b382141fe1d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,117 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Assertions.Must;
using UnityEngine.Playables;
using UnityEngine.SceneManagement;
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
{
if (Instance == null)
{
Instance = this;
}
else
{
Destroy(this.gameObject);
}
foreach (Playlist playlist in playlists)
{
foreach (string scene in playlist.trackScenes)
{
sceneToPlaylist.Add(scene, playlist);
}
}
}
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++)
{
AudioClip temp = randomized[i];
int randomIndex = Random.Range(i, randomized.Count);
randomized[i] = randomized[randomIndex];
randomized[randomIndex] = temp;
}
// Starts the music in the 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)
{
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);
}
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;
}
}
return "Title Screen";
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f701561e8549d464b9530bb56c6bfeb9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: -50
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,14 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Audio;
[System.Serializable]
public class Playlist
{
public string trackName;
public List<string> trackScenes;
public List<AudioClip> songs;
public float shuffleTime;
public float volume;
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7217d9ffc9dfd0c42bd093ee6d9c5db1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,16 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Audio;
[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;
public List<string> layerScenes;
public string triggerName;
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8a0b53b66b1b30143a043e7df151c454
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,366 @@
#if NO
using System.Collections;
using System.Collections.Generic;
using System.Data.Common;
using System.Runtime.CompilerServices;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.SceneManagement;
public class TrackManager : MonoBehaviour
{
public Playlist musicTrack;
public GameObject layerPrefab;
private List<TrackLayer> persistentLayers = new List<TrackLayer>();
private Scene currentScene;
private void Awake()
{
if (!GameManager.music)
{
Destroy(gameObject);
}
}
private void Start()
{
foreach (var layer in musicTrack.trackLayers)
{
if (layer.enableTrigger != TrackLayer.EnableTrigger.Scene)
{
persistentLayers.Add(layer);
}
}
currentScene = GetActiveSceneNotStatistics();
InitializeLayers();
UpdateLayers(musicTrack.trackLayers);
}
private void Update()
{
CheckForRestartability();
if (currentScene != GetActiveSceneNotStatistics())
{
currentScene = GetActiveSceneNotStatistics();
UpdateLayers(musicTrack.trackLayers);
}
if (persistentLayers.Count != 0) UpdateLayers(persistentLayers);
}
private void InitializeLayers()
{
foreach (TrackLayer layer in musicTrack.trackLayers)
{
AudioSource layerSource = Instantiate(layerPrefab, transform).GetComponent<AudioSource>();
layerSource.gameObject.name = layer.layerName;
layerSource.clip = layer.layerTrack;
layerSource.volume = 0;
try
{
layerSource.outputAudioMixerGroup = musicTrack.defaultMixer.FindMatchingGroups("Master/" + layer.layerName)[0];
}
catch
{
layerSource.outputAudioMixerGroup = musicTrack.defaultMixer.FindMatchingGroups("Master")[0];
}
layerSource.Play();
}
}
private void UpdateLayers(List<TrackLayer> layers)
{
if (StatisticsManager.PlayerPrefs.GetInt("settingMusic") == 1)
{
foreach (TrackLayer layer in layers)
{
DisableLayer(layer);
if (layer.enableTrigger == TrackLayer.EnableTrigger.Magnetism)
{
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());
}
}
else if (layer.enableTrigger == TrackLayer.EnableTrigger.Movement)
{
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());
}
}
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());
}
}
else if (layer.enableTrigger == TrackLayer.EnableTrigger.Goal)
{
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());
}
}
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;
}
}
}
}
}
}
private void CheckForRestartability()
{
bool restart = false;
for (int i = 0; i < transform.childCount; i++)
{
AudioSource child = transform.GetChild(i).GetComponent<AudioSource>();
if (child != null)
{
if (!child.isPlaying)
{
restart = true;
break;
}
}
}
if (!restart) return;
for (int i = 0; i < transform.childCount; i++)
{
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)
{
transform.Find(layer.layerName).GetComponent<Animator>().SetBool(parameter.name, false);
}
}
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 void Stop()
{
StartCoroutine(DestroyTrack());
}
public IEnumerator DestroyTrack()
{
yield return new WaitForSeconds(0.5f);
Destroy(gameObject);
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8f4092644348346419647bf86cb02003
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c9544c3283a37464f88c711b2bcd2f17
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -3,33 +3,32 @@ using UnityEngine;
[RequireComponent(typeof(Animator))]
public class AnimationPlayer : MonoBehaviour
{
public enum AnimationState { Idle, Run, Jump };
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()
private void Start() // Plays the specified animation clip
{
animator = GetComponent<Animator>();
animator.Play(clip.name);
}
private void LateUpdate()
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);
}
public void SetState(AnimationState state)
public void SetState(AnimationState state) // Sets the animation state
{
this.state = state;
}
public void Punch()
public void Punch() // Triggers punch animation
{
animator.SetTrigger("punch");
}

View File

@@ -0,0 +1,57 @@
using UnityEngine;
using UnityEngine.InputSystem;
[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()
{
actions = GetComponent<PlayerInput>().actions;
}
private void Update() // Player blocks when "block" is pressed
{
InputAction blockAction = actions.FindAction("Block");
if (blockAction.ReadValue<float>() == 1f)
{
if (!blocking)
{
blockPressTime = Time.time; // Start parry timer
}
blocking = true;
}
else
{
if (blocking) // Successful parry if blocked in time
{
float pressDuration = Time.time - blockPressTime;
if (pressDuration <= parryThreshold)
{
Parry();
}
else
{
isParrying = false;
}
}
blocking = false;
}
GetComponent<AnimationPlayer>().block = blocking;
}
private void Parry()
{
isParrying = true;
}
public bool IsParrying()
{
return isParrying;
}
}

View File

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

View File

@@ -0,0 +1,136 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(Rigidbody2D))]
[RequireComponent(typeof(Collider2D))]
[RequireComponent(typeof(RespawnOnTriggerEnter))]
public class Damageable : MonoBehaviour
{
public float force = 50f; // Force applied when hit
public float damage = 0f;
public float maxDamage = 1000f; // Set max health
public int lives = 0;
private Animator animator;
public bool damageSelfDebug = false;
public bool dying = false;
public event System.Action<GameObject> OnPlayerPunched;
private void Start()
{
animator = GetComponent<Animator>();
}
private void Update()
{
if (damageSelfDebug)
{
damageSelfDebug = false;
Damage(gameObject);
}
}
private void OnTriggerEnter2D(Collider2D collision) // Calls Damage method when player is hit
{
if (collision.gameObject.CompareTag("Punch Hurtbox"))
{
Damage(collision.transform.parent.gameObject);
}
}
private void Damage(GameObject damageSource) // Damages player
{
if (dying || damageSource.CompareTag("Hat")) return; // Exclude hat from taking damage
float actualForce = damageSource.GetComponent<Damageable>().force;
Block blockComponent = GetComponent<Block>();
GetComponentInChildren<UseItem>().DropItem(); // Drops hat if held
if (blockComponent != null && blockComponent.blocking)
{
if (blockComponent.IsParrying()) // Player receives damage if punching a parrying player
{
damageSource.GetComponent<Damageable>().SuccessfulParry(gameObject, actualForce);
return;
}
else // Player does less damage if punching a blocking player
{
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
{
GetComponent<Rigidbody2D>().AddForce(((transform.position - damageSource.transform.position).normalized + Vector3.up * 2) * actualForce * (1 + (damage / maxDamage) * 3), ForceMode2D.Force);
}
damage += actualForce;
damage = Mathf.Clamp(damage, 0f, maxDamage);
if (damage >= maxDamage)
{
Die();
}
}
public void Damage(float damage) // Adds damage to player when hit
{
if (GameManager.Instance.gameOver) return; // Prevent damage after game is over
this.damage += damage;
if (damage >= maxDamage)
{
Die();
}
}
private void SuccessfulParry(GameObject damageSource, float force)
{
GetComponent<Rigidbody2D>().AddForce(((transform.position - damageSource.transform.position).normalized + Vector3.up * 2) * force, ForceMode2D.Force);
damage += force;
damage = Mathf.Clamp(damage, 0f, maxDamage);
if (damage >= maxDamage)
{
Die();
}
}
private void Die() // Triggers death animation and sets player to dying state
{
if (GameManager.Instance != null && !GameManager.Instance.gameOver) // Prevent death after game is over
{
UseItem useItem = GetComponent<UseItem>();
if (useItem != null)
{
useItem.DropItem(); // Ensure the player drops the item before the death animation
}
animator.SetBool("die", true);
dying = true;
}
}
public void HandleDeath() // Removes player from dying state after respawn
{
GameManager.Instance.PlayerDied(this);
animator.SetBool("die", false);
dying = false;
}
public void Respawn() // Respawns player to the spawnPosition and resets damage/health bar
{
transform.position = GameManager.Instance.spawnPosition;
if (TryGetComponent<Rigidbody2D>(out var rb))
{
rb.linearVelocity = Vector2.zero;
rb.angularVelocity = 0f;
}
if (TryGetComponent<Damageable>(out var damageable))
{
damageable.ResetDamage();
}
}
public void ResetDamage()
{
damage = 0f;
}
}

View File

@@ -0,0 +1,86 @@
using System.Collections.Generic;
using UnityEngine;
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()
{
start = transform.position;
}
private void Update()
{
if (winScene) // If the game is over, the camera will follow the player that won
{
if (playerThatWon == null || !playerThatWon.activeInHierarchy)
{
playerThatWon = FindWinner();
}
if (playerThatWon != null)
{
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)
{
transform.position = new Vector3(transform.position.x, lowerBound, transform.position.z);
}
}
return;
}
// 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;
}
}

View File

@@ -0,0 +1,97 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
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()
{
Init();
}
private void Start()
{
GetComponent<PlayerInputManager>().onPlayerJoined += OnPlayerJoined;
GetComponent<PlayerInputManager>().onPlayerLeft += OnPlayerLeft;
}
private void OnPlayerJoined(PlayerInput playerInput) // Adds a player when they join
{
if (gameStarted)
{
Destroy(playerInput.gameObject);
return;
}
Debug.Log("Player joined");
DontDestroyOnLoad(playerInput.gameObject);
PlayerJoinCard card = PlayerCardCreator.Instance.CreateCard();
card.playerNumber = GameManager.players.Count + 1;
cards.Add(card);
GameManager.players.Add(playerInput.gameObject);
Colorize(GameManager.players.Count - 1);
}
private void OnPlayerLeft(PlayerInput playerInput) // Removes the player if they leave
{
Destroy(playerInput.gameObject);
GameManager.players.Remove(playerInput.gameObject);
Debug.Log("Player left");
}
private void Init()
{
if (Instance == null)
{
Instance = this;
}
else
{
Destroy(this.gameObject);
}
}
public void StartGame() // Allows game to start after a player has joined
{
if (GameManager.players.Count == 0)
{
return;
}
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 _))
{
obj.GetComponent<SpriteRenderer>().color = color;
}
foreach (Transform child in obj.transform)
{
if (child.TryGetComponent<SpriteRenderer>(out _))
{
ApplyColor(child.gameObject, color);
}
}
}
}

View File

@@ -1,8 +1,8 @@
using System.Collections;
using TMPro;
using Unity.IO.LowLevel.Unsafe;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.Tilemaps;
[RequireComponent(typeof(Rigidbody2D))]
[RequireComponent(typeof(BoxCollider2D))]
@@ -18,19 +18,20 @@ public class PlayerMovement : MonoBehaviour
[Header("Movement")]
public float walkSpeed;
public float walkSpeedFactor = 1f;
public float maxSpeed = 5f;
public float slowdownMultiplier = 10f;
public float walkSpeedFactor = 1f; // Sets walk speed
public float maxSpeed = 5f; // Sets max speed
public float maxSpeedOverride;
public float slowdownMultiplier = 10f; // Sets slow walk speed
public float virtualAxisX;
public float virtualButtonJump;
public float virtualButtonJumpLastFrame;
public float turnaroundMultiplier = 2;
public float turnaroundMultiplier = 2; // Sets speed when turning around
public float walkSmooth;
public float secondsToFullSpeed;
public float jumpSpeed;
public float coyoteTime;
public float jumpLenience;
public float timeUnableToBeDeclaredNotJumping = 0.1f;
public float timeUnableToBeDeclaredNotJumping = 0.1f; // Jump threshold
public float groundCheckDistance;
private Rigidbody2D body;
@@ -38,22 +39,20 @@ public class PlayerMovement : MonoBehaviour
private PlayerInput input;
private AnimationPlayer animationPlayer;
private Punch punch;
private Damageable damageable;
private bool jumpInputStillValid = false;
private float lastTimeJumpPressed;
private bool canBeDeclaredNotJumping = true;
private bool jumpPhysics;
private bool jumping;
private float lastTimeJumpPressed;
private float lastTimeOnGround;
private Vector3 positionLastFrame;
void Start()
void Start() // Sets up player components
{
maxSpeedOverride = maxSpeed;
GetComponent<RespawnOnTriggerEnter>().spawnPoint = transform.position;
body = GetComponent<Rigidbody2D>();
@@ -61,12 +60,16 @@ public class PlayerMovement : MonoBehaviour
input = GetComponent<PlayerInput>();
animationPlayer = GetComponent<AnimationPlayer>();
punch = GetComponent<Punch>();
damageable = GetComponent<Damageable>();
playerText.text = input.playerIndex.ToString();
}
private void Update()
private void Update() // Updates player movement
{
if (GameManager.Instance != null && GameManager.Instance.gameOver) maxSpeed = 1f;
if (damageable.dying) return;
Jump();
UpdateVirtualAxis();
@@ -81,19 +84,19 @@ public class PlayerMovement : MonoBehaviour
Land();
}
private void LateUpdate()
private void LateUpdate()
{
Animate();
}
private void Animate()
private void Animate() // Sets player animation
{
if (!IsPhysicallyGrounded())
animationPlayer.SetState(AnimationPlayer.AnimationState.Jump);
else
{
if (Mathf.Abs(body.linearVelocityX) >= 0.1f)
animationPlayer.SetState(AnimationPlayer.AnimationState.Run);
if (Mathf.Abs(body.linearVelocityX) >= 0.05f)
animationPlayer.SetState(GameManager.Instance.gameOver ? AnimationPlayer.AnimationState.Walk : AnimationPlayer.AnimationState.Run);
else
animationPlayer.SetState(AnimationPlayer.AnimationState.Idle);
}
@@ -104,7 +107,7 @@ public class PlayerMovement : MonoBehaviour
animationPlayer.backwards = false;
}
private void Land()
private void Land() // Stops jumping when player lands
{
if (body.linearVelocity.y >= 0f) return;
@@ -117,10 +120,8 @@ public class PlayerMovement : MonoBehaviour
}
}
private void Jump()
private void Jump() // Player jumps when 'jump' is pressed
{
//if (!punch.cancelable) return;
if (virtualButtonJumpLastFrame == 1f)
{
jumpInputStillValid = true;
@@ -138,12 +139,19 @@ public class PlayerMovement : MonoBehaviour
}
}
private void JumpPhysics()
private void JumpPhysics() // Applies jump physics
{
if (jumpPhysics)
{
if (body.linearVelocity.y < 0 || !IsPhysicallyGrounded()) body.linearVelocity = new Vector2(body.linearVelocity.x, 0);
body.AddForce(Vector2.up * jumpSpeed, ForceMode2D.Impulse);
if (!GetComponent<Block>().blocking)
{
if (body.linearVelocity.y < 0 || !IsPhysicallyGrounded()) body.linearVelocity = new Vector2(body.linearVelocity.x, 0);
body.AddForce(Vector2.up * jumpSpeed, ForceMode2D.Impulse);
if (Mathf.Abs(body.linearVelocityX) > maxSpeed)
{
body.linearVelocity = new Vector2(Mathf.Sign(body.linearVelocityX) * maxSpeed, body.linearVelocity.y);
}
}
jumpPhysics = false;
}
@@ -151,22 +159,26 @@ public class PlayerMovement : MonoBehaviour
body.AddForce(Vector2.down * jumpSpeed);
}
private IEnumerator NotJumpingDelay()
private IEnumerator NotJumpingDelay() // Sets jump threshold
{
canBeDeclaredNotJumping = false;
yield return new WaitUntil(() => !IsBasicallyGrounded());
canBeDeclaredNotJumping = true;
}
private void HorizontalMovement()
private void HorizontalMovement() // Sets player horizontal movement
{
//if (!punch.cancelable) return;
float temporaryMax = IsPhysicallyGrounded() ? maxSpeedOverride : Mathf.Infinity;
float temporarySlowdown = IsPhysicallyGrounded() ? slowdownMultiplier : 1;
body.AddForce(new Vector2(virtualAxisX * walkSpeed * walkSpeedFactor, 0), ForceMode2D.Force);
if (Mathf.Abs(body.linearVelocityX) >= maxSpeed)
if (!GetComponent<Block>().blocking && (Mathf.Abs(body.linearVelocityX) <= maxSpeed || Mathf.Sign(body.linearVelocityX) != Mathf.Sign(virtualAxisX)))
{
body.AddForce(new Vector2(-Mathf.Sign(body.linearVelocityX) * (Mathf.Abs(body.linearVelocityX) - maxSpeed) * slowdownMultiplier, 0));
body.AddForce(new Vector2(virtualAxisX * walkSpeed * walkSpeedFactor, 0), ForceMode2D.Force);
}
if (Mathf.Abs(body.linearVelocityX) >= temporaryMax)
{
body.AddForce(new Vector2(-Mathf.Sign(body.linearVelocityX) * (Mathf.Abs(body.linearVelocityX) - temporaryMax) * temporarySlowdown, 0));
}
if (transform.position == positionLastFrame && (input.actions.FindAction("Move").ReadValue<Vector2>().x == 0))
@@ -174,10 +186,15 @@ public class PlayerMovement : MonoBehaviour
virtualAxisX = 0;
}
if (GetComponent<Block>().blocking)
{
body.AddForce(new Vector2(-body.linearVelocityX * 0.8f, 0), ForceMode2D.Force);
}
positionLastFrame = transform.position;
}
private void UpdateVirtualAxis()
private void UpdateVirtualAxis() // Updates virtual axis
{
virtualButtonJump = input.actions.FindAction("Action").ReadValue<float>();
virtualButtonJumpLastFrame = input.actions.FindAction("Action").WasPressedThisFrame() ? 1 : 0;
@@ -186,7 +203,7 @@ public class PlayerMovement : MonoBehaviour
return;
}
public bool IsBasicallyGrounded()
public bool IsBasicallyGrounded() // Checks if player is on land within a threshold
{
if (IsPhysicallyGrounded())
{
@@ -201,7 +218,7 @@ public class PlayerMovement : MonoBehaviour
return false;
}
public bool IsPhysicallyGrounded()
public bool IsPhysicallyGrounded() // Checks if player is on land
{
RaycastHit2D leftCheck = Physics2D.Raycast(GetPointInBoxCollider(collide, -1, -1), Vector2.down, groundCheckDistance, ground);
RaycastHit2D rightCheck = Physics2D.Raycast(GetPointInBoxCollider(collide, 1, -1), Vector2.down, groundCheckDistance, ground);
@@ -215,7 +232,7 @@ public class PlayerMovement : MonoBehaviour
return false;
}
public Vector2 GetPointInBoxCollider(BoxCollider2D boxCollider2D, float horizontal, float vertical)
public Vector2 GetPointInBoxCollider(BoxCollider2D boxCollider2D, float horizontal, float vertical)
{
return new Vector2
(
@@ -224,7 +241,7 @@ public class PlayerMovement : MonoBehaviour
);
}
public void StopVelocity()
public void StopVelocity() // Stops inertia when landed
{
if (IsPhysicallyGrounded()) body.linearVelocity = Vector2.zero;
}

View File

@@ -0,0 +1,59 @@
using UnityEngine;
using UnityEngine.InputSystem;
[RequireComponent(typeof(PlayerInput))]
[RequireComponent(typeof(AnimationPlayer))]
public class Punch : MonoBehaviour
{
public bool cancelable = true;
[SerializeField] private BoxCollider2D hurtbox;
InputActionAsset actions;
private void Start()
{
actions = GetComponent<PlayerInput>().actions;
}
private void Update() // Executes punch when 'punch' is pressed
{
if (actions.FindAction("Punch").WasPressedThisFrame())
{
if (!cancelable) return;
ExecutePunch();
}
}
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;
}
}

View File

@@ -0,0 +1,37 @@
using UnityEngine;
public class TeleportPlatform : MonoBehaviour
{
public Vector2 teleportPoint;
public string teleportTag;
public string playerTag = "Player";
public bool isPlatform = true;
private void OnTriggerEnter2D(Collider2D collision)
{
if (!isPlatform)
{
// Teleports the platform
if (collision.CompareTag(teleportTag))
{
transform.position = teleportPoint;
if (TryGetComponent<Rigidbody2D>(out var rb))
{
rb.linearVelocity = Vector2.zero;
}
}
}
else
{
// Teleports the player
if (collision.CompareTag(playerTag))
{
collision.transform.position = teleportPoint;
if (collision.TryGetComponent<Rigidbody2D>(out var rb))
{
rb.linearVelocity = Vector2.zero;
}
}
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 182a9e619d36aeb4592fb532d3c9db55

View File

@@ -0,0 +1,97 @@
using System.Collections;
using UnityEngine;
public class UseItem : MonoBehaviour
{
[SerializeField] private string itemTag;
private GameObject heldItem;
private bool isHoldingItem = false;
private float holdStartTime;
public float holdTime;
private Damageable damageable;
[SerializeField] public Transform head;
private void Start()
{
damageable = GetComponent<Damageable>();
}
void Update()
{
if (isHoldingItem)
{
// Keeps hat on the player's head
heldItem.transform.localPosition = Vector3.zero;
if (GameManager.gameMode == GameManager.GameMode.keepAway)
{
// Adds time to the player's leaderboard standing
holdTime += Time.deltaTime;
GameManager.Instance.UpdatePlayerHoldTime(gameObject, holdTime);
}
}
}
private void OnCollisionEnter2D(Collision2D collision) // Player automatically picks up hat when touching it
{
if (collision.gameObject.CompareTag("Hat") && !isHoldingItem && !damageable.dying)
{
PickUpItem(collision.gameObject);
}
}
private void PickUpItem(GameObject item) // Player picks up hat and starts hold counter
{
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
heldItem = item;
isHoldingItem = true;
holdStartTime = Time.time;
item.GetComponent<Collider2D>().enabled = false;
item.GetComponent<Rigidbody2D>().bodyType = RigidbodyType2D.Static;
item.GetComponent<HatRespawn>().Interact();
item.transform.parent = head;
item.transform.localRotation = Quaternion.identity;
item.transform.localPosition = Vector3.zero;
if (!GameManager.playerHoldTimes.ContainsKey(gameObject))
{
GameManager.playerHoldTimes[gameObject] = 0f;
}
GameManager.Instance.StopCoroutine("MoveHatToWinner");
}
public void DropItem() // Player drops hat when hit
{
if (GameManager.Instance.gameOver) return; // Prevent dropping items if the game is over
if (isHoldingItem)
{
heldItem.GetComponent<Collider2D>().enabled = true;
HatRespawn.canBePickedUp = false;
StartCoroutine(WaitForInteractability());
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(-5, 5), ForceMode2D.Impulse);
heldItem.GetComponent<HatRespawn>().OnHatDropped();
heldItem.transform.parent = GameManager.Instance.transform;
heldItem = null;
isHoldingItem = false;
if (GameManager.playerHoldTimes.ContainsKey(gameObject))
{
GameManager.playerHoldTimes.Remove(gameObject);
}
}
}
private IEnumerator WaitForInteractability()
{
yield return new WaitForSeconds(0.1f);
HatRespawn.canBePickedUp = true;
}
public bool IsHoldingItem()
{
return isHoldingItem;
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 861b3bde023ca7f48a72c48ef9ee25d2

View File

@@ -1,33 +0,0 @@
using System.Collections.Generic;
using UnityEngine;
public class PlayerCameraMovement : MonoBehaviour
{
private Vector3 start;
private Vector3 target;
public float weight;
public float speed;
private void Start()
{
start = transform.position;
}
private void Update()
{
List<GameObject> players = PlayerManager.Instance.players;
if (players.Count == 0) return;
Vector3 playerAverage = Vector3.zero;
foreach (GameObject player in players)
{
playerAverage += player.transform.position;
}
playerAverage /= players.Count;
target = start * weight + playerAverage * (1 - weight);
transform.position = Vector3.Lerp(transform.position, target, speed * Time.deltaTime);
transform.position = new Vector3(transform.position.x, transform.position.y, -10);
}
}

View File

@@ -1,54 +0,0 @@
using System.Collections.Generic;
using NUnit.Framework.Constraints;
using UnityEngine;
using UnityEngine.InputSystem;
public class PlayerManager : MonoBehaviour
{
public static PlayerManager Instance;
public List<GameObject> players;
[SerializeField] private InputActionAsset playerActions;
private Vector2 spawnPosition;
private void Awake()
{
Init();
}
private void Start()
{
GetComponent<PlayerInputManager>().onPlayerJoined += OnPlayerJoined;
GetComponent<PlayerInputManager>().onPlayerLeft += OnPlayerLeft;
}
private void OnPlayerJoined(PlayerInput playerInput)
{
playerInput.transform.position = spawnPosition;
players.Add(playerInput.gameObject);
print("Player joined");
}
private void OnPlayerLeft(PlayerInput playerInput)
{
Destroy(playerInput.gameObject);
players.Remove(playerInput.gameObject);
print("Player left");
}
private void Init()
{
if (Instance == null)
{
Instance = this;
}
else
{
print("A PlayerManager already exists.");
Destroy(this.gameObject);
}
spawnPosition = transform.position;
}
}

View File

@@ -1,49 +0,0 @@
using UnityEngine;
using UnityEngine.InputSystem;
[RequireComponent(typeof(PlayerMovement))]
[RequireComponent(typeof(PlayerInput))]
[RequireComponent(typeof(AnimationPlayer))]
public class Punch : MonoBehaviour
{
public bool cancelable = true;
[SerializeField] private BoxCollider2D hurtbox;
InputActionAsset actions;
private void Start()
{
actions = GetComponent<PlayerInput>().actions;
}
private void Update()
{
if (actions.FindAction("Punch").ReadValue<float>() == 1f)
{
//if (!cancelable) return;
GetComponent<AnimationPlayer>().Punch();
DisableCancellation();
}
}
public void EnableHurtbox()
{
hurtbox.enabled = true;
}
public void DisableHurtbox()
{
hurtbox.enabled = false;
}
public void DisableCancellation()
{
cancelable = false;
}
public void EnableCancellation()
{
cancelable = true;
}
}