using TMPro; using UnityEngine; public class PongGame : MonoBehaviour { [Header("Objects")] [SerializeField] private Transform _player1; [SerializeField] private Transform _player2; [SerializeField] private Transform _ball; [SerializeField] private TMP_Text _scoreText; [Header("Field Bounds")] [SerializeField] private float _topY = 4f; [SerializeField] private float _bottomY = -4f; [SerializeField] private float _leftX = -8f; [SerializeField] private float _rightX = 8f; [Header("Ball")] [SerializeField] private float _ballSpeed = 8f; [SerializeField, Range(10f, 80f)] private float _maxBounceAngleDeg = 60f; [Header("Player 1 (Keyboard)")] [SerializeField] private KeyCode _player1Up = KeyCode.W; [SerializeField] private KeyCode _player1Down = KeyCode.S; [SerializeField] private float _player1Speed = 10f; [Header("Player 2 (Mouse)")] [SerializeField] private Camera _camera; private Vector2 _ballDirection; private float _player1HalfHeight; private float _player2HalfHeight; private float _player1HalfWidth; private float _player2HalfWidth; private float _ballHalfSize; private int _player1Score; private int _player2Score; private void Start() { if (_camera == null) _camera = Camera.main; if (_scoreText == null) { GameObject scoreObject = GameObject.Find("Score"); if (scoreObject != null) _scoreText = scoreObject.GetComponent(); } _player1HalfHeight = GetHalfExtents(_player1).y; _player2HalfHeight = GetHalfExtents(_player2).y; _player1HalfWidth = GetHalfExtents(_player1).x; _player2HalfWidth = GetHalfExtents(_player2).x; _ballHalfSize = GetHalfExtents(_ball).x; UpdateScoreText(); ResetBall(); } private void Update() { HandlePlayer1(); HandlePlayer2(); MoveBall(); } private void HandlePlayer1() { float input = 0f; if (Input.GetKey(_player1Up)) input += 1f; if (Input.GetKey(_player1Down)) input -= 1f; Vector3 position = _player1.position; position.y += input * _player1Speed * Time.deltaTime; position.y = Mathf.Clamp(position.y, _bottomY + _player1HalfHeight, _topY - _player1HalfHeight); _player1.position = position; } private void HandlePlayer2() { Vector3 mouseScreen = Input.mousePosition; mouseScreen.z = Mathf.Abs(_camera.transform.position.z - _player2.position.z); Vector3 mouseWorld = _camera.ScreenToWorldPoint(mouseScreen); Vector3 position = _player2.position; position.y = Mathf.Clamp(mouseWorld.y, _bottomY + _player2HalfHeight, _topY - _player2HalfHeight); _player2.position = position; } private void MoveBall() { Vector3 newPosition = _ball.position + (Vector3)(_ballDirection * _ballSpeed * Time.deltaTime); if (newPosition.y + _ballHalfSize > _topY && _ballDirection.y > 0f) { newPosition.y = _topY - _ballHalfSize; _ballDirection.y = -_ballDirection.y; } else if (newPosition.y - _ballHalfSize < _bottomY && _ballDirection.y < 0f) { newPosition.y = _bottomY + _ballHalfSize; _ballDirection.y = -_ballDirection.y; } if (_ballDirection.x < 0f && Intersects(newPosition, _player1, _player1HalfWidth, _player1HalfHeight)) { BounceOffPaddle(_player1, _player1HalfWidth, _player1HalfHeight, 1f, ref newPosition); } else if (_ballDirection.x > 0f && Intersects(newPosition, _player2, _player2HalfWidth, _player2HalfHeight)) { BounceOffPaddle(_player2, _player2HalfWidth, _player2HalfHeight, -1f, ref newPosition); } if (newPosition.x < _leftX) { _player2Score++; UpdateScoreText(); ResetBall(); return; } if (newPosition.x > _rightX) { _player1Score++; UpdateScoreText(); ResetBall(); return; } _ball.position = newPosition; } private void UpdateScoreText() { if (_scoreText != null) _scoreText.text = $"{_player1Score} : {_player2Score}"; } private bool Intersects(Vector3 ballPos, Transform paddle, float paddleHalfWidth, float paddleHalfHeight) { Vector3 paddlePos = paddle.position; return Mathf.Abs(ballPos.x - paddlePos.x) <= paddleHalfWidth + _ballHalfSize && Mathf.Abs(ballPos.y - paddlePos.y) <= paddleHalfHeight + _ballHalfSize; } private void BounceOffPaddle(Transform paddle, float paddleHalfWidth, float paddleHalfHeight, float directionX, ref Vector3 ballPosition) { float relative = Mathf.Clamp((ballPosition.y - paddle.position.y) / paddleHalfHeight, -1f, 1f); float angle = relative * _maxBounceAngleDeg * Mathf.Deg2Rad; _ballDirection = new Vector2(directionX * Mathf.Cos(angle), Mathf.Sin(angle)).normalized; ballPosition.x = paddle.position.x + directionX * (paddleHalfWidth + _ballHalfSize); } private void ResetBall() { _ball.position = new Vector3(0f, 0f, _ball.position.z); float angle = Random.Range(-_maxBounceAngleDeg, _maxBounceAngleDeg) * Mathf.Deg2Rad; float directionX = Random.value < 0.5f ? -1f : 1f; _ballDirection = new Vector2(directionX * Mathf.Cos(angle), Mathf.Sin(angle)).normalized; } private Vector2 GetHalfExtents(Transform target) { if (target == null) return new Vector2(0.5f, 0.5f); Renderer renderer = target.GetComponent(); if (renderer != null) return renderer.bounds.extents; Collider2D collider2D = target.GetComponent(); if (collider2D != null) return collider2D.bounds.extents; return target.localScale * 0.5f; } }