Top down pivot constraint confusion

Official forum for the Chipmunk2D Physics Library.
dorthysmash
Posts: 5
Joined: Thu Mar 10, 2011 11:21 pm
Contact:

Top down pivot constraint confusion

Post by dorthysmash »

I've been reading through the forums and going through the tank demo, so I apologize if I'm missing the obvious here. I'm still pretty new to working with chipmunk. And thanks for all the work on the chipmunk and for the assistance! Sorry for length, I figure more info is better than less...

Basically I'm trying to get a top-down game working where I can push objects (nothing fancy). I first got my player movement working how I wanted without using the control body. So I just have a cpBody and a cpCircleShape. Using the mouse I would set a goal position and then use cpBodySlew() to move towards that goal position. Everything was working as expected that way. Then I tried to add the control body like the tank demo does and all hell broke loose ^_^. So I thought I could simply set up the control body and the pivot joint, and then run cpBodySlew() on my control body and everything would just work. However I'm getting a lot of unexpected behavior (I'm sure it's expected, I'm just not expecting it).

1) Does cpBodySlew work on static bodies that aren't part of the space? As far as I can see, it has no effect.

2) Do I need to manually update the control body position after the body is updated? From what I'm seeing, my normal body is anchored to the position of the static body, but with some amount of give. From looking at the tank demo, it looks like the control body position is never updated. However I can only get my body to move if I update the control body position after I call cpSpaceStep().

3) When I do manually update the control body, and then try to collide with another object with the same settings, they appear to stop each other, but they are very jittery. My guess would be that they are continually trying to get to their goal and so you get the jitter as they attempt to reach it. The objects also are overlapping quite a bit. I'm essentially trying to create two tanks which can push each other (depending on which one you are controlling).

4) At the moment I'm not worried about objects rotating when they collide, do I still need to use the gear constraint (could this be part of my problem since I'm excluding it)? When I add in the gear constraint, things seem to get much worst (I'm sure I'm doing it wrong ;_;).

Here is how I am setting up the objects:

Code: Select all

    // create bodies
    body = cpBodyNew(1.0f, 1.0f);
    controlBody = cpBodyNew(INFINITY, INFINITY);
    
    // create shape
    shape = cpCircleShapeNew(body, 32.0f, cpv(0.0f, 0.0f));
    shape->e = 0.0f;
    shape->u = 1.0f;
    shape->group = CP_GROUP_PLAYERS + playerId;
    shape->collision_type = CP_COLLISION_TYPE_PLAYER;
    shape->data = self;

    pivot = cpPivotJointNew2(controlBody, body, cpvzero, cpvzero);
    pivot->biasCoef = 0.0f; // disable joint correction
    pivot->maxForce = 10000.0f; // emulate linear friction

    // init position
    body->p = cpv(spawnPoint.x, spawnPoint.y);
    controlBody->p = body->p; // body will appear at (0, 0) if I don't initialize control body position
  
   // add objects to space
   cpSpaceAddBody(space, body);
   cpSpaceAddShape(space, shape);
   cpSpaceAddConstraint(space, pivot);
Here is how i am updating the velocity. Since cpBodySlew() wasn't working I basically hacked out my own slew to try and debug what is going on.

Code: Select all

    CGPoint diff = ccp(abs(self.position.x - goalPosition.x),
                                abs(self.position.y - goalPosition.y));
    
    if (diff.x < 10.0f && diff.y < 10.0f) {
        // update chipmunk body
        //cpBodySlew(body, self.position, elapsedTime);
        controlBody->v = cpvzero;
        return;
    }
    
    // figure out our new position
    float delta = velocity * elapsedTime;
    CGPoint direction = ccpSub(goalPosition, self.position);
    direction = ccpNormalize(direction);
    CGPoint newPosition = ccpAdd(self.position, ccpMult(direction, delta));
    
    // update chipmunk body (hacked out cpBodySlew() basically)
    // cpBodySlew(body, newPosition, elapsedTime);
    cpVect deltaPosition = cpvsub(newPosition, body->p);
    cpVect deltaDirection = cpvnormalize(deltaPosition);
    cpVect velocity = cpvzero;
    if (deltaDirection.x != 0 && elapsedTime != 0)
        velocity.x = deltaPosition.x / (deltaDirection.x * elapsedTime);
    
    if (deltaDirection.y != 0 && elapsedTime != 0)
        velocity.y = deltaPosition.y / (deltaDirection.y * elapsedTime);
    
    controlBody->v.x = deltaDirection.x * velocity.x;
    controlBody->v.y = deltaDirection.y * velocity.y;
And if I don't update the control body position in my chipmunk update function, then the body will only move about 50 pixels away from the control body as if an invisible constraint is holding it in place (I do realize the irony in that statement, cause there is a constraint there, just trying to describe what I'm seeing).

Code: Select all

- (void)chipmunkUpdate {
    // sync sprite with chipmunk body
    self.position = body->p;
    controlBody->p = body->p;
}
User avatar
slembcke
Site Admin
Posts: 4166
Joined: Tue Aug 14, 2007 7:13 pm
Contact:

Re: Top down pivot constraint confusion

Post by slembcke »

Wow, all of a sudden I'm getting a lot of questions about this. I guess I need to make a clearer example that explains all the magic and strangely configured joints. :-\

1) cpBodySlew() calculates a velocity so that an object will move to the desired point in the next timestep. You don't quite want that, you need a maximum speed. Pull the calculation out of cpBodySlew() and clamp it like this:
body->v = cpvclamp(cpvmult(cpvsub(desiredPosition, body->p), 1.0f/dt), maxSpeed);

I'm seriously considering removing cpBodySlew() function in the next version because it causes so much confusion for a trivial one line function.

2 & 3) You shouldn't have to no. Setting the pivot joint's biasCoef to 0.0 should cause the joint to ignore position error and only work on the velocity. The code you pasted to initialize the bodies and joint looks correct though. I'm not sure what to tell you unless you can make a patch for the tank demo to reproduce the problem. I have no idea what the problem would be.

Maybe part of the problem is just that mass of your body is so low and the maxForce is so high? In the tank demo, the mass/force ratio is 10/1000. You are using 1/10000. I guess I would try that.

4) The gear joint is only if you want rotation. If you don't want rotation, you probably want to give your body an infinite moment of inertia so that it can't rotate at all. Actually having such a low moment of inertia might be causing some of the problems here.
Can't sleep... Chipmunks will eat me...
Check out our latest projects! -> http://howlingmoonsoftware.com/wordpress/
dorthysmash
Posts: 5
Joined: Thu Mar 10, 2011 11:21 pm
Contact:

Re: Top down pivot constraint confusion

Post by dorthysmash »

I'm already thankful for all the documentation that is there. Just started doing embedded development at my job and any documentation is a God send.

I did all the things you suggested but still had no luck (still can't control the body via the control body). Basically the only change was movement seemed a little smoother and less jittery. I edited the Tank.c source to mimic what I am doing, and it looked good (moving the body via the control body). I then went back to my project, removed all the objects I had in my space and made sure I was creating the objects in the same order with same parameters as what is in the Tank.c. And waddaya know, I can now move the body via the control body.

Hopefully tonight or tomorrow, I will start slowly piecing all of my stuff back together and try to figure out where I went wrong. I'll post an update when I actually know something.

Thanks for the quick response!
User avatar
slembcke
Site Admin
Posts: 4166
Joined: Tue Aug 14, 2007 7:13 pm
Contact:

Re: Top down pivot constraint confusion

Post by slembcke »

Is it possible that you were using the position of the control body instead of the normal body to draw stuff?
Can't sleep... Chipmunks will eat me...
Check out our latest projects! -> http://howlingmoonsoftware.com/wordpress/
markhula
Posts: 188
Joined: Wed Feb 02, 2011 4:23 am
Contact:

Re: Top down pivot constraint confusion

Post by markhula »

Hey Scott,

Remove cpBodySlew because *everyone* tries to use it to 'force' things in place.
it causes all kinds of issues and it's not really required anymore.

Bin it :-)
dorthysmash
Posts: 5
Joined: Thu Mar 10, 2011 11:21 pm
Contact:

Re: Top down pivot constraint confusion

Post by dorthysmash »

Sigh, no. Had a *face palm* moment when I was piecing my code back together. I had separated initializing the pivot parameters into an initialization function that I forgot to call. So the pivot constraint parameters where never being set when I assumed that they were. I guess it was defaulting to maxForce of 0, which is why no velocity would move my body. Now I'm getting the results I want ^_^. So in conclusion, maxForce == 0 means your constraint an't doing nada (which makes sense).

Yea, cpBodySlew() is causing me small headaches in other areas. I had some collision objects that were mainly just there for detecting collisions (I would destroy them if anything hit them and tell the space to not process them). I was trying to slew their positions and instead just set body->p myself (cause I'm calculating the displacement each update anyways). Once I did that, it got rid of some strange jitteriness that I would run into every once in a while. Let me know if this is a bad idea.

So next question. I want my player to move at a constant velocity to a goal. I'm a applying a pretty high velocity to my control body and this ends up over shooting my goal position and then I get slung across the stage. What is the best practice for manually moving objects with constant velocity to a goal position? Feel like I'm shaving the yak here, cause I know how to do the calcs myself, just not sure how to get chipmunk to do it and play nicely.

And as I wrote that, I think I thought of the answer. So it looks like the problem was my simulated friction, aka maxForce, was too low so I would go sliding off into a frictionless oblivion. So I bumped up the maxForce and I'm stopping on a dime. I still don't have the controls exactly like I want, but at this point it just looks like I need to play with the numbers.

Code I'm using to set control body velocity.

Code: Select all

   if (cpvnear(goalPosition, body->p, 1.0f)) {
        controlBody->v = cpvzero;
        return;
    }
 
    CGPoint direction = cpvsub(goalPosition, body->p);
    direction = cpvnormalize(direction);
    _controlBody->v = cpvmult(direction, velocity);
Again thanks for all the help!
User avatar
slembcke
Site Admin
Posts: 4166
Joined: Tue Aug 14, 2007 7:13 pm
Contact:

Re: Top down pivot constraint confusion

Post by slembcke »

The code you posted at the end there is more or less what you want to do. The only problem you are running into now is that you need to take the stopping distance into account. I'll save you the derivation of the formula, but the stopping distance will be dist = (v^2*mass)/(2*force). So the maximum velocity that will allow you to stop before reaching your destination will be v = sqrt(2*dist*force/mass).

Try this instead:

Code: Select all

// Maximum speed that will allow you to stop before reaching the destination.
cpFloat maxStoppingSpeed = cpfsqrt(2.0f*cpvdist(goalPosition, body->p)*pivotMaxForce/body->m);

// Calculate the velocity that will move to the goalPosition in dt seconds (this is what cpBodySlew() calculates)
cpVect velocity = cpvmult(cpvsub(goalPosition, body->p), 1.0f/dt);

// Clamp the velocity's magnitude by the stopping speed and the objects max speed (which ever is smaller)
_controlBody->v = cpvclamp(velocity, cpfmin(maxSpeed, maxStoppingSpeed));
edit: whoops, forgot to include pivotMaxForce in the maxStoppingSpeed calculation.
Can't sleep... Chipmunks will eat me...
Check out our latest projects! -> http://howlingmoonsoftware.com/wordpress/
User avatar
slembcke
Site Admin
Posts: 4166
Joined: Tue Aug 14, 2007 7:13 pm
Contact:

Re: Top down pivot constraint confusion

Post by slembcke »

Being obsessive compulsive sometimes, I had to test this one out. It does overshoot by a little bit because Chipmunk (like every other rigid body physics library) is using euler integration. Now that I think about it, I came up with probably this exact same equation for the flight control computer in the Solaro game we have been making. I know I fixed the euler issue there... I'll see if I can find the code buried in the project somewhere.

Multiplying maxStoppingSpeed by like 0.9 will probably work just as well if you aren't obsessive compulsive. It will keep it from overshooting, but will cause the object to arrive some small fraction of a second second late to the stopping point.
Can't sleep... Chipmunks will eat me...
Check out our latest projects! -> http://howlingmoonsoftware.com/wordpress/
dorthysmash
Posts: 5
Joined: Thu Mar 10, 2011 11:21 pm
Contact:

Re: Top down pivot constraint confusion

Post by dorthysmash »

Just came to the same conclusion that I needed to calculate stopping distance. I came up with a different solution. I stole formula's for calculating breaking distance for car's off the internetz. This doesn't take into account the mass but I can control the acceleration/deacceleration. Just throwing it out there incase it sparks ideas for others. I'll use your suggestion and see what results I get as well. Thanks! This was an amazing amount of help!

Code: Select all

    // these values are cached, so i'm not recalculating them every tick 
    maxVelocity = 500.0f;
    acceleration = 1500.0f;
    stoppingDistance = 0.5f * maxVelocity * (maxVelocity / acceleration); // 0.5f * current velocity * stopping time

    // update velocity code, called very tick
    cpVect direction = cpvsub(goalPosition, body->p);
    float length = cpvlength(direction);
    
    // if at goal or user is not controlling us, then kill velocity
    if (length <= 1.0f || touch == nil) {
       controlBody->v = cpvzero;
       return;
    }
    
    // assume max velocity
    float velocity = maxVelocity;
    
    // if are with in the stopping distance, figure out our slow downed velocity
    if (length <= _stoppingDistance) {
        velocity = maxVelocity * (length / stoppingDistance);
    }
    
    // get our new direction and set to new velocity
    direction = cpvnormalize(direction);
    controlBody->v = cpvmult(direction, velocity);
edit: spacing in code snippet wasn't perfect, me being OCD
User avatar
slembcke
Site Admin
Posts: 4166
Joined: Tue Aug 14, 2007 7:13 pm
Contact:

Re: Top down pivot constraint confusion

Post by slembcke »

Hah, Ok. I'll take a look at that too. I guess I didn't handle the euler overshoot in Solaro. I figured out the euler version, but it added needless complexity and still left a problem where the object would oscillate over the target point. This works very well though. I guess I can deal with the 0.9f magic number. :p

Code: Select all

		// Distance to the target point + a radius
		cpFloat radius 2.0f; // a magic number big enough to avoid the oscillation
		cpFloat dist = cpfmax(cpvdist(goalPosition, body->p) - radius, 0.0f);
		
		// Maximum speed that will allow you to stop before reaching the destination.
		cpFloat maxStoppingSpeed = 0.9f*cpfsqrt(2.0f*dist*maxForce/body->m);
		
		// Calculate the velocity that will move to the goalPosition in dt seconds (this is what cpBodySlew() calculates)
		cpVect velocity = cpvmult(cpvsub(goalPosition, body->p), 1.0f/dt);

		// Clamp the velocity's magnitude by the stopping speed and the objects max speed (which ever is smaller)
		_controlBody->v = cpvclamp(velocity, cpfmin(maxSpeed, maxStoppingSpeed));
Can't sleep... Chipmunks will eat me...
Check out our latest projects! -> http://howlingmoonsoftware.com/wordpress/
Post Reply

Who is online

Users browsing this forum: No registered users and 3 guests