using System;
+using System.Collections;
+using JetBrains.Annotations;
using UnityEngine;
+using UnityEngine.SceneManagement;
using UnityEngine.UI;
namespace Controllers
public Player player;
public Player enemy;
+
+ public bool freezeMotion;
private float startTime;
- [SerializeField] private float timeLimit;
+ public static float timeLimit;
/// <summary>
/// The single ball for the game.
/// </summary>
[SerializeField] public Ball ball;
+ [SerializeField] private float dribbleHeight;
+ [SerializeField] private float dribbleSpeed;
+ private Vector3 ballTarget;
+
+ [SerializeField] public SpriteRenderer[] trenchCoatSegments;
+
[Header("Spawn Points")]
[SerializeField] private SpawnPoints PlayerSpawnPoints;
[SerializeField] private SpawnPoints EnemySpawnPoints;
[Header("SFX")]
[SerializeField] public AudioSource dribbleSound;
+ [SerializeField] public AudioSource airhornSound;
[Header("UI")]
[SerializeField] private Text playerScoreText;
[SerializeField] private GameObject resultOverlay;
[SerializeField] private Text resultText;
+ [SerializeField] private GameObject actionsUI;
private void Awake()
{
private void Start()
{
+ startTime = Time.time - 2f;
+ freezeMotion = true;
+ StartCoroutine(FadeInCoat());
+ }
+
+ private IEnumerator FadeInCoat()
+ {
+ foreach (var trenchCoat in trenchCoatSegments)
+ trenchCoat.material.color = new Color(1, 1, 1, 0);
+
+ yield return new WaitForSeconds(1f);
+
+ for (float t = 0; t < 1f; t += Time.deltaTime / 1)
+ {
+ foreach (var trenchCoat in trenchCoatSegments)
+ trenchCoat.material.color = new Color(1, 1, 1, Mathf.Lerp(0, 1, t));
+ yield return null;
+ }
+
+ yield return new WaitForSeconds(1f);
+
+ freezeMotion = false;
startTime = Time.time;
+ ball.Rigidbody.velocity = new Vector2(0, 10f);
}
private void Update()
UpdateUI();
}
+ private void FixedUpdate()
+ {
+ if (player.HasBall || enemy.HasBall)
+ {
+ ball.transform.position = ballTarget - new Vector3(0, (Mathf.Sin(Time.time * dribbleSpeed) + 1f) * (ballTarget.y - 0.75f) * dribbleHeight, 0);
+ }
+ }
+
+ private bool gameover;
private void UpdateUI()
{
playerScoreText.text = $"{player.score}";
var remaining = TimeSpan.FromSeconds(Mathf.Clamp(remainingRaw, 0, float.MaxValue));
timerText.text = $"{remaining.Minutes:00}:{remaining.Seconds:00}";
- if (remainingRaw <= 0)
+ if (remainingRaw <= 0 && !gameover)
{
- resultOverlay.SetActive(true);
+ airhornSound.Play();
var outcome = player.score == enemy.score ? "TIE GAME" : player.score < enemy.score ? "AWAY TEAM WINS" : "HOME TEAM WINS";
- resultText.text = $"{outcome}\n{player.score}-{enemy.score}";
+ actionsUI.SetActive(true);
+ ShowModal($"{outcome}\n{player.score}-{enemy.score}");
+
+ freezeMotion = true;
+ gameover = true;
}
}
{
score += 2;
}
+
+ // They made a shot! Now respawn the players and give possession to the opposite player.
+ controller.Respawn(isEnemy ? Possession.Player : Possession.Enemy, $"{controller.player.score}-{controller.enemy.score}");
}
private State dribble => isEnemy ? State.EnemyDribble : State.PlayerDribble;
public void Move(Vector2 handPosition)
{
if (controller.state == (isEnemy ? State.EnemyDribble : State.PlayerDribble)) // Make sure they're dribbling.
- controller.ball.transform.position = handPosition; // TODO: Make this perform a dribbling motion, otherwise it looks like they're travelling.
+ controller.ballTarget = handPosition;
}
/// <summary>
lastShotPosition = playerTransform.position;
return true;
}
+
+ public void Foul(string reason)
+ {
+ // Give the other player the ball on a foul.
+ controller.Respawn(isEnemy ? Possession.Player : Possession.Enemy, reason);
+ }
}
internal void BallDropped()
state = State.Idle;
}
+ private void Respawn(Possession possession, string message)
+ {
+ BallDropped();
+
+ PlayerSpawnPoints.body.transform.position = new Vector3(PlayerSpawnPoints.character.position.x, PlayerSpawnPoints.character.position.y, PlayerSpawnPoints.body.transform.position.y);
+ PlayerSpawnPoints.body.GetComponent<Rigidbody2D>().velocity = Vector2.zero;
+
+ if (PlayerSpawnPoints.secondBody is { })
+ {
+ if (PlayerSpawnPoints.secondCharacter is { })
+ PlayerSpawnPoints.secondBody.transform.position = new Vector3(PlayerSpawnPoints.secondCharacter.position.x, PlayerSpawnPoints.secondCharacter.position.y, PlayerSpawnPoints.body.transform.position.y);
+ PlayerSpawnPoints.secondBody.GetComponent<Rigidbody2D>().velocity = Vector2.zero;
+ }
+
+ PlayerSpawnPoints.body.transform.localRotation = Quaternion.identity;
+ EnemySpawnPoints.body.transform.position = new Vector3(EnemySpawnPoints.character.position.x, EnemySpawnPoints.character.position.y, EnemySpawnPoints.body.transform.position.y);
+ ball.transform.position = possession switch
+ {
+ Possession.Player => new Vector3(PlayerSpawnPoints.ball.position.x, PlayerSpawnPoints.ball.position.y, ball.transform.position.y),
+ Possession.Enemy => new Vector3(EnemySpawnPoints.ball.position.x, EnemySpawnPoints.ball.position.y, ball.transform.position.y),
+ _ => ball.transform.position
+ };
+
+ // Set a cooldown so they can stop trying to wrangle the player while it respawns.
+ StartCoroutine(RespawnCooldown(possession, message));
+ }
+
+ private IEnumerator RespawnCooldown(Possession possession, string message)
+ {
+ // Show the new score.
+ var possessionText = possession == Possession.Player ? "HOME" : "AWAY";
+ ShowModal($"{message}\n{possessionText}'S POSSESSION");
+
+ freezeMotion = true;
+
+ yield return new WaitForSeconds(1f);
+
+ HideModal();
+
+ freezeMotion = false;
+ }
+
+ private void ShowModal(string text)
+ {
+ if (gameover) return;
+ resultOverlay.SetActive(true);
+ resultText.text = text;
+ }
+
+ private void HideModal()
+ {
+ if (gameover) return;
+ resultOverlay.SetActive(false);
+ }
+
+ public void Restart() => SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
+
+ public void MainMenu() => SceneManager.LoadScene("Menu");
+
internal enum State
{
Idle,
[Serializable]
private struct SpawnPoints
{
+ [SerializeField] internal Transform body;
+ [SerializeField] [CanBeNull] internal Transform secondBody;
[SerializeField] internal Transform ball;
[SerializeField] internal Transform character;
+ [SerializeField] [CanBeNull] internal Transform secondCharacter;
+ }
+
+ private enum Possession
+ {
+ Player,
+ Enemy
}
}