Objects slowing down even before impact

Official forum for the Chipmunk2D Physics Library.
Post Reply
User avatar
Yoape
Posts: 3
Joined: Tue May 07, 2013 1:39 am
Contact:

Objects slowing down even before impact

Post by Yoape »

Hi,

I have just started playing around with Chipmunk for Objective-C (not Objective-Chipmunk though). Let me just start by saying thank you to @slembcke for creating this great library and sharing it!

After having played with a bunch of different demos and tested the code I have encountered a really strange behavior. I have no idea why this is happening to me and after looking at the running demos in the App I can download from App Store I'm pretty sure it shouldn't happen this way either. My impacts are behaving like they know about the impact before they happen and slows down (decelerates really) to finaly softly settle on a static ground. A simple scenario where I drop box shapes onto a static ground shape and let the gravity pull it down makes it look like a little moonlander that brakes for impact a little bit before it hits the ground and then nicely parks the lander on the ground. The actual distance where it starts to slow down depends on the velocity of the falling body.

I'm convinced that it is something really trivial I have missed in my code and the answer will likely come back as 'dude, didn't you read the docs?' and I did, but I can't figure out what it is that I have missed so please if anyone can figure out what is wrong with my code I would be very greatful. I have reproduced the strange behavior in as simplified code as I could, simply using CALayers to draw the shapes and not relying on anything else in there.

Code: Select all

#import <QuartzCore/QuartzCore.h>
#import "chipmunk.h"

#define MAX_DT (1.0/15.0)

@interface CEPhysicsObject : NSObject{
    CALayer *layer;
    cpBody *body;
}
@property (nonatomic, strong) CALayer *layer;
@property (nonatomic) cpBody *body;

@end

@implementation CEPhysicsObject
@synthesize layer, body;

@end

@interface CEPhysicsView()
{
    cpSpace *space;

    NSTimer *gameTimer;
    CADisplayLink *_displayLink;
    
    double t;
    double dt;    
    NSTimeInterval currentTime;
    double accumulator;

    NSMutableArray *rocks;
    CALayer *groundLayer;
    
}

@end

@implementation CEPhysicsView

- (id)init
{
    self = [super init];
    if (self) {
        [self initialize];
    }
    return self;
}

- (void)initialize
{
    t = 0.0;
    dt = 1.0f/60.0f;
    accumulator = 0.0;
    
    space = cpSpaceNew();
    
    // Create gravity
    space->gravity = cpv(0, -900);
    space->iterations = 30;
    
    // Create an empty space.
    cpSpaceSetGravity(space, space->gravity);
    
    // Create a ground shape
    cpBody *body = cpSpaceGetStaticBody(space);
    cpShape *ground = cpSegmentShapeNew(body, cpv(20, -400), cpv(180, -400), 10);
    cpShapeSetFriction(ground, 1.0f);
    cpShapeSetElasticity(ground, 1.0f);
    cpSpaceAddShape(space, ground);
    
    // Draw the ground shape
    groundLayer = [CALayer layer];
    groundLayer.backgroundColor = [UIColor blackColor].CGColor;
    groundLayer.frame = CGRectMake(10, 390, 180, 20);
    [self.layer addSublayer:groundLayer];
    
    // Create rocks array
    rocks = [NSMutableArray new];
    
    _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(tick:)];
    [_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
}

-(void)tick:(CADisplayLink *)displayLink
{
	NSTimeInterval newTime = _displayLink.timestamp;
    double frameTime = MIN(newTime - currentTime, MAX_DT);
    currentTime = newTime;
    accumulator += frameTime;
    
    // Take steps at controlled framerate
    while ( accumulator >= dt )
    {
        cpSpaceStep(space, dt);
        t += dt;
        accumulator -= dt;
    }

    // Print out the 'framerate'
    NSLog(@"%f %f", 1.0f/dt, newTime);

    // Update positions (i.e. render)
    for (CEGameRock *rock in rocks)
    {
        rock.layer.position = CGPointMake(rock.body->p.x, -rock.body->p.y);
    }
    [self setNeedsDisplay];
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    CEPhysicsObject *rock = [CEPhysicsObject new];
    
    // Create a rock
    rock.layer = [CALayer layer];
    rock.layer.backgroundColor = [UIColor grayColor].CGColor;
    rock.layer.frame = CGRectMake(90, 0, 20, 20);
    [self.layer addSublayer:rock.layer];
    
    // Create a box
    cpFloat mass = 1.0;
    cpFloat moment = cpMomentForBox(mass, 20.0, 20.0);
    rock.body = cpSpaceAddBody(space, cpBodyNew(mass, moment));
    cpShape *boxShape = cpSpaceAddShape(space, cpBoxShapeNew(rock.body, 20.0, 20.0));
    cpShapeSetFriction(boxShape, 1.0f);
    cpShapeSetElasticity(boxShape, 0.0f);
    cpBodySetPos(rock.body, cpv(90, 0));

    rock.layer.position = CGPointMake(rock.body->p.x, -rock.body->p.y);
    [rocks addObject:rock];
}

@end
User avatar
slembcke
Site Admin
Posts: 4166
Joined: Tue Aug 14, 2007 7:13 pm
Contact:

Re: Objects slowing down even before impact

Post by slembcke »

Very strange. I don't see anything wrong with the physics part of your code. I know CoreAnimation likes to add a lot of implicit animations to *everything* you do to smooth things out. Are you sure it's not actually a rendering issue then? When working with Chipmunk/UIKit, I've always set the transform of a UIView. Maybe that doesn't trigger an implicit animation while setting the position of a CALayer does? I dunno.

Also, this line of code is effectively a no-op: cpSpaceSetGravity(space, space->gravity); As Chipmunk has grown more complicated, it's recommended to use the setter functions instead of directly setting the fields, though either generally works fine. Calling a most setter functions on a cpBody will cause it to wake up for instance, setting the variable directly will not.
Can't sleep... Chipmunks will eat me...
Check out our latest projects! -> http://howlingmoonsoftware.com/wordpress/
User avatar
Yoape
Posts: 3
Joined: Tue May 07, 2013 1:39 am
Contact:

Re: Objects slowing down even before impact

Post by Yoape »

Thank you! You were absolutely right about CALayer being the culprit in here. After having looked at the actual physics values generated by Chipmunk, not how my renderer used them, it became clear that it was Core Animation doing exactly what you said, trying to smooth out my updates to the position. For future reference, it does the same to transformations as well. For anyone trying to do stuff with CALayers or UIKit the trick is to explicitly tell CA not to apply animations to your updated properties by calling

Code: Select all

[CATransaction setDisableActions:YES]; 
before you update positions etc. and you don't want CA to 'help' you. When you are done just call

Code: Select all

[CATransaction setDisableActions:NO]; 
to turn it back on.

Thanks for the quick answer :D. Chipmunk rocks!
Post Reply

Who is online

Users browsing this forum: No registered users and 24 guests