Rotate a body on touch on UIKit, first timer with Chipmunk.

Official forum for the Chipmunk2D Physics Library.
Post Reply
GustavPicora
Posts: 6
Joined: Sun Jul 03, 2011 5:00 am
Contact:

Rotate a body on touch on UIKit, first timer with Chipmunk.

Post by GustavPicora »

Hello all.

Im doing a UIKit-chipmunk app, and I need to rotate an object with momentum. I have successfully imported the sources and I created my shaw and body.

Right now I'm applying a force to the body when the touchesMoved, but the body never stops.

This is what I'm doing.

Code: Select all


void updateShape(void *ptr, void* unused) {  
    
    // Get our shape  
    
    cpShape *shape = (cpShape*)ptr;  
    
    // Make sure everything is as expected or tip & exit  
    
    if(shape == nil || shape->body == nil || shape->data == nil) {  
        
        NSLog(@"Unexpected shape please debug here...");  
        
        return;  
        
    }
    // Lastly checks if the object is an UIView of any kind  
    
    // and update its position accordingly  
    
    if([(__bridge NSObject *)shape->data isKindOfClass:[UIImageView class]]) {          
        CGAffineTransform t = ((__bridge UIImageView  *)shape->data).transform;
        t = CGAffineTransformRotate(t, shape->body->a);
        ((__bridge UIImageView  *)shape->data).transform = t;
    }  
    
    else       
        NSLog(@"The shape data wasn't updateable using this code.");  

}
setting up chipmunk

Code: Select all

- (void)setUpChipmunk{
 
    cpInitChipmunk(); // Start the engine
    space = cpSpaceNew(); // new space;
    space->gravity = cpv(0, -9.8*10);
  
    cpBody *staticBody = &space->staticBody;
	cpShape *shape;
    
	// Create segments around the edge of the screen.
	shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(0,0), cpv(480,0), 0.0f));
	shape->e = 1.0f; shape->u = 1.0f;
    
	shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(480,0), cpv(480,320), 0.0f));
	shape->e = 1.0f; shape->u = 1.0f;
    
	shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(480,320), cpv(0,320), 0.0f));
	shape->e = 1.0f; shape->u = 1.0f;
    
	shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(0,320), cpv(0,0), 0.0f));
	shape->e = 1.0f; shape->u = 1.0f;
    
    
    // create the circle shape that defines the cylinder
    cylinderBody = cpBodyNew(10, 1);

    // set the position
    cylinderBody->p = cpv(self.cylinderView.center.x, self.cylinderView.center.y);
    
    // add the shape to the space
    
    cpSpaceAddBody(space, cylinderBody);
    
    cpShape * cyliniderShape = cpCircleShapeNew(cylinderBody, self.cylinderView.frame.size.width/2, cpvzero);
    cyliniderShape->e = 0.0;
    cyliniderShape->u = 1.0f;
    cyliniderShape->data = (__bridge_retained CFTypeRef)self.cylinderView;
    if(cyliniderShape->data)
        NSLog(@"has data");  
    cpSpaceAddShape(space, cyliniderShape);      
    constraint = cpPivotJointNew(cylinderBody, staticBody, cylinderBody->p);
    cpSpaceAddConstraint(space, constraint);
  [NSTimer scheduledTimerWithTimeInterval:1.0f/60.0f target:self selector:@selector(tick:) userInfo:nil repeats:YES];

}

and my touches

Code: Select all


#pragma mark =================


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

    CGPoint p = [[touches anyObject] locationInView:self.view];
    self.initialTouchPoint = p;
    if([self insideOfCylinder:p]){
        
        cpConstraint * constr = cpPivotJointNew2(cylinderBody,&space->staticBody,cpv(p.x,p.y), cylinderBody->p);
        mouseJoint = cpSpaceAddConstraint(space, constr); 
        mouseJoint->maxForce = 10000.0f;
        mouseJoint->biasCoef = 0.0f;
  
    }

    

}

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

    if(mouseJoint != NULL){
         CGPoint p = [[touches anyObject] locationInView:self.view];
         cpPivotJointSetAnchr2(mouseJoint, p);  
         cpBodySetAngVel(cylinderBody,0.25); 
    }
  
}

- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    
    if(mouseJoint != NULL){
        cpConstraintDestroy(mouseJoint);
        mouseJoint = NULL;
    }
     CGPoint p = [[touches anyObject] locationInView:self.view];
    if(CGPointEqualToPoint(p,self.initialTouchPoint)){
        cpBodySetAngVel(cylinderBody,0); 
        NSLog(@"Stop");
    } 
    
}

So I was trying to follow the Demo, but I couldn't figure out when the force is applied. I migrated from Box2d into chipmunk because eI see it has many kool stuff, there i was able to create a mouseJoint and on move calculate a force an apply it, but i dunno if I can have that same approach here.

BTW the touchesMoved method i put there an if conditional just to try to stop the body.

Any ideas/help, I will very appreciate it.
Thx

Gustavo
GustavPicora
Posts: 6
Joined: Sun Jul 03, 2011 5:00 am
Contact:

Re: Rotate a body on touch on UIKit, first timer with Chipmu

Post by GustavPicora »

Hello All.

I have updated my touches methods to the following:

Code: Select all


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

    CGPoint p = [[touches anyObject] locationInView:self.view];
    self.initialTouchPoint = p;
    if([self insideOfCylinder:p]){
        
        mouseJoint = cpPivotJointNew2(mouseBody,cylinderBody,cpv(p.x,320-p.y), cylinderBody->p);
        mouseJoint->maxForce = 400.0f;
        mouseJoint->biasCoef = 0.3;
        cpSpaceAddConstraint(space, mouseJoint); 
  
    }

    

}

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

    
    if(mouseJoint != NULL){
        CGPoint p = [[touches anyObject] locationInView:self.view];
        p.y = 320 - p.y;
        cpPivotJointSetAnchr2(mouseJoint, p);  
    }
  
}

- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    
    cpSpaceRemoveConstraint(space, mouseJoint);
    cpConstraintFree(mouseJoint);
    mouseJoint = NULL; 
    
}
the cylinder rotates but it doesn't stop ever. it tries to stop but then it goes back again.

Any ideas?
User avatar
slembcke
Site Admin
Posts: 4166
Joined: Tue Aug 14, 2007 7:13 pm
Contact:

Re: Rotate a body on touch on UIKit, first timer with Chipmu

Post by slembcke »

You have this line in your touches moved method:
cpBodySetAngVel(cylinderBody,0.25);

You are telling Chipmunk to cause the object to rotate at 0.25 radians per second every time you move the touch. I don't think you want that line to be there at all.

Also, touch input events are very coarse. The OS only delivers them to you every few frames so you should smooth out the movement in between. I do the following in my demo code:
cpVect newPoint = cpvlerp(mouseBody->p, mousePoint, 0.25f);
mouseBody->v = cpvmult(cpvsub(newPoint, mouseBody->p), 60.0f);
mouseBody->p = newPoint;
mousePoint always contains the most recent mouse position. newPoint is a smoothed position of the mouse. 0.25 is the smoothing factor (0 < smoothing factor <= 1). The second line updates the mouseBody's velocity so the grabbing joint doesn't act mushy.

Box2D's mouse joint isn't anything magical. It basically just wraps a pivot joint and does exactly what you are already doing.
Can't sleep... Chipmunks will eat me...
Check out our latest projects! -> http://howlingmoonsoftware.com/wordpress/
GustavPicora
Posts: 6
Joined: Sun Jul 03, 2011 5:00 am
Contact:

Re: Rotate a body on touch on UIKit, first timer with Chipmu

Post by GustavPicora »

Hello.

Im doing this already with little better results.. but still it never stops.

Code: Select all

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

    
    if(mouseJoint != NULL){
         
        CGPoint p = [[touches anyObject] locationInView:self.view];
        cpVect actual = cpv(p.x,p.y);
//        cpVect newPoint = cpvlerp(touch_last, actual, 0.25f);
//        mouseBody->p = newPoint;
//        mouseBody->v = cpvmult(cpvsub(newPoint, touch_last), 60.0f);
//        touch_last = newPoint;        
        
        cpPivotJointSetAnchr2(mouseJoint, actual);	
    }
  
}


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

    UITouch * touch = [touches anyObject]; 
    CGPoint p = [touch locationInView:self.view];
    self.initialTouchPoint = p;
    if([self insideOfCylinder:p]){
        cpVect point = cpv(p.x,p.y);
        mouseJoint = cpPivotJointNew2(cylinderBody,&space->staticBody,cpBodyWorld2Local(cylinderBody, point),cpBodyWorld2Local(&space->staticBody, point));
        cpSpaceAddConstraint(space, mouseJoint); 
        mouseJoint->maxForce = 50000.0f;
        mouseJoint->biasCoef = 0.15f;       
        mouseJoint->maxBias = 4.0f;
        
        touch_last = point;        
    }

}

As you can see I had commented some lines in the touchesMoved, which are quite similar to what the demo had. but I didn't figured it out what else was I missing, now I'm trying this approach.. maybe I'm close to the right solution?

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

Re: Rotate a body on touch on UIKit, first timer with Chipmu

Post by slembcke »

Oh, I think I understand your problem now. It spins around the touch location as if it's frictionless and you want it to have friction? You'll need to configure a second joint (a cpSimpleMotor) to fake the friction for you.

Also, you were connecting the pivot between the static body and changing the anchor position. From what I could tell, mouse body was never actually used.

The smoothing code should go in your tick function not touches moved. That way it smoothes it out per frame. Sorry about the confusion.

So something like this:

Code: Select all

- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
	UITouch * touch = [touches anyObject]; 
	CGPoint p = [touch locationInView:self.view];
	self.initialTouchPoint = p;
	if([self insideOfCylinder:p]){
		// Shouldn't need this, cpVect and CGPoint are the same on iOS.
		//cpVect point = cpv(p.x,p.y);
		mouseJoint = cpPivotJointNew2(cylinderBody,&space->staticBody,cpBodyWorld2Local(cylinderBody, point),cpBodyWorld2Local(&space->staticBody, point));
		cpSpaceAddConstraint(space, mouseJoint); 
		mouseJoint->maxForce = 50000.0f;
		mouseJoint->biasCoef = 0.15f;       
		mouseJoint->maxBias = 4.0f;

		// Connect to mouseBody, not space.staticBody!
		mouseFriction = cpSimpleMotorNew(cylinderBody, mouseBody, 0.0);
		mouseFriction = maxForce = 1000.0f; // Tweak this for the friction amount.
		
		touch_last = point;        
	}
}

- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
	CGPoint p = [[touches anyObject] locationInView:self.view];
	touch_location = cpv(p.x,p.y); // Store the most recent touch to an instance variable, but don't do anything else
}

// in your tick method
- (void) tick {
	cpVect newPoint = cpvlerp(mouseBody->p, touch_location, 0.25f);
	// Change the position and velocity of the mouse body instead of changing the joint anchor.
	mouseBody->v = cpvmult(cpvsub(newPoint, mouseBody->p), 60.0f);
	mouseBody->p = newPoint;
	...
}
Can't sleep... Chipmunks will eat me...
Check out our latest projects! -> http://howlingmoonsoftware.com/wordpress/
GustavPicora
Posts: 6
Joined: Sun Jul 03, 2011 5:00 am
Contact:

Re: Rotate a body on touch on UIKit, first timer with Chipmu

Post by GustavPicora »

Hello.

First of all sorry for the duplicity in cocos2d forum. :(

second:

Thanks for the help. :) , not working still, actually is not moving now :(.

Code: Select all

-(void)tick:(NSTimer *)timer{
    
    cpVect newPoint = cpvlerp(mouseBody->p, touch_last, 0.25f);
    // Change the position and velocity of the mouse body instead of changing the joint anchor.
    mouseBody->v = cpvmult(cpvsub(newPoint, mouseBody->p), 60.0f);
    mouseBody->p = newPoint;
    
    // step the engine
    cpSpaceStep(space, timer.timeInterval);    
    cpSpaceHashEach(space->activeShapes, &updateShape, nil);  
    
}



#pragma mark =================


- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch * touch = [touches anyObject]; 
    CGPoint p = [touch locationInView:self.view];
    self.initialTouchPoint = p;
    if([self insideOfCylinder:p]){
        mouseJoint = cpPivotJointNew2(cylinderBody,&space->staticBody,cpBodyWorld2Local(cylinderBody, p),cpBodyWorld2Local(&space->staticBody, p));
        cpSpaceAddConstraint(space, mouseJoint); 
        mouseJoint->maxForce = 500000.0f;
        mouseJoint->biasCoef = 0.15f;       

        // Connect to mouseBody, not space.staticBody!
        mouseFriction = cpSimpleMotorNew(cylinderBody, mouseBody, 0.0);
        mouseFriction->maxForce = 100.0f; // Tweak this for the friction amount.   
        cpSpaceAddConstraint(space, mouseFriction); 
        touch_last = p;        
    }

}

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

    CGPoint p = [[touches anyObject] locationInView:self.view];
    touch_last = cpv(p.x,p.y); // Store the most recent touch to an instance variable, but don't do anything else
  
}

- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    
    if(mouseJoint){
        cpSpaceRemoveConstraint(space, mouseJoint);
        cpConstraintFree(mouseJoint);
        cpSpaceRemoveConstraint(space, mouseFriction);
        cpConstraintFree(mouseFriction);
        mouseJoint = NULL; 
        mouseFriction = NULL;
    }
    
}
and in my initializer I'm setting the cylinder body as

Code: Select all

....
    // create the circle shape that defines the cylinder
    
    CGFloat radius = self.cylinderView.frame.size.width/2;
    
    cylinderBody = cpBodyNew(10.0f, cpMomentForCircle(10, radius, radius, cpvzero));
    

    // set the position
    cylinderBody->p = cpv(self.cylinderView.center.x, 320-self.cylinderView.center.y);
    
    // add the shape to the space
    
    cpSpaceAddBody(space, cylinderBody);
    
    cpShape * cyliniderShape = cpCircleShapeNew(cylinderBody, radius, cpvzero);

    cyliniderShape->e = 0.5;
    
    cyliniderShape->u = 0.2f;
...

I have been modifying the friction max force but nothing still.


:(
GustavPicora
Posts: 6
Joined: Sun Jul 03, 2011 5:00 am
Contact:

Re: Rotate a body on touch on UIKit, first timer with Chipmu

Post by GustavPicora »

A quick update: :D

I changed the code to look like this":

Code: Select all

-(void)tick:(NSTimer *)timer{
    
    cpVect newPoint = cpvlerp(mouseBody->p, touch_last, 0.25f);
    // Change the position and velocity of the mouse body instead of changing the joint anchor.
    mouseBody->v = cpvmult(cpvsub(newPoint, mouseBody->p), 60.0f);
    mouseBody->p = newPoint;
    
    // step the engine
    cpSpaceStep(space, timer.timeInterval);    
    cpSpaceHashEach(space->activeShapes, &updateShape, nil);  
    
}


#pragma mark =================


- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch * touch = [touches anyObject]; 
    CGPoint p = [touch locationInView:self.view];
    self.initialTouchPoint = p;
    p.y = 320-p.y;
    if([self insideOfCylinder:p]){
        mouseJoint = cpPivotJointNew2(cylinderBody,mouseBody,cpBodyWorld2Local(cylinderBody, p),cpBodyWorld2Local(mouseBody, p));
        cpSpaceAddConstraint(space, mouseJoint); 
        mouseJoint->maxForce = 100000.0f;
        mouseJoint->biasCoef = 0.2f;       
       
    }

    self.lastTimestamp = [event timestamp];
}

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

    CGPoint p = [[touches anyObject] locationInView:self.view];
    touch_last = cpv(p.x,320-p.y); // Store the most recent touch to an instance variable, but don't do anything else
  
}

- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    
    if(mouseJoint){
        cpSpaceRemoveConstraint(space, mouseJoint);
        cpConstraintFree(mouseJoint);

        mouseJoint = NULL; 

        
        
    }
    
}
So it moves and stop , think that was preventing form stoping it was the CGAffineTransofrm I was making.

But the movement its not nice, I put the finger on and well it stops, no problem, but if I move the finger too fast it just start rotating really weird, like on the opposite direction from the swipe, or jumps the rotation.
If I swipe gently its much better but not as expected. :(

Any ideas?
User avatar
slembcke
Site Admin
Posts: 4166
Joined: Tue Aug 14, 2007 7:13 pm
Contact:

Re: Rotate a body on touch on UIKit, first timer with Chipmu

Post by slembcke »

Hmm. I think I'd need to see a video.

A couple other things:

You make your mouse joint like this:
mouseJoint = cpPivotJointNew2(cylinderBody,mouseBody,cpBodyWorld2Local(cylinderBody, p),cpBodyWorld2Local(mouseBody, p));

But the mouseBody anchor should always be centered on the mouseBody (0, 0). It could be causing problems if it's slightly off center from the last touch.
mouseJoint = cpPivotJointNew2(cylinderBody, mouseBody, cpBodyWorld2Local(cylinderBody, p), cpvzero);

I'd also get rid of self.initialTouchPoint maybe. Just set the mouseBody position and touch_last instead.

Lastly, in your tick method, you should be using a constant timestep and you need the time interval used to step the space and calculate the mouse velocity to match. If you'r game runs at 60fps, use 1/60 or do something fancier.

Here is what it looks like with all those changes:

Code: Select all

-(void)tick:(NSTimer *)timer{
    cpFloat dt = 1.0/60.0f; // I strongly suggest using a fixed timestep
    
    cpVect newPoint = cpvlerp(mouseBody->p, touch_last, 0.25f);
    // Change the position and velocity of the mouse body instead of changing the joint anchor.
    mouseBody->v = cpvmult(cpvsub(newPoint, mouseBody->p), 1.0/dt);
    mouseBody->p = newPoint;
    
    // step the engine
    cpSpaceStep(space, dt); // 
    cpSpaceHashEach(space->activeShapes, &updateShape, nil);  
}


#pragma mark =================


- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch * touch = [touches anyObject]; 
    CGPoint p = [touch locationInView:self.view];
    self.initialTouchPoint = p;
    p.y = 320-p.y;
    
    touch_last = p;
    mouseBody->p = p;
    mouseBody->v = cpvzero;
    
    if([self insideOfCylinder:p]){
        mouseJoint = cpPivotJointNew2(cylinderBody, mouseBody, cpBodyWorld2Local(cylinderBody, p), cpvzero);
        cpSpaceAddConstraint(space, mouseJoint); 
        mouseJoint->maxForce = 100000.0f;
        mouseJoint->biasCoef = 0.2f;       
    }

    self.lastTimestamp = [event timestamp];
}

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

    CGPoint p = [[touches anyObject] locationInView:self.view];
    touch_last = cpv(p.x,320-p.y); // Store the most recent touch to an instance variable, but don't do anything else
  
}

- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    
    if(mouseJoint){
        cpSpaceRemoveConstraint(space, mouseJoint);
        cpConstraintFree(mouseJoint);

        mouseJoint = NULL; 
    }
}
Can't sleep... Chipmunks will eat me...
Check out our latest projects! -> http://howlingmoonsoftware.com/wordpress/
GustavPicora
Posts: 6
Joined: Sun Jul 03, 2011 5:00 am
Contact:

Re: Rotate a body on touch on UIKit, first timer with Chipmu

Post by GustavPicora »

Hello.

It worked Like CHARM!!!!! :D
Im gonna check each line and check what was making me problem.. THANKS A LOT.
User avatar
slembcke
Site Admin
Posts: 4166
Joined: Tue Aug 14, 2007 7:13 pm
Contact:

Re: Rotate a body on touch on UIKit, first timer with Chipmu

Post by slembcke »

Ah, good. :) Sorry it took a couple days to pin down all the problems.
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 8 guests