My pong game problem - hit detection

Started by
13 comments, last by Zahlman 15 years, 3 months ago
So I'm making a 2D pong game as an exercise and to make an effect of bouncing off the paddle I did something like so if( gPong->mPosition.x < gRedPaddle->mPosition.x ) { gPong->mPosition.x = (float)gRedPaddle->mPosition.x; gPong->mVelocity.x = +100.0f; gPong->mVelocity.y = 0.0f; } Now I was all excited when I found that it does indeed bounce off my paddle when it hits it. Then I tried moving the paddle and letting the ball go off into space. And guess what? It still bounced off where my paddle had originated from without actually being there!!! I was like wtf? So I soon found out that I was testing if it hit where the paddle had originated from in 2D space and not where the paddle is currently located!! Duh! So I was thinking of a way to store the current position of the paddle into a variable and then use that variable to compare. My question is can anyone help me with getting the current position of the red paddle as I've tried diffrent ways of doing it and they all don't work. ThankS!
Advertisement
Something about your question / code snippet is throwing me off.

In short, if the Red Paddle is drawing in the correct place, then somehow your rendering code knows where its current position is. So, you should be able to check your rendering/drawing code and see how it knows.

But looking at your code snippet, that doesn't appear to be your problem. I mean, if gRedPaddle->mPosition is the position where the paddle originated, instead of its current position, then yes, I understand this would be a problem.

However, even if that were not the case -- that is, even if mPosition stored the current position of the paddle -- the code you posted would still produce the same erroneous result: the ball would bounce whether it hit the paddle or not.

That is because you're only testing the x-position of the ball and paddle. Since the paddle only moves up and down, the x position never changes. Therefore, when the ball crosses that x-boundary, it'll bounce whether it hits the paddle or not.

You need to know the paddle's y-position, as well as its length. If the paddle's y-position refers to the top of the paddle, and the paddle is 100 units (pixels, maybe?) long, then in addition to checking to see if:

gPong->mPosition.x < gRedPaddle->mPosition.x

../you will also need to check to see if:

gPong->mPosition.y > gRedPaddle->mPosition.y && gPong->mPosition.y < (gRedPaddle->mPosition.y + 100)
Hi Roberts,

I'm not sure I understand completely. If you can already move the paddle in your game, then you must be somehow already storing a current position for it.

If the paddles move up and down, then you need to store a variable 'gRedPaddle->mPosition.y'.

The code you have showed doesn't check if the ball hits the paddle at all, you need to also check whether the balls y-position is within the paddles top and bottom edges.

For example:

/* Check x direction */if( gPong->mPosition.x < gRedPaddle->mPosition.x ){     /* Check if paddle hits ball */     if( gPong->mPosition.y > gRedPaddle->BottomEdge &&          gPong->mPosition.y < gRedPaddle->TopEdge)     {          ...          /* Ball response code */          ...     }}
- Haptic
Well after doing that I'm getting some very weird affects it bounces without even being close to the paddle
Haptic's logic looks correct. Can you post your source code?
I tried starting over and trying Haptics version but now the ball doesn't hit at all.

//=========================================================// Includes//=========================================================#include <string>#include "resource.h"#include "Sprite.h"#include "BackBuffer.h"#include <list>using namespace std;//=========================================================// Globals//=========================================================HWND        ghMainWnd  = 0;HINSTANCE   ghAppInst  = 0;HMENU       ghMainMenu = 0;HDC         ghSpriteDC = 0;BackBuffer* gBackBuffer = 0;Sprite*     gBackground = 0;Sprite*		gBluePaddle = 0;Sprite*		gRedPaddle  = 0;Sprite*		gPong	    = 0;list<Vec2> gBulletPos;RECT gMapRect = {0, 0, 800, 600};string gWndCaption = "Pong v1.0";// Client dimensions exactly equal dimensions of // background bitmap.  This is found by inspecting // the bitmap in an image editor, for example.const int gClientWidth  = 800;const int gClientHeight = 600;// Center point of client rectangle.const POINT gClientCenter = {	gClientWidth  / 2,	gClientHeight / 2};// Pad window dimensions so that there is room for window// borders, caption bar, and menu.const int gWindowWidth  = gClientWidth;const int gWindowHeight = gClientHeight;//=========================================================// Function Prototypes//=========================================================bool InitMainWindow();int  Run();void DrawFramesPerSecond(float deltaTime);LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);//=========================================================// Name: WinMain// Desc: Program execution starts here.//=========================================================int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 		PSTR cmdLine, int showCmd){	ghAppInst = hInstance;	// Create the main window.	if( !InitMainWindow() )	{		MessageBox(0, "Window Creation Failed.", "Error", MB_OK);		return 0;	}	// Enter the message loop.	return Run();}//=========================================================// Name: InitMainWindow// Desc: Creates the main window upon which we will//       draw the game graphics onto.  //=========================================================bool InitMainWindow(){	WNDCLASS wc; 	wc.style         = CS_HREDRAW | CS_VREDRAW;	wc.lpfnWndProc   = WndProc;	wc.cbClsExtra    = 0;	wc.cbWndExtra    = 0;	wc.hInstance     = ghAppInst;	wc.hIcon         = ::LoadIcon(0, IDI_APPLICATION);	wc.hCursor       = ::LoadCursor(0, IDC_ARROW);	wc.hbrBackground = (HBRUSH)::GetStockObject(NULL_BRUSH);	wc.lpszMenuName  = 0;	wc.lpszClassName = "MyWndClassName";	RegisterClass( &wc );	// WS_OVERLAPPED | WS_SYSMENU: Window cannot be resized	// and does not have a min/max button.  	ghMainWnd = ::CreateWindow("MyWndClassName", 		gWndCaption.c_str(), WS_OVERLAPPED | WS_SYSMENU, 		200, 200, gWindowWidth, gWindowHeight, 0, 		ghMainMenu, ghAppInst, 0);	if(ghMainWnd == 0)	{		::MessageBox(0, "CreateWindow - Failed", 0, 0);		return 0;	}	ShowWindow(ghMainWnd, SW_NORMAL);	UpdateWindow(ghMainWnd);	return true;}//=========================================================// Name: Run// Desc: Encapsulates the message loop.//=========================================================int Run(){	MSG msg;	ZeroMemory(&msg, sizeof(MSG));	// Get the performance timer frequency.	__int64 cntsPerSec = 0;	bool perfExists = QueryPerformanceFrequency((LARGE_INTEGER*)&cntsPerSec)!=0;	if( !perfExists )	{		MessageBox(0, "Performance timer does not exist!", 0, 0);		return 0;	}	double timeScale = 1.0 / (double)cntsPerSec;	// Get the current time.	__int64 lastTime = 0;	QueryPerformanceCounter((LARGE_INTEGER*)&lastTime);		double timeElapsed = 0.0f;	while(msg.message != WM_QUIT)	{		// IF there is a Windows message then process it.		if(PeekMessage(&msg, 0, 0, 0, PM_REMOVE))		{			TranslateMessage(&msg);			DispatchMessage(&msg);		}		// ELSE, do game stuff.		else        {				// Get the time now.						__int64 currTime = 0; 			QueryPerformanceCounter((LARGE_INTEGER*)&currTime);			// Compute the differences in time from the last			// time we checked.  Since the last time we checked			// was the previous loop iteration, this difference			// gives us the time between loop iterations...			// or, I.e., the time between frames.			double deltaTime = (double)(currTime - lastTime) * timeScale;			timeElapsed += deltaTime;			// Clamp speed to 100 units per second.			if(gRedPaddle->mVelocity.length() > 100.0f)				gRedPaddle->mVelocity.normalize() *= 100.0f;			// Update RedPaddle.			gRedPaddle->update((float)deltaTime);			// Update BluePaddle.			gBluePaddle->update((float)deltaTime);			// Update Pong.			gPong->update((float)deltaTime);			// Make sure RedPaddle stays in the map boundary.			if( gRedPaddle->mPosition.x < gMapRect.left )			{				gRedPaddle->mPosition.x = (float)gMapRect.left;				gRedPaddle->mVelocity.x = 0.0f;				gRedPaddle->mVelocity.y = 0.0f;			}			if( gRedPaddle->mPosition.x > gMapRect.right )			{				gRedPaddle->mPosition.x = (float)gMapRect.right;				gRedPaddle->mVelocity.x = 0.0f;				gRedPaddle->mVelocity.y = 0.0f;			}			if( gRedPaddle->mPosition.y < gMapRect.top )			{				gRedPaddle->mPosition.y = (float)gMapRect.top;				gRedPaddle->mVelocity.x = 0.0f;				gRedPaddle->mVelocity.y = 0.0f;			}			if( gRedPaddle->mPosition.y > gMapRect.bottom )			{				gRedPaddle->mPosition.y = (float)gMapRect.bottom;				gRedPaddle->mVelocity.x = 0.0f;				gRedPaddle->mVelocity.y = 0.0f;			}			// Make sure the BluePaddle stays in the map boundary.			if( gBluePaddle->mPosition.x < gMapRect.left )			{				gBluePaddle->mPosition.x = (float)gMapRect.left;				gBluePaddle->mVelocity.x = +1000;				gBluePaddle->mVelocity.y = 0;			}			if( gBluePaddle->mPosition.x > gMapRect.right )			{				gBluePaddle->mPosition.x = (float)gMapRect.right;				gBluePaddle->mVelocity.x = -1000;				gBluePaddle->mVelocity.y = 0;			}			if( gBluePaddle->mPosition.y < gMapRect.top )			{				gBluePaddle->mPosition.y = (float)gMapRect.top;				gBluePaddle->mVelocity.x = 0;				gBluePaddle->mVelocity.y = +1000;			}			if( gBluePaddle->mPosition.y > gMapRect.bottom )			{				gBluePaddle->mPosition.y = (float)gMapRect.bottom;				gBluePaddle->mVelocity.x = 0;				gBluePaddle->mVelocity.y = -1000;			}			// Make sure the Pong stays in the map boundary.			if( gPong->mPosition.x < gMapRect.left )			{				gPong->mPosition.x = (float)gMapRect.left;				gPong->mVelocity.x = +100.0f;				gPong->mVelocity.y = 0.0f;			}			if( gPong->mPosition.x > gMapRect.right )			{				gPong->mPosition.x = (float)gMapRect.right;				gPong->mVelocity.x = -100.0f;				gPong->mVelocity.y = 0.0f;			}			if( gPong->mPosition.y < gMapRect.top )			{				gPong->mPosition.y = (float)gMapRect.top;				gPong->mVelocity.x = 0.0f;				gPong->mVelocity.y = +100.0f;			}			if( gPong->mPosition.y > gMapRect.bottom )			{				gPong->mPosition.y = (float)gMapRect.bottom;				gPong->mVelocity.x = 0.0f;				gPong->mVelocity.y = +100.0f;			}			// Check for red paddle.			if( gPong->mPosition.x < gRedPaddle->mPosition.x )			{				if( gPong->mPosition.y > gRedPaddle->mPosition.y)				{					gPong->mPosition.x = (float)gRedPaddle->mPosition.x;					gPong->mVelocity.x = 0.0f;					gPong->mVelocity.y = +100.0f;				}			}			// Draw objects.			gBackground->draw(gBackBuffer->getDC(), ghSpriteDC);			gRedPaddle->draw(gBackBuffer->getDC(), ghSpriteDC);			gBluePaddle->draw(gBackBuffer->getDC(), ghSpriteDC);			gPong->draw(gBackBuffer->getDC(), ghSpriteDC);						DrawFramesPerSecond((float)deltaTime);			// Now present the backbuffer contents to the main			// window client area.			gBackBuffer->present();							// We are at the end of the loop iteration, so			// prepare for the next loop iteration by making			// the "current time" the "last time."			lastTime = currTime;			// Free 20 miliseconds to Windows so we don't hog			// the system resources.			Sleep(20);        }    }	// Return exit code back to operating system.	return (int)msg.wParam;}//=========================================================// Name: DrawFramesPerSecond// Desc: This function is called every frame and updates//       the frame per second display in the main window//       caption.//=========================================================void DrawFramesPerSecond(float deltaTime){	// Make static so the variables persist even after	// the function returns.	static int   frameCnt    = 0;	static float timeElapsed = 0.0f;	static char  buffer[256];	// Function called implies a new frame, so increment	// the frame count.	++frameCnt;	// Also increment how much time has passed since the	// last frame.  	timeElapsed += deltaTime;	// Has one second passed?	if( timeElapsed >= 1.0f )	{		// Yes, so compute the frames per second.		// FPS = frameCnt / timeElapsed, but since we		// compute only when timeElapsed = 1.0, we can 		// reduce to:		// FPS = frameCnt / 1.0 = frameCnt.				sprintf(buffer, "--Frames Per Second = %d", frameCnt);		// Add the frames per second string to the main		// window caption--that is, we'll display the frames		// per second in the window's caption bar.		string newCaption = gWndCaption + buffer;		// Now set the new caption to the main window.		SetWindowText(ghMainWnd, newCaption.c_str());			// Reset the counters to prepare for the next time		// we compute the frames per second.		frameCnt    = 0;		timeElapsed = 0.0f;	}}//=========================================================// Name: WndProc// Desc: The main window procedure.//=========================================================LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam){		Circle bc; // Not needed in this demo, leave default.	Vec2   p0(gClientCenter);	Vec2   v0(0.0f, 0.0f);	switch( msg )	{	    case WM_LBUTTONDOWN:		gPong->mVelocity.x -= 100.0f;	return 0;	// Create application resources.	case WM_CREATE:		// Create the sprites		gBackground = new Sprite(ghAppInst,			IDB_BACKGROUND, IDB_BACKGROUND, bc, p0, v0);		p0.x = 100;		p0.y = 250;		gRedPaddle = new Sprite(ghAppInst, IDB_REDPADDLE, IDB_REDPADDLEMASK,			bc, p0, v0);		p0.x = 700;		p0.y = 250;		gBluePaddle = new Sprite(ghAppInst, IDB_BLUEPADDLE, IDB_BLUEPADDLE,			bc, p0, v0);		p0.x = 400;		p0.y = 250;        gPong = new Sprite(ghAppInst, IDB_PONG, IDB_PONGMASK,			bc, p0, v0);		// Create system memory DCs 		ghSpriteDC = CreateCompatibleDC(0);		// Create the backbuffer.		gBackBuffer = new BackBuffer(			hWnd, 			gClientWidth,			gClientHeight);		return 0;	case WM_COMMAND:			return 0;	case WM_KEYDOWN:		switch(wParam)		{		// Accelerate left.		case 'W':			gRedPaddle->mVelocity.y -= 20.0f;			break;		// Accelerate right.		case 'S':			gRedPaddle->mVelocity.y += 20.0f;			break;		}		return 0;	// Destroy application resources.	case WM_DESTROY: 			delete gBackground;		delete gRedPaddle;		delete gBluePaddle;		delete gBackBuffer;		delete gPong;		DeleteDC(ghSpriteDC);		PostQuitMessage(0); 		return 0;		}		// Forward any other messages we didn't handle to the	// default window procedure.	return DefWindowProc(hWnd, msg, wParam, lParam);}
I'm not using a rectangle I'm using a sprite so how am I suppose to get the TopEdge and bottomEdge of my sprite?
This code you posted won't do what you want it to do:

if( gPong->mPosition.x < gRedPaddle->mPosition.x ){   if( gPong->mPosition.y > gRedPaddle->mPosition.y)   {      gPong->mPosition.x = (float)gRedPaddle->mPosition.x;      gPong->mVelocity.x = 0.0f;      gPong->mVelocity.y = +100.0f;   }}


All this is saying is, "If the pong ball is to the left of the red paddle, and above the red paddle's position, then handle the collision." Test it out. Let the ball cross the boundary above the red paddle, and I think you'll find that a collision happens (even though it shouldn't).

The thing is, the paddle isn't dimensionless. It has width and length. You can disregard the width to some extent, but when it comes to detecting collisions, you must know not only where the paddle is but how long it is. That means you either need to know its top edge and bottom edge, or you need to have some sort of position point and length.




Oh MY GOD!!!!!! I got it to work thank you so much guys!
w00t!

This topic is closed to new replies.

Advertisement