Advertisement

Raycasting in 2D Pong

Started by May 01, 2018 11:14 AM
8 comments, last by Lactose 6 years, 6 months ago

Hi guys, 

I've never done any ray-casting before and have a problem using it in my game. I have managed to get the initial raycast to work and drawing rays is much easier now to me but if the ball were to bounce against the top of the bottom walls in my game of pong, I want to raycast from those points as quickly as possible to predict where it is going to end up on the computer side, my code for it is below.. I've tried to annotate to help demonstrate my problem.

 


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

}

 

My real question is, when you have a script attached to the ball, like this one, is the raycast attached and moving with the ball? Do I need to make a script on the side? Or can I remove the initial raycast attached and start a new one from the new collision point if it hits the top or bottom boundary? 

Thanks guys!

16 minutes ago, bowcox said:

to predict where it is going to end up on the computer side

Assuming there's just constant speed with perfect reflection upon collision...

If this is the specific problem you're trying to solve, I'm thinking this can be done without raycasts.

You know what the x position of the ball will be (thus, you also know the distance the ball needs to travel). You also know the velocity with which the ball travels. You can figure out how long it will take the ball to reach the other side based on that information.

Given travel time, you can calculate the distance travelled in the y direction. Using that and the known height of the playing area, I'm thinking you can just straight up calculate the number of bounces and use the remainder to figure out the exact y position.

Hello to all my stalkers.

Advertisement
51 minutes ago, Lactose said:

Assuming there's just constant speed with perfect reflection upon collision...

 If this is the specific problem you're trying to solve, I'm thinking this can be done without raycasts.

You know what the x position of the ball will be (thus, you also know the distance the ball needs to travel). You also know the velocity with which the ball travels. You can figure out how long it will take the ball to reach the other side based on that information.

Given travel time, you can calculate the distance travelled in the y direction. Using that and the known height of the playing area, I'm thinking you can just straight up calculate the number of bounces and use the remainder to figure out the exact y position.

Yeh, it's a constant speed with perfect collision. My bad.

Do you mean finding the y-position of the top boundary and bottom boundary, using the velocity of the ball and initial collision point to calculate the path? I feel it may be slightly long winded for more than one collision but I'll give it a go.

I just felt like using raycasts might have been easier and I wondered whether it was possible to do so? I'm curious to know how they work anyway for future reference. Like whether they move with the object, whether you can have two raycasts, or if you have to remove a raycast in order to produce another one (maybe because they're associated with the same object). 

Thanks for replying by the way :) Appreciate any help I can get :D 

15 minutes ago, bowcox said:

Do you mean finding the y-position of the top boundary and bottom boundary, using the velocity of the ball and initial collision point to calculate the path? I feel it may be slightly long winded for more than one collision but I'll give it a go.

No, I didn't mean anything about initial collision points. I'll try to type it out shortly.

 

16 minutes ago, bowcox said:

Like whether they move with the object,

A ray is just from a position an in a direction. A single ray itself does not move.

30 minutes ago, bowcox said:

whether you can have two raycasts

You can have as many raycasts as you want (performance might not be your friend if lots are created, though).

 

59 minutes ago, bowcox said:

remove a raycast in order to produce another one (maybe because they're associated with the same object)

A raycast doesn't persist. A raycast is basically "at the current point in time, what intersects something going from point A in direction N". It doesn't auto-update, it doesn't move around or change direction, etc.

Hello to all my stalkers.

1 hour ago, Lactose said:

You know what the x position of the ball will be (thus, you also know the distance the ball needs to travel). You also know the velocity with which the ball travels. You can figure out how long it will take the ball to reach the other side based on that information.

Given travel time, you can calculate the distance travelled in the y direction. Using that and the known height of the playing area, I'm thinking you can just straight up calculate the number of bounces and use the remainder to figure out the exact y position.

Using this as reference and converting into code...

NOTE: Untested, probably doesn't even compile.  Use at your own peril, etc.. Should only need to call this when the ball hits the player's paddle (i.e. doesn't need to be every frame/in Update).

Will fail if called while the ball is moving away from the opponent.


Vector2 collision;
collision.x = opponent.position.x; //Or opponentWall.x or whatever.

float playAreaHeight = border.max.y - border.min.y;

float distanceX = collision.x - ball.position.X;
float timeToTarget = distanceX / ball.velocity.x;
float verticalDistanceCovered = timeToTarget * ball.velocity.y;
int numBounces = Int(verticalDistanceCovered / playAreaHeight);
float remainder = verticalDistanceCovered % playAreaHeight;

bool movingUpwards = (numBounces % 2) == 0;
float distanceAfterBounces = movingUpwards ? remainder : playAreaHeight - remainder;

float ballPositionAfterBounces = ball.position.y + distanceAfterBounces;
if (ballPositionAfterBounces > border.max.y)
{
	float delta = ballPositionAfterBounces - border.max.y;
	collision.y = border.max.y - delta;
}
else if (ballPositionAfterBounces < border.min.y)
{
	float delta = border.min.y - ballPositionAfterBounces;
	collision.y = border.min.y + delta;
}
else
{
	collision.y = ballPositionAfterBounces;
}

 

Hello to all my stalkers.

Ok, I see what you mean now with using the total height of the play area. I would never have thought to do that aha, thanks for the idea! I'll let you know how I get on with it. Out of curiosity though, with this below, because I just like to know :P

1 hour ago, Lactose said:

A raycast doesn't persist. A raycast is basically "at the current point in time, what intersects something going from point A in direction N". It doesn't auto-update, it doesn't move around or change direction, etc.

What do you mean by it doesn't persist? You mean, it's job is done after it hits something and doesn't exist anymore? Then the information about the collision is stored within a variable? Sorry if I'm an idiot, I've looked elsewhere and I haven't managed to find many clear enough answers. I've learnt way more talking to you than I have the past 2 days looking to see how raycasts act. If I could make them just appear for a split second to simply understand whats happening, I would.

Advertisement
4 minutes ago, bowcox said:

What do you mean by it doesn't persist?

A raycast is an information request -- a snapshot of the world, and doesn't persist, auto-update, change, or move around. It's literally "from the given position, looking in the given direction, what do I see/intersect?".

The result you get from a raycast is something you can store, but if the world changes, your results are out of date, and you'll need to ask again to get updated information.

 

You can think of a raycast as photographing something. You might photograph Anna reading a book, and the result is a picture. You can store the picture, and get various information from it ("Anna is reading Wheel of Time, and is on page 87 in book 5 of the series"), but when Anna turns the page, swaps to another book, turns on Netflix or goes to sleep, the picture doesn't update.

The act of photographing (the raycast) isn't something that persists. The resulting picture (the raycast result) is something that can be persisted, but what it depicts doesn't change, even if reality does.

 

This analogy, I think, also applies to most of your other questions.

Hello to all my stalkers.

On 5/1/2018 at 4:56 PM, Lactose said:

A raycast is an information request -- a snapshot of the world, and doesn't persist, auto-update, change, or move around. It's literally "from the given position, looking in the given direction, what do I see/intersect?".

The result you get from a raycast is something you can store, but if the world changes, your results are out of date, and you'll need to ask again to get updated information.

 

You can think of a raycast as photographing something. You might photograph Anna reading a book, and the result is a picture. You can store the picture, and get various information from it ("Anna is reading Wheel of Time, and is on page 87 in book 5 of the series"), but when Anna turns the page, swaps to another book, turns on Netflix or goes to sleep, the picture doesn't update.

The act of photographing (the raycast) isn't something that persists. The resulting picture (the raycast result) is something that can be persisted, but what it depicts doesn't change, even if reality does.

 

This analogy, I think, also applies to most of your other questions.

This does make a lot of sense, thanks. 

I have tried your way and I've tried adapting it to my situation as much as I can. The division of the distance travelled vertically by the play area height doesn't work if there is a bounce and vertical distance less than the play area height. I've tried patching it from top to bottom and it seems way too complicated compared to using raycasts. I've even had to add in a raycast to detect if there was a bounce if the distance is less than the play area height. However, I might be being stupid. 

Can I ask if you know why, in the above code I originally attached, there ends up being a loop from the middle if statement? The other two statements seem to work perfectly fine. I don't quite understand why just simply trying to add only two raycasts to the situation should be a problem..

On 5/2/2018 at 7:19 PM, bowcox said:

I have tried your way and I've tried adapting it to my situation as much as I can. The division of the distance travelled vertically by the play area height doesn't work if there is a bounce and vertical distance less than the play area height. I've tried patching it from top to bottom and it seems way too complicated compared to using raycasts. I've even had to add in a raycast to detect if there was a bounce if the distance is less than the play area height. However, I might be being stupid. 

If I understand you correctly, the pseudo-code I posted should have dealt with this case.

 

On 5/2/2018 at 7:19 PM, bowcox said:

Can I ask if you know why, in the above code I originally attached, there ends up being a loop from the middle if statement? The other two statements seem to work perfectly fine. I don't quite understand why just simply trying to add only two raycasts to the situation should be a problem..

I haven't really read your initial code in much detail. It's been a while since I touched Unity, and I don't immediately see what's going on.

Hello to all my stalkers.

This topic is closed to new replies.

Advertisement