Sign in to follow this  

My pong game problem - hit detection

This topic is 3307 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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!

Share this post


Link to post
Share on other sites
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)

Share this post


Link to post
Share on other sites
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 */
...
}
}


Share this post


Link to post
Share on other sites
Well after doing that I'm getting some very weird affects it bounces without even being close to the paddle

Share this post


Link to post
Share on other sites
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);
}

Share this post


Link to post
Share on other sites
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?

Share this post


Link to post
Share on other sites
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.




Share this post


Link to post
Share on other sites
Though it doesn't work so well for the BluePaddle it messes up everything if I make a detection for the bluepaddle as well

it doesnt make any sense instead of bouncing of the left side it bounces on the right side, thus making it bounce and forth against right paddle and the right wall.

[Edited by - Roberts91 on December 27, 2008 8:43:51 PM]

Share this post


Link to post
Share on other sites
The Blue paddle is on the right side of the screen, right? Did you make sure to check if the pong ball's x position is greater than (rather than less than) the blue paddle's x position?

Share this post


Link to post
Share on other sites
Yes I did change the sign. In fact I tried diffrent combos of sign changing I was am that stumped.

Nevermind I got it fixed thanks again. (I thought I had tried that)

Well what I did was change the sign and negated the velocity instead of +'ing it like you have to do on the left hand paddle(red).

Share this post


Link to post
Share on other sites
Is it a copy and paste error, maybe? Did you make sure to use the BluePaddle object instead of the RedPaddle?

If so, could you post that snippet for me?

Share this post


Link to post
Share on other sites
Quote:
Original post by Roberts91
Well what I did was change the sign and negated the velocity instead of +'ing it like you have to do on the left hand paddle(red).


No no no no no no no no no.

Look. If the ball is moving to the left, its x-velocity is negative, yes? Let's say its x-velocity in that case is V (I don't care what the actual value is). If the ball bounces, it should start moving just as fast to the right. "To the right" is the opposite direction as "to the left", yes. Now, what velocity has the same magnitude as V, but in the opposite direction? -V, of course.

Now, if the ball is moving to the right, its velocity is positive. Let's say that its velocity in that case is V... look, whether V is positive or negative doesn't matter. What you want to do is replace the current x-velocity with the negative of that x-velocity, in either case.

Now, how might you handle reflections off the top and bottom of the screen? :)

Share this post


Link to post
Share on other sites

This topic is 3307 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this