Changing a ball's direction based on the paddle position hit

Official forum for the Chipmunk2D Physics Library.
Post Reply
drey08
Posts: 15
Joined: Mon Oct 28, 2013 10:55 pm
Contact:

Changing a ball's direction based on the paddle position hit

Post by drey08 »

Hi,

I'm experimenting with a breakout-style game. I've found a nice collision handler function for another library which makes the ball bounce off of the paddle at an angle based on the position of where the ball hit the paddle (this isn't realistic physics but it's the typical breakout-style physics). E.g. if the ball hits the center of the paddle, it simply bounces off at the same angle, and if it hits close to the edges of the paddle it could either increase or decrease its angle, based on its own angle and whether the ball hit the left portion or right portion of the paddle.

Quoting from the forum post from here:
Here's a good physics setup for your ball's collision handler:

Code: Select all

protected function handleContact(e:ContactEvent):void
{
   //don't handle contacts that aren't with the paddle
   if (!(e.other.GetBody().GetUserData() is Paddle) || !live)
      return;
      
   //get a reference to the paddle
   var paddle:Paddle = e.other.GetBody().GetUserData() as Paddle;
   
   //only change collisions that are against the top surface of the paddle (let it shank off if it hits an edge).
   var collisionNormal:V2 = e.getWorldManifold().normal;
   if(collisionNormal.x == 0 && collisionNormal.y == 1)
   {
      var diff:Number = x - paddle.x; //x distance between ball and paddle.
      var ratio:Number = diff / (paddle.width / 2); //distance as a ratio (between -1 and 1)
      if (ratio < -1 || ratio > 1) //it will shank.
         return;
      
      //Create a new velocity vector and set it to the ball's current speed.
      var velocity:V2 = _body.GetLinearVelocity();
      var speed:Number = velocity.length();
      
      //set the ball to be going straight up at the same speed as it came.
      velocity = new V2(0, -1);
      velocity.normalize(speed);
      
      //rotate the velocity angle between a specific range (I chose 1 radian) as a proportion ball's distance from center of paddle.
      velocity.rotate(paddleBounceAngleRange * ratio);
      _body.SetLinearVelocity(velocity);
   }
}
This is code for the Citrus engine, but I'd like to translate this into C for use with Chipmunk.

So far I know how to:

- Set a collision handler that is only called when a ball hits a paddle (via cpSpaceAddCollisionHandler and collision types)
- I'm using a beginCollision callback, I assume this is where I'm supposed to manipulate the velocity/angle of the ball.

But this is where I fall short, the original code checks the normals to be (0, 1), but my normals returned from the cpArbiterGetContactPointSet call are almost always (0, -1). And I'm not sure how to translate the rest of the code, such as the usage of GetLinearVelocity and paddleBounceAngleRange.

Does someone know how to implement a simple C handler based on the Citrus one?

Cheers.
drey08
Posts: 15
Joined: Mon Oct 28, 2013 10:55 pm
Contact:

Re: Changing a ball's direction based on the paddle position

Post by drey08 »

Ok, so here's my hackish hardcoded attempt first:

(This is actually D code, but it should be just as readable as C code):

Code: Select all

static bool beginCollision(cpArbiter *arb, cpSpace *space, void *data)
{
    cpShape* ball;
    cpShape* paddle;
    cpArbiterGetShapes(arb, &ball, &paddle);

    cpContactPointSet set = cpArbiterGetContactPointSet(arb);

    foreach (i; 0 .. set.count)
    {
        cpVect oldVel = cpBodyGetVel(ball.body_);

        float diff = ball.body_.p.x - paddle.body_.p.x;

        cpVect newVel = cpVect(oldVel.x + (diff * 5), oldVel.y);
        cpBodySetVel(ball.body_, newVel);
    }

    return true;
}

// default collision types for all types is 0, player is 2.
cpSpaceAddCollisionHandler(space, 0, 2, &beginCollision, null, null, null, null);
Edit: Fixed some code here.

That kind of works, but it's very hacky.
User avatar
slembcke
Site Admin
Posts: 4166
Joined: Tue Aug 14, 2007 7:13 pm
Contact:

Re: Changing a ball's direction based on the paddle position

Post by slembcke »

So that will probably work fine. Though I'd make it a pre-solve callback (in case something weird happens that causes it to be in contact for multiple frames), and also return false from the callback so the regular physics doesn't apply. Also, it's safe to assume that you will only ever have a single contact point if one of the shapes is a circle and you can get rid of the for-loop.

Alternatively, what I've done in the past was to change the collision information itself. Basically, I made the paddle act as though it had some circular curvature to it even though it's surface was flat. I did that by changing the contact normal and point based on the position of the ball relative to the paddle. http://chipmunk-physics.net/release/Chi ... rFunctions It ended up with a very nice, physical feel to it and allowed you to really slam the ball with the paddle if you wanted to.

Unfortunately I can't share the original code since it was for a contract project, but I could rewrite it maybe.
Can't sleep... Chipmunks will eat me...
Check out our latest projects! -> http://howlingmoonsoftware.com/wordpress/
drey08
Posts: 15
Joined: Mon Oct 28, 2013 10:55 pm
Contact:

Re: Changing a ball's direction based on the paddle position

Post by drey08 »

Thanks for the tips.

Another sort-of hardcoded version, although it's much more predictable this time:

Code: Select all

static bool preSolve(cpArbiter *arb, cpSpace *space, void *data)
{
    cpShape* ball;
    cpShape* paddle;
    cpArbiterGetShapes(arb, &ball, &paddle);

    cpVect oldVel = cpBodyGetVel(ball.body_);

    // ball position relative to the center of the paddle
    float diff = ball.body_.p.x - paddle.body_.p.x;

    auto paddleWidth = paddle.bb.r - paddle.bb.l;
    float halfWidth = paddleWidth / 2.0;

    // we want the paddle to represent a velocity range of [-200 .. 0 .. 200]
    float step = 200.0 / halfWidth;

    float newX = step * diff;
    float newY = -oldVel.y;  // simply flip the velocity

    cpVect newVel = cpVect(newX, newY);

    cpBodySetVel(ball.body_, newVel);

    return false;
}
It gives that classic arcade-y feel to it. :)

It still doesn't take into account the ball's original X velocity, or any velocity of the paddle itself. I'm using a rogue body for the paddle so I don't really have a velocity unless I manually calculate it.
drey08
Posts: 15
Joined: Mon Oct 28, 2013 10:55 pm
Contact:

Re: Changing a ball's direction based on the paddle position

Post by drey08 »

Btw, you'll have to excuse my really poor code. I'm completely new to physics or game development in general, although I've been using D for many years (and I'm a D core developer). :)
Post Reply

Who is online

Users browsing this forum: No registered users and 32 guests