using System; using UnityEngine; using UnityEngine.UI; namespace Controllers { /// /// The controller for the basketball-game logic. /// public class GameController : MonoBehaviour { private State state = State.JumpBall; // A basketball game always starts with a jump ball. internal Animator BallAnimation; public Player player; public Player enemy; private float startTime; [SerializeField] private float timeLimit; /// /// The single ball for the game. /// [SerializeField] public Ball ball; [Header("Hoops")] [SerializeField] public Hoop PlayerHoop; [SerializeField] public Hoop EnemyHoop; [Header("SFX")] [SerializeField] public AudioSource dribbleSound; [Header("UI")] [SerializeField] private Text playerScoreText; [SerializeField] private Text enemyScoreText; [SerializeField] private Text timerText; private void Awake() { player = new Player { isEnemy = false, controller = this }; enemy = new Player { isEnemy = true, controller = this }; ball.controller = this; PlayerHoop.controller = this; EnemyHoop.controller = this; BallAnimation = ball.GetComponentInChildren(); } private void Start() { startTime = Time.time; } private void Update() { UpdateUI(); } private void UpdateUI() { playerScoreText.text = $"{player.score}"; enemyScoreText.text = $"{enemy.score}"; var remaining = TimeSpan.FromSeconds(timeLimit - (Time.time - startTime)); timerText.text = $"{remaining.Minutes:00}:{remaining.Seconds:00}"; } public struct Player { /// /// Whether this player is the AI-enemy. /// internal bool isEnemy; /// /// A back-reference to the containing GameController. /// internal GameController controller; internal int score; private Vector2 lastShotPosition; public void Score(Vector2 Rim) { if (Vector2.Distance(lastShotPosition, Rim) >= 10) { score += 3; } else { score += 2; } } private State dribble => isEnemy ? State.EnemyDribble : State.PlayerDribble; private State shoot => isEnemy ? State.EnemyShoot : State.PlayerShoot; private Hoop hoop => isEnemy ? controller.EnemyHoop : controller.PlayerHoop; public bool HasBall => controller.state == dribble; /// /// When dribbling, move the ball with the player. /// /// The position of the hand dribbling the ball. 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. } /// /// Grab the ball if possible given the current game state. /// /// The position of the hand to attempt grabbing from. /// Whether or not the ball was able to be picked up. public bool GrabBall(Vector2 handPosition) { // Don't allow the ball to be picked up if someone shot it. Also don't try picking it up if we're already holding it. if (controller.state == shoot || controller.state == dribble) return false; // Make sure its within their grab area. if (Vector2.Distance(controller.ball.transform.position, handPosition) >= 1f) return false; controller.state = dribble; controller.BallAnimation.enabled = false; controller.dribbleSound.Play(); Move(handPosition); controller.ball.Rigidbody.bodyType = RigidbodyType2D.Kinematic; return true; } /// /// Shoot the ball if possible. /// /// /// /// Whether or not the ball was shot public bool Shoot(Transform playerTransform, float time) { if (controller.state != dribble) return false; // We must be dribbling the ball to shoot it. controller.BallAnimation.enabled = true; controller.dribbleSound.Stop(); controller.state = shoot; controller.ball.Rigidbody.bodyType = RigidbodyType2D.Dynamic; controller.ball.Shoot(hoop.transform.position, time); lastShotPosition = playerTransform.position; return true; } } internal void BallDropped() { BallAnimation.enabled = true; dribbleSound.Stop(); ball.Rigidbody.bodyType = RigidbodyType2D.Dynamic; state = State.Idle; } internal enum State { Idle, JumpBall, PlayerDribble, PlayerShoot, EnemyDribble, EnemyShoot, } } internal static class GameControllerStateExtensions { internal static bool IsShot(this GameController.State state) { return state == GameController.State.EnemyShoot || state == GameController.State.PlayerShoot; } internal static bool IsDribble(this GameController.State state) { return state == GameController.State.EnemyDribble || state == GameController.State.PlayerDribble; } } }