Child Rogue Bodies with shapes that don't follow

Official forum for the Chipmunk2D Physics Library.
Post Reply
pkclsoft
Posts: 5
Joined: Mon Jun 11, 2012 12:10 am
Contact:

Child Rogue Bodies with shapes that don't follow

Post by pkclsoft »

Hi

I'm using Cocos2d 2.0 with Chipmunk. I have a sprite A that moves across the screen, can rotate to change direction arbitrarily and move again.

Orbiting A is a child sprite B. B is a child because it makes it easy to let the child actions worry about moving in a circle around cpv(0,0). The beauty of this is that while A moves around the screen, B follows it whilst also following it's orbit. It's like having the earth orbit the sun all the time having the moon orbiting the earth as the earth follows it's path.

A is subject to Chipmunks physics, and is an active body with a shape for collision detection.
B is a rogue body so that it is not subject to Chipmunk physics, but has a shape for the purposes of collision detection with other shapes (not A). B is also a subclass of CCPhysicsSprite.

What I'm finding is that B is not colliding with anything, and having used CCPhysicsDebugNode to see what is happening, I can see that although the Cocos2D sprite that is B is in the correct place, orbiting A, the Chipmunk shape for B is orbiting the bottom left corner of the screen.

I've tried all manner of convertToWorldSpace: calls, overriding setPosition, not using CCPhysicsSprite and using a cut down copy of cpSprite from an old copy of Kobold, but no matter what I try I just can't get the space to follow sprite B in the correct position on the screen.

One other bit of information is that I note that when A rotates to change direction of travel, this of course affects the orbit of A in that is can appear to speed up or slow down. I don't mind this effect, but what concerns me is that the space that is orbiting the bottom left seems to get out of sync with B. It continues to rotate around the screen corner, but it's position in the orbit is no longer in sync with B's position in it's orbit around A.

I have a nagging feeling that this is going to be harder than I thought. I had figured that the space would automatically overlay the body, and that if the body, which is visually represented by the sprite is in the right place, then the shape would be too. With one experiment I made B an active body by adding it to the space, and that showed the body AND shape in the same orbit around the screen corner whilst the sprite continued to orbit the correct position on screen.

I suspect I could fix this by adding B as a child of A's parent layer, but that would mean that I'd have to manually move A to follow B whilst maintaining it's orbit.

I'd love some advice.

Thanks

pkclsoft

Here is the code for the implementation of B:

Code: Select all

#import "Sweeper.h"
#import "GameConstants.h"
#import "GameLayer.h"

@implementation Sweeper {
    
    GameLayer *device;

}

void sweeperBodyUpdatePosition(cpBody *body, cpFloat dt) {
//    cpBodyUpdatePosition(body, dt);
}

- (void) initPhysicsInSpace:(cpSpace*)inSpace {    
    const float elasticity = 0.0f;
    const float friction = 0.0f;
    
    cpBody *body = cpBodyNew(10000.0, cpMomentForCircle(10000.0, self.contentSize.width/2.0, self.contentSize.width/2.0, cpvzero));

    body->p = [CocosUtil screenCentre]; // starting place; this will be obsolete as soon as self is added to a parent
    body->data = self;
    body->w_limit = 0;
    body->v_limit = 0;
    
    cpShape* shape = cpCircleShapeNew(body, self.contentSize.width/2.0, cpvzero);
    shape->e = elasticity;
    shape->u = friction;
    shape->group = BALL_GROUP;
    shape->collision_type = kCollisionTypeSweeper;
    shape->data = self;
    shape->sensor = YES;
    cpSpaceAddShape(inSpace, shape);
    
    [self setBody:body];
}

- (id) initOnDevice:(GameLayer*)onDevice {
    self = [super initWithSpriteFrameName:@"spriteB.png"];
    
    if (self != nil) {
        [self initPhysicsInSpace:[onDevice space]];

        device = [onDevice retain];
    }

    return self;
}

- (void) dealloc {
    if (device != nil) {
        [device release];
    }
    
    [super dealloc];
}

+ (Sweeper*) sweeperOnDevice:(GameLayer*)onDevice {
    return [[[Sweeper alloc] initOnDevice:onDevice] autorelease];
}

@end
pkclsoft
Posts: 5
Joined: Mon Jun 11, 2012 12:10 am
Contact:

Re: Child Rogue Bodies with shapes that don't follow

Post by pkclsoft »

I've answered my own question (hmmm, maybe I should have asked on StackOverflow).

I could see that the Chipmunk shape/body were in local coordinates when they needed to be in world coordinates, but the problem was that trying to convert them to world coordinates in my subclass by overriding setPosition didn't work because this confused the nodeToParentTransform() message in CCPhysicsSprite which was expecting coordinates in the Node space of the parent, not world space.

So, as a hack to prove my theory I changed CCPhysicsSprite to store the cps value received in setPosition into a local property, and then call cpBodySetPos() with the world coordinates. Here are the setter/getter:

Code: Select all

// Override the setters and getters to always reflect the body's properties.
-(CGPoint)position
{
	return normalPos;
}

-(void)setPosition:(CGPoint)position
{
    normalPos = position;
    
	cpBodySetPos(_body, [self.parent convertToWorldSpace:position]);
}
Then, in nodeToParentTransform() I changed it to use the saved position, not the world position:

Code: Select all

-(CGAffineTransform) nodeToParentTransform
{
	cpVect rot = (_ignoreBodyRotation ? cpvforangle(-CC_DEGREES_TO_RADIANS(rotationX_)) : _body->rot);
	CGFloat x = normalPos.x + rot.x*-anchorPointInPoints_.x - rot.y*-anchorPointInPoints_.y;
	CGFloat y = normalPos.y + rot.y*-anchorPointInPoints_.x + rot.x*-anchorPointInPoints_.y;
	
	if(ignoreAnchorPointForPosition_){
		x += anchorPointInPoints_.x;
		y += anchorPointInPoints_.y;
	}
	
	return (transform_ = CGAffineTransformMake(rot.x, rot.y, -rot.y,	rot.x, x,	y));
}
This works a treat, but it's not enough for me. I'll put that out and come up with a solution that doesn't involve me hacking the Cocos2D library code.

Just thought I'd share my solution as I know it's something that others might want.

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

Re: Child Rogue Bodies with shapes that don't follow

Post by slembcke »

Is there a reason why you need it to be a child sprite? On the physics side, everything needs to be done in absolute coordinates (or at least in the same coordinate space), so hierarchical transforms make very little sense.

To make it's orbit follow the parent you really just need a pointer to A and add B's position to that. That's really all the more it's giving you in this case. If you did anything more complicated than translation of A, you'd be very much out of luck. Rotating, scaling, or something more exotic like skewing wouldn't work without a lot of hacks.
Can't sleep... Chipmunks will eat me...
Check out our latest projects! -> http://howlingmoonsoftware.com/wordpress/
pkclsoft
Posts: 5
Joined: Mon Jun 11, 2012 12:10 am
Contact:

Re: Child Rogue Bodies with shapes that don't follow

Post by pkclsoft »

Thanks for your reply @slembcke. Sprite A can indeed rotate at will. It's ability to rotate causes really interesting game play when the orbit of B can be accelerated or decelerated as A spins on bits center.

If I use the pointer as you suggest then B will lose its ability to be affected by the rotation of A but as you say it would mean B would only need a single coordinate for itself instead of what I've done.

I should also note that A and the layer containing it can at times be scaled so that will affect things too.

This is great stuff. Even if this game never takes off it has my mind engaged.

Thanks
pkclsoft
Posts: 5
Joined: Mon Jun 11, 2012 12:10 am
Contact:

Re: Child Rogue Bodies with shapes that don't follow

Post by pkclsoft »

Actually your suggestion may end up having to be the way to go after all.

Sprite B also has a particle system on it and I'm finding that because its a child the positiontypefree option doesn't work.

A patch to v1.1 if cocos2d added a new position type called world but this hasn't made it into v2.0. I tried taking the patch into my 2.0 clone but my head started to overheat.

It may just be easier to cop out and make B a sibling of A and kill two birds with one stone. It means losing a neat effect when A rotates to match its bearing but such is life.

Thx.
Post Reply

Who is online

Users browsing this forum: No registered users and 7 guests