# Pong AI - Raycasting

I've been focusing on other games recently but it annoyed me that I skipped the AI part of making my pong game a 1 player game too. I have found out how to RayCast but am struggling with being able to make a path to predict where the ball will end up on the computer side. I have managed to get the first ray to work but in the second if-statement from the code below, it's somehow a loop. If anyone is able to help me, I would be extremely grateful!

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

public class RaycastTest : MonoBehaviour {

private Rigidbody2D rb2d;
private RaycastHit2D hit;
private Vector2 predictedCollision;
private Vector2 collisionPoint;
private int numberOfCollisions;

void Start () {
rb2d = GetComponent<Rigidbody2D> ();
numberOfCollisions = 0;
}

void FixedUpdate () {
//Directions of the ball (normalized)
Vector3 forward3 = new Vector3 (rb2d.velocity.normalized.x, rb2d.velocity.normalized.y, 0) * 20;
Vector2 forward2 = new Vector2 (rb2d.velocity.normalized.x, rb2d.velocity.normalized.y);

// If the ball has just hit the player paddle
if (BallMovement.isOnPath == true) {
if (numberOfCollisions == 0) {
//make sure we have the correct initial collision point from another script
collisionPoint = BallMovement.collisionPoint;

//draw and cast a ray to detect the next point of collision
Debug.DrawRay (collisionPoint, forward3, Color.red, 20f);
hit = Physics2D.Raycast (collisionPoint, forward2, Mathf.Infinity);
//print the name of the object that has been hit
print (hit.collider.name);

//change the initial collision point to the new one
collisionPoint = new Vector2 (hit.point.x, hit.point.y);
print (collisionPoint);

//add on to the number of collisions
numberOfCollisions += 1;
print (numberOfCollisions);
}

//if we still haven't reached the HitArea we need to do another bounce
if (hit.collider.name != "HitArea") {

//make sure we get the velocity after impact and reverse the direction of the y velocity.
forward2 = new Vector2(rb2d.velocity.normalized.x, rb2d.velocity.normalized.y * -1f);
forward3 = new Vector3 (rb2d.velocity.normalized.x, rb2d.velocity.normalized.y * -1f, 0) * 20;

//draw and cast a ray to detect the next point of collision
hit = Physics2D.Raycast(collisionPoint, forward2, Mathf.Infinity);
Debug.DrawRay(collisionPoint, forward3, Color.red, 20f);

//change the initial collision point to the new one
collisionPoint = hit.point;
print (collisionPoint);

//add on to the number of collisions
numberOfCollisions += 1;
}

//If we've hit the HitArea, we reset everything so it stops looping and we've got our predicted point of collision on the computer side.
if (hit.collider.name == "HitArea") {
predictedCollision = hit.point;
numberOfCollisions = 0;
BallMovement.isOnPath = false;
}
}
}


}

The loop isn't in this code (it's in the caller to this code); but I think I know what's causing it:

    //if we still haven't reached the HitArea we need to do another bounce
if (hit.collider.name != "HitArea") {


This assumes that you WILL find a HitArea. This means the calling code will then call this function again and raycast again.

Were I to guess at why, you are probably allowing yourself no buffer between HitArea and your hit detection, so a bounce happens in an area that a bounce is not supposed to occur. This is likely an issue with your hit detection code, but could be fixed by shrinking your "hit area" to have a buffer around the edge so that when the edge of your pong-ball hits the edge of the screen it counts, rather than the middle.

Without more code it is not possible to definitively point to the root cause of the bug; but the loop your seeing IS because you're not colliding with "HitArea" for some reason.

• Ok, awesome, thanks for the help. I'll try this tomorrow :) – Bowcox May 2 '18 at 20:14

If your ball is travelling straight towards its path, then you could do a delta of positions, or if it has a Rigidbody you could use it's velocity vector.

In both cases, you will get a vector that points to the current direction your ball is facing relative to the world space. You could then make a raycast from that velocity vector, so the hit point will be it's final destination with that velocity vector.

Example:

using UnityEngine;

public class ExampleBall : MonoBehaviour
{
public Vector3 predictionPoint;
private Vector3 latePosition;
private Rigidbody rigidbody;

void Awake()
{
rigidbody = GetComponent<Rigidbody>();
latePosition = transform.position;
}

void Update()
{
/// With Velocity
RaycastHit hit;
if(Physics.Raycast(transform.position, rigidbody.velocity, out hit, Mathf.Infinity))
{
predictionPoint = hit.point;
}

/// With delta positions
Vector3 delta = transform.position - latePosition;

if(Physics.Raycast(transform.position, delta, out hit, Mathf.Infinity))
{
predictionPoint = hit.point;
}

latePosition = transformPosition;
}
}


The script's predictionPoint is public, so you can access it, maybe it's expensive to make raycasts each frame, but it was for the sake of example.

You can also use extrapolations, considering your velocity vector, (LerpUnclamped does the trick). Example:

using UnityEngine;

public class ExampleBall : MonoBehaviour
{
public Vector3 predictionPoint;
public float pedictionTime;
private Vector3 latePosition;
private Rigidbody rigidbody;

void Awake()
{
rigidbody = GetComponent<Rigidbody>();
Vector3 delta = transform.position - latePosition;
}

void Update()
{
/// With Velocity
predictionPoint = Vector3.LerpUnclamped(transform.position, transform.position + rigidbody.velocity, predictionTime);

/// With delta positions
Vector3 delta = transform.position - latePosition;
predictionPoint = Vector3.LerpUnclamped(transform.position, transform.position + delta, predictionTime);

latePosition = transformPosition;
}
}


Hope it helps.

• Thanks for the help. I'll definitely have a look at these tomorrow. :) It's just so nice to have a fresh perspective on something when you're staring at it - wondering what's wrong. Thanks again! – Bowcox May 2 '18 at 20:18