Pong Game Attempt

Hi,

After completing the block breaker section of the course, I thought of making a clone of the pong game to practice my skills. Most of the game is done, however I am having a problem with the movement of the AI paddle.

The first round of the game is going fine and the AI paddle follows the ball. But once that round is over, the AI paddle is just moves a little and doesn’t follow the ball anymore. I couldn’t figure out what is happening, everything seems to be fine.

Here is a gameplay of the first round

As we can see, in this round everything works fine.

However in the subsequent rounds, the AI paddle doesn’t seem to move at all to follow the ball (it just moves a little bit in its y position from 6.08 to 6.20)

Here is a demo of that

As we can see the AI paddle doesn’t follow the ball after the first round. I couldn’t figure out why.

Here are the scripts

Ball GameObject -> Ball.cs

Player Paddle GameObject -> Paddle.cs

Computer Paddle GameObject -> AIPaddle.cs

Game Manager -> GameManager.cs

Ball.cs

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Ball : MonoBehaviour
{
    //Configuration Parameters
    [SerializeField] float ballSpeed;
 
    //Cached References
    private Rigidbody2D ballRb;
    private GameManager gameManager;

    //State Variables
    private float ballXDirection = 12f;
    private float ballYDirection = 6.25f;
    private float delayTime = 2f;

    // Start is called before the first frame update
    void Start()
    {
        gameManager = FindObjectOfType<GameManager>();
        StartCoroutine(WaitBeforeGameStart());
    }

    IEnumerator WaitBeforeGameStart()
    {
        yield return new WaitForSeconds(delayTime);
        StartGame();
    }

    private void StartGame()
    {
        gameManager = GetGameManagerInstance();
        gameManager.SetCurrentGameStatus(true);

        ballRb = GetComponent<Rigidbody2D>();
        bool isPlayerTurn = gameManager.IsPlayerTurn();

        SetBallVelocityVector(ballRb, isPlayerTurn);
    }

    private GameManager GetGameManagerInstance()
    {
        if (gameManager == null)
        {
            gameManager = FindObjectOfType<GameManager>();
        }

        return gameManager;
    }

    private void OnCollisionEnter2D(Collision2D collision)
    {
        gameManager = GetGameManagerInstance();
        gameManager.PlayWallHitSound();
    }

    private void OnTriggerEnter2D(Collider2D collision)
    {
        Destroy(gameObject);

        gameManager = GetGameManagerInstance();

        gameManager.SetCurrentGameStatus(false);
        gameManager.PlayRoundOverSound();

        if(collision.gameObject.CompareTag("AI"))
        {
            gameManager.UpdateScore(true);
        } 
        else
        {
            gameManager.UpdateScore(false);
        }
    }
    public Vector2 GetCurrentBallVelocityVector(Rigidbody2D ballRb)
    {
        return ballRb.velocity;
    }
    public void SetBallVelocityVector(Rigidbody2D ballRb, bool isPlayerTurn)
    {
        if(isPlayerTurn)
        {
            ballRb.velocity = new Vector2(ballXDirection, ballYDirection) * Time.deltaTime * ballSpeed;
        } 
        else
        {
            ballRb.velocity = new Vector2(-ballXDirection, ballYDirection) * Time.deltaTime * ballSpeed;
        }
    }
}

Paddle.cs


using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Paddle : MonoBehaviour
{
    //Cached References
    private GameManager gameManager;

    //State Variables
    private float screenHeightInUnits = 12;
    private float minYLimit = 1.2f;
    private float maxYLimit = 10.5f;

    // Start is called before the first frame update
    void Start()
    {
        gameManager = FindObjectOfType<GameManager>(); 
    }

    // Update is called once per frame
    void Update()
    {
        MovePaddle();
    }

    private void MovePaddle()
    {
        float mousePosY = (Input.mousePosition.y / Screen.width) * screenHeightInUnits;
        mousePosY = Mathf.Clamp(mousePosY, minYLimit, maxYLimit);

        Vector2 paddlePos = new Vector2(transform.position.x, mousePosY);
        transform.position = paddlePos;
    }

    private void OnCollisionEnter2D(Collision2D collision)
    {
        gameManager.PlayPaddleHitSound();
    }
}

AIPaddle.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class AIPaddle : MonoBehaviour
{
    //Configuration Parameters
    [SerializeField] float paddleMovementSpeed = 2f;

    //Cached References
    private Ball ball;
    private GameManager gameManager;

    //State Variables
    private float maxYLimit = 10.5f;
    private float minYLimit = 1.5f;

    private Vector3 initialAIPaddlePos;
    private Vector2 forwardDirection;
    private Vector2 ballVelocityVector;

    // Start is called before the first frame update
    void Start()
    {
        gameManager = FindObjectOfType<GameManager>();
        ball = FindObjectOfType<Ball>();

        initialAIPaddlePos = transform.position;

        //Since the AI paddle is on the left side, it will be facing towards the right side
        forwardDirection = Vector2.right;
    }

    // Update is called once per frame
    void Update()
    {
        Rigidbody2D ballRb;

        if(ball == null)
        {
            ball = gameManager.GetCurrentBallInstance().GetComponent<Ball>();
        }

        if (gameManager.GetCurrentGameStatus())
        {
            ballRb = ball.GetComponent<Rigidbody2D>();
            MovePaddleTowardsBall(ballRb);
        }
    }

    private void MovePaddleTowardsBall(Rigidbody2D ballRb)
    {
        ballVelocityVector = ball.GetCurrentBallVelocityVector(ballRb);
        float yPosForPaddle = GetUpdatedYPosition();
        yPosForPaddle = Mathf.Clamp(yPosForPaddle, minYLimit, maxYLimit);
        transform.position = new Vector2(transform.position.x, yPosForPaddle);
    }

    private float GetUpdatedYPosition()
    {
        float yPos = transform.position.y;

        bool isBallIncoming = IsBallIncoming(ballVelocityVector);

        if (isBallIncoming)
        {
            float moveSpeed = paddleMovementSpeed * Time.deltaTime;

            yPos = Mathf.MoveTowards(transform.position.y,
                ball.transform.position.y,
                moveSpeed); 
        }

        return yPos;
    }

    //Make the AI Paddle follow the ball's position, only if the ball is approaching towards it
    private bool IsBallIncoming(Vector2 ballVelocityVector)
    {
        float dotProduct = Vector2.Dot(ballVelocityVector, forwardDirection);
        return dotProduct <= 0f;
    }

    public void ResetAIPaddlePos()
    {
        transform.position = initialAIPaddlePos;
    }
}

GameManager.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;

public class GameManager : MonoBehaviour
{
    //Configuration Parameters
    [SerializeField] TextMeshProUGUI playerScoreText;
    [SerializeField] TextMeshProUGUI computerScoreText;
    [SerializeField] GameObject ballPrefab;

    [SerializeField] AudioClip wallHitSound;
    [SerializeField] AudioClip paddleHitSound;
    [SerializeField] AudioClip roundOverSound;

    //Cached References
    private AIPaddle aIPaddle;

    //State Variables
    private int playerScore = 0;
    private int computerScore = 0;

    private bool hasGameStarted = false;
    private bool isPlayerTurn = true;

    // Start is called before the first frame update
    void Start()
    {
        aIPaddle = FindObjectOfType<AIPaddle>();

        playerScoreText.text = SetTextFieldValue(playerScoreText.text, playerScore);
        computerScoreText.text = SetTextFieldValue(computerScoreText.text, computerScore);
    }
    public bool GetCurrentGameStatus()
    {
        return hasGameStarted;
    }

    public void SetCurrentGameStatus(bool gameStatus)
    {
        hasGameStarted = gameStatus;
    }

    public void UpdateScore(bool isPlayer)
    {
        if (isPlayer)
        {
            playerScore++;
            isPlayerTurn = false;
            playerScoreText.text = SetTextFieldValue(playerScoreText.text, playerScore);
        }
        else
        {
            computerScore++;
            isPlayerTurn = true;
            computerScoreText.text = SetTextFieldValue(computerScoreText.text, computerScore);
        }

        StartNextRound();
    }

    public void StartNextRound()
    {
        aIPaddle.ResetAIPaddlePos();
        Instantiate(ballPrefab, ballPrefab.transform.position, Quaternion.identity);
    }

    public bool IsPlayerTurn()
    {
        return isPlayerTurn;
    }

    public void PlayWallHitSound()
    {
        AudioSource.PlayClipAtPoint(wallHitSound, Camera.main.transform.position);
    }

    public void PlayPaddleHitSound()
    {
        AudioSource.PlayClipAtPoint(paddleHitSound, Camera.main.transform.position);
    }

    public void PlayRoundOverSound()
    {
       AudioSource.PlayClipAtPoint(roundOverSound, Camera.main.transform.position);
    }

    public GameObject GetCurrentBallInstance()
    {
        return ballPrefab;
    }

    private string SetTextFieldValue(string textField, int value)
    {
        textField = value.ToString();
        return textField;
    }
}

Though the question is out of the course and the scripts are a bit long, any help would be much appreiciated :pray:

Thanks :slight_smile:

Hi Prashanth,

First of all, good job on challenging yourself. :slight_smile:

Have you already tried to add Debug.Logs to your code to see what is going on during runtime? Since the AI paddle seems to have problems, add Debug.Logs to its class. I assume you expect a certain behaviour. Check if you get the corresponding values while your game is running.

Also please feel free to ask our helpful community of students for further advice over on our Discord chat server.

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.

Privacy & Terms