views:

670

answers:

9

Hi all, I am currently busy on writing a small ball physics engine for my programming course in Win32 API and c++. I have finished the GDI backbuffer renderer and the whole GUI (couple of more things to adjust) but i am very near to completion. The only big obstacles that last are ball to ball collision (but i can fix this on my own) but the biggest problem of them all is the bouncing of the balls. What happens is that i throw a ball and it really falls, but once it bounces it will bounce higher than the point were i released it??? the funny thing is, it only happens if below a certain height. This part is the physics code: (If you need any more code or explanation, please ask, but i would greatly appreciate it if you guys could have a look at my code.)

#void RunPhysics(OPTIONS &o, vector<BALL*> &b)
{ 
    UINT simspeed = o.iSimSpeed;
    DOUBLE  DT; //Delta T
    BOOL bounce; //for playing sound

    DT= 1/o.REFRESH;

    for(UINT i=0; i<b.size(); i++)
    {
        for(UINT k=0; k<simspeed; k++)
        {           
            bounce=false;

            //handle the X bounce
            if( b.at(i)->rBall.left <= 0 && b.at(i)->dVelocityX < 0 ) //ball bounces against the left wall
            {
                b.at(i)->dVelocityX = b.at(i)->dVelocityX * -1 * b.at(i)->dBounceCof;
                bounce=true;
            }
            else if( b.at(i)->rBall.right >= SCREEN_WIDTH && b.at(i)->dVelocityX > 0) //ball bounces against the right wall
            {           
                b.at(i)->dVelocityX = b.at(i)->dVelocityX * -1 * b.at(i)->dBounceCof;
                bounce=true;
            }
            //handle the Y bounce
            if( b.at(i)->rBall.bottom >= SCREEN_HEIGHT && b.at(i)->dVelocityY > 0 ) //ball bounces against the left wall
            {
                //damping of the ball
                if(b.at(i)->dVelocityY < 2+o.dGravity/o.REFRESH)
                {
                    b.at(i)->dVelocityY = 0;
                }

                //decrease the Velocity of the ball according to the bouncecof
                b.at(i)->dVelocityY = b.at(i)->dVelocityY * -1*b.at(i)->dBounceCof;
                b.at(i)->dVelocityX = b.at(i)->dVelocityX * b.at(i)->dBounceCof;

                bounce=true;
            }


            //gravity
            b.at(i)->dVelocityY += (o.dGravity)/o.REFRESH;
            b.at(i)->pOrigin.y += b.at(i)->dVelocityY + (1/2)*o.dGravity/o.REFRESH*DT*METER; 
            //METER IS DEFINED GLOBALLY AS 100 which is the amount of pixels in a meter

            b.at(i)->pOrigin.x += b.at(i)->dVelocityX/o.REFRESH*METER; 

            b.at(i)->UpdateRect();
        }
    }
    return;
}
+1  A: 

Okay, a few things here.

You have differing code paths for bounce against left wall and against right wall, but the code is the same. Combine those code paths, since the code is the same.

As to your basic problem: I suspect that your problem stems from the fact that you apply the gravity after you apply any damping forces / bounce forces.

McWafflestix
A: 

If your dBounceCof is > 1 then, yes your ball will bounce higher. We do not have all the values to be able to reply to your question.

MickTaiwan
+4  A: 

You are using the Euler method of integration. It is possible that your time step (DT) is too large. Also there seems to be a mistake on the row that updates the Y coordinate:

  b.at(i)->pOrigin.y += b.at(i)->dVelocityY + (1/2)*o.dGravity/o.REFRESH*DT*METER;

You have already added the gravity to the velocity, so you don't need to add it to the position and you are not multiplying the velocity by DT. It should be like this:

  b.at(i)->pOrigin.y += b.at(i)->dVelocityY * DT;

Furthermore there appears to be some confusion regarding the units (the way METER is used).

TrayMan
Never use Euler method with collision bounce. On collision detect, recompute velocity via conservation of momentium and new position immediately.
Joshua
If i use your method, the ball doesn't move in the y direction at all, thanks anyway
@Joshua: I personally have used (to great success) variable time segmentation. So long as you're not running up against computational limits, it works like a charm.
McWafflestix
@Rik: The ball probably moves too slow to see, because the Y coordinate is never multiplied by METER. You should compute the coordinates in meters, then convert separately to pixels. That makes the code clearer.
TrayMan
+1  A: 

When do you call RunPhysics? In a timer loop? This code is just an approximation and no exact calculation. In the short interval of delta t, the ball has already changed his position and velocity a litte bit which isn't considered in your algorithm and produces little mistakes. You'll have to compute the time until the ball hits the ground and predict the changes.

And the gravity is already included in the velocity, so don't add it twice here:

b.at(i)->pOrigin.y += b.at(i)->dVelocityY + (1/2)*o.dGravity/o.REFRESH*DT*METER;

By the way: Save b.at(i) in a temporary variable, so you don't have to recompute it in every line.

Ball* CurrentBall = b.at(i);
Dario
A: 

I don't think your equation for position is right:

 b.at(i)->dVelocityY += (o.dGravity)/o.REFRESH;

This is v=v0+gt - that seems fine, although I'd write dGravity*DT instead of dGravity/REFRESH_FREQ.

 b.at(i)->pOrigin.y += b.at(i)->dVelocityY + (1/2)*o.dGravity/o.REFRESH*DT*METER;

But this seems off: It is eqivalent to p = p0+v + 1/2gt^2.

  • You ought to multiply velocity * time to get the units right
  • You are scaling the gravity term by pixels/meter, but not the velocity term. So that ought to be multiplied by METER also
  • You have already accounted for the effect of gravity when you updated velocity, so you don't need to add the gravity term again.
AShelly
A: 

Thanks for the quick replies!!! Sorry, i should have been more clear, the RunPhysics is beiing run after a PeekMessage. I have also added a frame limiter which makes sure that no more calculations are done per second than the refresh rate of the monitor. My dleta t is therefore 1 second devided by the refresh rate. Maybe my DT is actually too small to calculate, although it's a double value??? My cof of restitution is adjustable but starts at 0.9

The dt should work if it's between 50 ms and 2 us. Try lowering it to make your simulation more accurate but heavier on cpu cycles.If you use Euler as you do, you might want to use a dt of 2-10 ms.
Marcus Lindblom
A: 

You need to recompute your position on bounce, to make sure you bounce from the correct place on the wall.

I.e. resolve the exact point in time when the bounce occured, and calculate new velocity/position based on that direction change (partially into a "frame" of calculation) to make sure your ball does not move "beyond" the walls, more and more on each bounce.

W.r.t. time step, you might want to check out my answer here.

Marcus Lindblom
My timer is exactly like that :P depending on the refresh of the monitor the time interval changes. And i don't mind it bouncing beyond the wall as it is for such a short period of time, and it should not affect the rebounce ass the point were it bounces (beyond the wall) should be compensated in lowering the speed again
Do you have an upper bound on your dt? Like max 0.05s? Have you tried running your compute loop several times per frame? (Like 10 or 50)
Marcus Lindblom
A: 

In a rigid body simulation, you need to run the integration up to the instant of collision, then adjust the velocities to avoid penetration at the collision, and then resume the integration. It's sort of an instantaneous kludge to cover the fact that rigid bodies are an approximation. (A real ball deforms during a collision. That's hard to model, and it's unnecessary for most purposes.)

You're combining these two steps (integrating the forces and resolving the collisions). For a simple simulation like you've shown, it's probably enough to skip the gravity bit on any iteration where you've handled a vertical bounce.

In a more advanced simulation, you'd split any interval (dt) that contains a collision at the actual instance of collision. Integrate up to the collision, then resolve the collision (by adjusting the velocity), and then integrate for the rest of the interval. But this looks like overkill for your situation.

Adrian McCarthy
Thanks, the reason i want to have gravity is because my goal is to keep everything adjustable, i don't mind if it's a approximation, as long as the effect shows. Could you maybe show some example on how you would calculate the trajectory?
+1  A: 

ANSWER!!ANSWER!!ANSWER!! but i forgot my other account so i can't flag it :-(

Thanks for all the great replies, it really helped me alot! The answers that you gave were indeed correct, a couple of my formulas were wrong and some code optimisation could be done, but none was really a solution to the problem. So i just sat down with a piece of paper and started calculation every value i got from my program by hand, took me like two hours :O But i did find the solution to my problem: The problem is that as i update my velocity (whith corrected code) i get a decimal value, no problem at all. Later i increase the position in Y by adding the velocity times the Delta T, which is a verry small value. The result is a verry small value that needs to be added. The problem is now that if you draw a Elipse() in Win32 the point is a LONG and so all the decimal values are lost. That means that only after a verry long period, when the values velocity starts to come out of the decimal values something happens, and that alongside with that, the higher you drop the ball the better the results (one of my symptons) The solution to this problem was really simple, ad an extra DOUBLE value to my Ball class which contained the true position (including decimals) of my ball. During the RenderFrame() you just take the floor or ceiling value of the double to draw the elipse but for all the calculations you use the Double value. Once again thanks alot for all your replies, STACKOVERFLOW PEOPLE ROCK!!!

Rik Nauta