Page 1 of 1

Manually Moving the Body

Posted: Sun Apr 25, 2010 10:29 am
by Xcode
Well do i need to rewrite cpBodyUpdatePosition if i have to move the body manually?
I have a ball falling off(bcoz of gravity) , if user touches the ball during flight and drags, its position should change.. One way was to just changing the body's position,,,,but i read in some post that changing just the position is like teleporting the body(it keeps its original velocity and other paramters) . So whats the correct way of doing it in Chipmunk...

Thanks for your help :)

Re: Manually Moving the Body

Posted: Sun Apr 25, 2010 12:41 pm
by slembcke
You will rarely need to change the position update function. The easiest thing to do is just to change the velocity update function to call cpBodySlew() to set the velocity so that it will move to the desired place.

Probably a better solution of you want nice dragging behavior is to implement the mouse grabbing controls using a soft joint like the demos do. It's pretty simple:

Code: Select all

static void
click(int button, int state, int x, int y)
{
	if(button == GLUT_LEFT_BUTTON){
		if(state == GLUT_DOWN){
			cpVect point = mouseToSpace(x, y);
		
			cpShape *shape = cpSpacePointQueryFirst(space, point, GRABABLE_MASK_BIT, 0);
			if(shape){
				cpBody *body = shape->body;
				mouseJoint = cpPivotJointNew2(mouseBody, body, cpvzero, cpBodyWorld2Local(body, point));
				mouseJoint->maxForce = 50000.0f;
				mouseJoint->biasCoef = 0.15f;
				cpSpaceAddConstraint(space, mouseJoint);
			}
		} else if(mouseJoint){
			cpSpaceRemoveConstraint(space, mouseJoint);
			cpConstraintFree(mouseJoint);
			mouseJoint = NULL;
		}
	}
}
This enables and disables the joint.

Code: Select all

	cpVect newPoint = cpvlerp(mousePoint_last, mousePoint, 0.25f);
	mouseBody->p = newPoint;
	mouseBody->v = cpvmult(cpvsub(newPoint, mousePoint_last), 60.0f);
	mousePoint_last = newPoint;
This updates the position and velocity of the cpBody attached to the mouse cursor.

Re: Manually Moving the Body

Posted: Mon Apr 26, 2010 12:14 pm
by Xcode
Hey Thanks, that really worked :)

Re: Manually Moving the Body

Posted: Wed May 05, 2010 9:42 am
by byers
hey there,

this topic is somewhat related, but if it should be in it's own post, apologies...

i'm working on a cocos2d game which has a bunch of objects that need to be dragged around.

using the technique from the tank example, i'm trying to port the mousejoint constraint logic to have my object body properly respond to the touchBegan and touchMoved events.

i'm fairly new to cocos2d and i'm not quite sure about how to "connect" the new movements of the joints/mousebody and how to update the position of the sprite that is set to the shape's data property.

below is a snippet from what i've done so far. i'm at a loss to determine where is the recommended place to update the sprite object's position/orientation? or am i going down the wrong path completely?!?!??!

Code: Select all


-(id) init {
	if( (self=[super init])) {
		
		self.isTouchEnabled = YES;
		
		CGSize wins = [[CCDirector sharedDirector] winSize];
		cpInitChipmunk();
		
		mouseBody = cpBodyNew(INFINITY, INFINITY);
		
		cpBody *staticBody = cpBodyNew(INFINITY, INFINITY);
		space = cpSpaceNew();
		cpSpaceResizeStaticHash(space, 400.0f, 40);
		cpSpaceResizeActiveHash(space, 100, 600);
		
		space->gravity = ccp(0, 0);
		space->elasticIterations = space->iterations;

		[self schedule: @selector(step:)];
}

-(void) step {

	int steps = 1;
	cpFloat dt = 1.0f/60.0f/(cpFloat)steps;

	for(int i=0; i<steps; i++){
		// turn the control body based on the angle relative to the actual body
		cpVect mouseDelta = cpvsub(mousePoint, spaceBody->p);
		
		cpFloat turn = cpvtoangle(cpvunrotate(spaceBody->rot, mouseDelta));
		
		cpBodySetAngle(spaceControlBody, spaceBody->a - turn);
		
		// drive the tank towards the mouse
		if(cpvnear(mousePoint, spaceBody->p, 30.0)){
			spaceControlBody->v = cpvzero; // stop
		} else {
			cpFloat direction = (cpvdot(mouseDelta, spaceBody->rot) > 0.0 ? 1.0 : -1.0);
			spaceControlBody->v = cpvrotate(spaceBody->rot, cpv(30.0f*direction, 0.0f));
		}
		cpSpaceStep(space, dt);
	}

}

-(void) makeSpaceShips {
	
	CGSize winSize = [[CCDirector sharedDirector] winSize];

	CCTexture2D * texture = [[CCTextureCache sharedTextureCache] addImage:@"space.png"];
	spaceOne = [[CCSprite alloc] initWithTexture:texture];
	spaceOne.rotation = -90.f;
	spaceOne.position = CGPointMake(winSize.width/2 - (spaceOne.contentSize.width/2) + 20, 100);
	[self addChild:spaceOne z:10];

	// 86 x 47
	cpVect oneVerts[] = {
	cpv(43, 23.5),
	cpv(43, -23.5),
	cpv(-43, -23.5), 
	cpv(-43, 23.5), 
	};
	
	spaceBody = cpBodyNew(100.0, INFINITY);
	spaceBody->p = cpv(winSize.width/2 - (spaceOne.contentSize.width/2) + 20, 100);
	cpSpaceAddBody(space, spaceBody);
	
	cpShape * spaceOneShape = cpPolyShapeNew(spaceBody, 4, oneVerts, cpvzero);
	spaceOneShape->data = spaceOne;
	spaceOneShape->e = 0.0;
	spaceOneShape->u = 0.0;
	spaceOneShape->collision_type = 1;
	cpSpaceAddShape(space, spaceOneShape);
	
	spaceControlBody = cpBodyNew(INFINITY, INFINITY);
	
	cpConstraint * pivot = cpSpaceAddConstraint(space, cpPivotJointNew2(spaceControlBody, spaceBody, cpvzero, cpvzero));
	pivot->biasCoef = 0.0f; // disable joint correction
	pivot->maxForce = 10000.0f; // emulate linear friction

	cpConstraint * gear = cpSpaceAddConstraint(space, cpGearJointNew(spaceControlBody, spaceBody, 0.0f, 1.0f));
	gear->biasCoef = 1.0f; // limit angular correlation rate
	gear->maxBias = 1.0f; // limit angular correction rate
	gear->maxForce = 500000.f; // emulate angular friction
	
}

-(void) ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
	
	UITouch *myTouch = [touches anyObject];
	
	CGPoint location = [myTouch locationInView: [myTouch view]];
	location = [[CCDirector sharedDirector] convertToGL: location];
	
	cpVect point =  [self convertTouchToNodeSpace:myTouch]; //cpv(location.x, location.y);
	mousePoint = point;
	
	cpShape *shape = cpSpacePointQueryFirst(space, location, GRABABLE_MASK_BIT, 0);
	if ( shape ) {
		cpBody *body = shape->body;
		
		mousePoint_last = mousePoint;
		mouseBody->p = mousePoint;
		
		mouseJoint = cpPivotJointNew2(mouseBody, body, cpvzero, cpBodyWorld2Local(body, location));
		mouseJoint->maxForce = 50000.0f;
		mouseJoint->biasCoef = 0.15f;
		cpSpaceAddConstraint(space, mouseJoint);
	}
	
}

-(void) ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
	
	UITouch *myTouch = [touches anyObject];
	
	CGPoint location = [myTouch locationInView: [myTouch view]];
	location = [[CCDirector sharedDirector] convertToGL: location];
	
	cpVect point = [self convertTouchToNodeSpace:myTouch]; // cpv(location.x, location.y);
	mousePoint = point;
	
}

-(void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {

	if (cpArrayContains(space->constraints, mouseJoint)) {
		cpSpaceRemoveConstraint(space, mouseJoint);
		cpConstraintFree(mouseJoint);
		mouseJoint = nil;
	}
	
}


Re: Manually Moving the Body

Posted: Wed May 05, 2010 10:26 am
by byers
ok, figured out the problem. from the tick/display method i needed to first properly set the latest mouse position, then iterate through my set of active sprites and update their position / rotation.

Re: Manually Moving the Body

Posted: Mon Nov 08, 2010 8:58 am
by barabashka
Hi! Here's a way i'm doing that.

Code: Select all

cpVect mousePosLast;
cpBody *dragBody = 0;

void clickFunc(int button, int state, int x, int y)
{
	if( button == GLUT_LEFT_BUTTON )
	{
		if( state == GLUT_DOWN )
		{
			mousePosLast = mouseToSpace(x, y);

			cpShape *shape = cpSpacePointQueryFirst(space, mousePosLast, CP_ALL_LAYERS, CP_NO_GROUP);

			dragBody = (shape) ? shape->body : 0;
		}
		else
			dragBody = 0;
	}
}

void dragFunc(int x, int y)
{
	if( dragBody )
	{
		cpVect mousePos = mouseToSpace(x, y);

		// Update body's position
		dragBody->p = cpvadd(dragBody->p, cpvsub(mousePos, mousePosLast));
		// Stop body. Isn't necessary
		dragBody->v = cpvzero;
		
		// Walk throught body's shapes and update space hash for them
		cpShape *shapes = dragBody->shapesList;

		while( shapes )
		{
			cpSpaceRehashShape(space, shapes);
			shapes = shapes->next;
		}

		mousePosLast = mousePos;

		glutPostRedisplay();
	}
}