1
\$\begingroup\$

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;
        }
    }
}

}

\$\endgroup\$
2
\$\begingroup\$

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.

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

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.

Basic to get the velocity vector having a delta of positions, this vector indicates the direction it's currently facing

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.

\$\endgroup\$
  • \$\begingroup\$ 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! \$\endgroup\$ – Bowcox May 2 '18 at 20:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.