PDA

View Full Version : [Resolved] Simple game physics issue...




ArtOfWarfare
Mar 8, 2012, 10:38 PM
Edit: Set a breakpoint, stepped through, grabbed a sheet of paper and plotted what was happening. I was surprised to find my two "futurePoints" weren't where I expected to be... in fact, they were parallel with ballPoint instead of priorPoint. Which is, of course, because I mistakenly had written down "ballPoint" instead of "priorPoint". I've edited my code to show what was wrong.

Original Post: I'm trying to make a 2D game from scratch (Using mostly C with some Quartz(?) for graphics... some cocoa/obj-C to handle inputs.)

Thus far my game consists entirely of a "paddle" (line, defined with two CGPoints for endpoints, startPoint and endPoint) and a "ball" (circle, defined with a CGPoint ballPosition and a second CGPoint ballSpeed.)

Here's all the code for moving the ball, detecting collisions, and calculating what the rebound x and y should be. I've marked the code that I suspect is causing the problem in red:

float distanceBetweenTwoPoints(CGPoint p0, CGPoint p1)
{
return sqrtf(((p0.x-p1.x)*(p0.x-p1.x))+((p0.y-p1.y)*(p0.y-p1.y)));
}

float distanceBetweenPointAndLineEndpoints(CGPoint p0, CGPoint p1, CGPoint p2, bool segment)
{
//Is it a line segment or an infinite line?
if (segment)
{
CGPoint bp1 = CGPointMake(p1.x+((p1.x-p2.x)*0.01), p1.y+((p1.y-p2.y)*0.01));
CGPoint bp2 = CGPointMake(p2.x+((p2.x-p1.x)*0.01), p2.y+((p2.y-p1.y)*0.01));

//Verify it is closer to each endpoint than its cooresponding beyond point.
if (distanceBetweenTwoPoints(p0, p1) > distanceBetweenTwoPoints(p0, bp1))
{
//It's beyond p1, so find its distance to p1.
return distanceBetweenTwoPoints(p0, p1);
}

else if (distanceBetweenTwoPoints(p0, p2) > distanceBetweenTwoPoints(p0, bp2))
{
//It's beyond p2, so find its distance to p2.
return distanceBetweenTwoPoints(p0, p2);
}
}

//Either we didn't care about the segment or we're in the right range if we're here:
float top = fabs(((p2.x-p1.x)*(p1.y-p0.y))-((p1.x-p0.x)*(p2.y-p1.y)));
float bottom = sqrtf(((p2.x-p1.x)*(p2.x-p1.x))+((p2.y-p1.y)*(p2.y-p1.y)));
return top/bottom;
}

- (void)updateBall
{
if (updateTimer && [updateTimer isValid])
{
[updateTimer invalidate];
}

updateTimer = [NSTimer scheduledTimerWithTimeInterval:0.02 target:self selector:@selector(updateBall) userInfo:nil repeats:NO];

float height = self.frame.size.height;
[self setNeedsDisplayInRect:NSRectFromCGRect(CGRectMake((ballPoint.x-.01)*height, (ballPoint.y-0.01)*height, 0.02*height, 0.02*height))];

ballPoint.x += (ballSpeed.x*0.02);
ballPoint.y += (ballSpeed.y*0.02);

redBall = false;

//Has the ball collided with the paddle?
if (0.01 >= distanceBetweenPointAndLineEndpoints(ballPoint, startPoint, endPoint, TRUE))
{
NSLog(@"Collision!");
redBall = true;

//Is the line either horizontal or vertical?
if (endPoint.x == startPoint.x)
{
ballSpeed.y *= -1.0;
}

else if (endPoint.y == startPoint.y)
{
ballSpeed.x *= -1.0;
}

else
{

//Find the prior point.
CGPoint priorPoint = CGPointMake(ballPoint.x-(ballSpeed.x*0.02), ballPoint.y-(ballSpeed.y*0.02));

//Find the distance from the prior point to perpendicular bisector.
float slope = ((endPoint.y-startPoint.y)/(endPoint.x-startPoint.x));
float perp = (-1.0)/slope;
CGPoint perpPoint = CGPointMake(ballPoint.x+1, ballPoint.y+perp);
float priorPerpD = distanceBetweenPointAndLineEndpoints(priorPoint, ballPoint, perpPoint, FALSE);

//Move twice this distance along a line parallel to the slope.
float offsetX = 2.0*(priorPerpD/sqrt(1+(slope*slope)));
CGPoint futurePoint1 = CGPointMake(priorPoint.x /*was incorrectly ballPoint.x before*/+offsetX, priorPoint.y /*was incorrectly ballPoint.y before*/+(offsetX*slope));
CGPoint futurePoint2 = CGPointMake(priorPoint.x /*was incorrectly ballPoint.x before*/-offsetX, priorPoint.y /*was incorrectly ballPoint.y before*/-(offsetX*slope));

//Figure out which of the two future points are correct:
float error1 = fabs(priorPerpD-distanceBetweenPointAndLineEndpoints(futurePoint1, ballPoint, perpPoint, FALSE));
float error2 = fabs(priorPerpD-distanceBetweenPointAndLineEndpoints(futurePoint2, ballPoint, perpPoint, FALSE));
CGPoint futurePoint = (error1<error2)?futurePoint1:futurePoint2;

//Finally, determine a trajectory that heads towards the correct point.
ballSpeed.x = (futurePoint.x - ballPoint.x)*50.0;
ballSpeed.y = (futurePoint.y - ballPoint.y)*50.0;
}
}

if (ballPoint.y > 0.99)
{
ballPoint.y = 0.99;
ballSpeed.y = -ballSpeed.y;
}

else if (ballPoint.y < 0.01)
{
ballPoint.y = 0.01;
ballSpeed.y = -ballSpeed.y;
}

float screenWidth = self.frame.size.width/self.frame.size.height;

if (ballPoint.x > screenWidth-0.01)
{
ballPoint.x = screenWidth-0.01;
ballSpeed.x = -ballSpeed.x;
}

else if (ballPoint.x < 0.01)
{
ballPoint.x = 0.01;
ballSpeed.x = -ballSpeed.x;
}

[self setNeedsDisplayInRect:NSRectFromCGRect(CGRectMake((ballPoint.x-.01)*height, (ballPoint.y-0.01)*height, 0.02*height, 0.02*height))];
}

My problem is... the speed of the ball seems to be all wrong after it rebounds off of the paddle. At some angles, it'll leave the paddle much faster than the speed it comes in at; at other angles it will leave much slower than the angle it came in at (it's even possible to stop the ball altogether.)

Edit now that the above is all solved:

My next issue is that the ball can sometimes pass straight through the paddle... this is likely an issue with the fact that the ball moves too quickly between each calculation... possibly I should find a way to check if the line formed by the old ball point and the new ball point passes through the paddle instead...



seepel
Mar 10, 2012, 11:00 PM
If you are doing this as an exercise to learn about physics engines then I suggest you work on separating the physics from the frame rate, should help with your new problem. But if you are more interested in game design I really suggest that you check out cocos2d and use a physics engine like chipmunk or Box2D. Ultimately you can write your own game engine but it will likely suck up a lot of time if you want to get to the level that these projects are already at.

ArtOfWarfare
Mar 11, 2012, 10:03 PM
If you are doing this as an exercise to learn about physics engines then I suggest you work on separating the physics from the frame rate, should help with your new problem. But if you are more interested in game design I really suggest that you check out cocos2d and use a physics engine like chipmunk or Box2D. Ultimately you can write your own game engine but it will likely suck up a lot of time if you want to get to the level that these projects are already at.

I take pride in being able to say I did everything myself. It gives me experience if nothing else, right?

Edit: As far as separating the physics from the frame rate... I'm working on that...

chrono1081
Mar 13, 2012, 08:57 PM
I take pride in being able to say I did everything myself. It gives me experience if nothing else, right?

Edit: As far as separating the physics from the frame rate... I'm working on that...

I have no clue what your experience is with game development so if you know this already, my apologies :p

Generally you will see two types of physics bodies. There are regular bodies which is probably what you are using and then there are special bodies (sometimes called bullet bodies) that are calculated much more frequently but at a performance cost.

You'll want to make your game engine be able to differentiate between these two bodies and set your ball to the bullet type body so that it doesn't pass through the paddle.

ArtOfWarfare
Mar 14, 2012, 06:30 AM
You'll want to make your game engine be able to differentiate between these two bodies and set your ball to the bullet type body so that it doesn't pass through the paddle.

My experience is... not much. Thus far the only other game I've made relied entirely on UIView animation blocks.

Thank you for the suggestion. I was (and still am, actually,) thinking that the solution might be to instead look at the before and after positions of the paddle and ball to check if the ball passed through the paddle. The ball's size is twice the distance it moves with each update, so I don't think I need to be calculating its position anymore often than I already am.