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
Thanks