In cocos2d, you can ease in CCSprites and move them around in all kinds of ways. Most importantly - they can have easing in/out. For most games this is desirable for smooth movement etc.
id action = [CCMoveTo actionWithDuration:dur position:pos];
move = [CCEaseInOut actionWithAction:action rate:2];
[self runAction: move];
When moving a box2d body, the sprite attached to it is updated after the box2d step(). Moving the sprite and then updating the body is not an option here, as it entirely defeats the purpose of the physics framework.
So the other option, which I have successfully implemented, is to calculate the displacement, velocity and acceleration of a sprite by treating it as a mechanics entity in its own right. Each time I call my update() on the sprite so the character can decide where to move etc, my superclass also stores the previous position and velocity. These are stored as box2d compliant values by dividing by the PTM_RATIO.
In the subclass of CCSprite, called FMSprite:
-(CGPoint) displacement {
return ccpSub(self.position, lastPos);
}
-(b2Vec2) getSpriteVelocity:(ccTime)dt {
return b2Vec2(self.displacement.x / dt / PTM_RATIO,
self.displacement.y / dt / PTM_RATIO);
}
-(b2Vec2) getSpriteAccel:(ccTime)dt {
b2Vec2 currVel = [self getSpriteVelocity:dt];
if (dt == 0) {
return b2Vec2(0,0);
} else {
float accelX = (currVel.x - lastVel.x)/dt;
float accelY = (currVel.y - lastVel.y)/dt;
return b2Vec2(accelX, accelY);
}
}
// This is called each update()
-(void) updateLast:(ccTime)dt {
// MUST store lastVel before lastPos is updated since it uses displacement
lastVel = [self getSpriteVelocity:dt];
lastPos = ccp(self.X, self.Y);
}
// Leave this method untouched in subclasses
-(void) update:(ccTime)dt {
[self updateObject:dt];
// Store previous update values
[self updateLast:dt];
}
// Override this method in subclasses for custom functionality
-(void) updateObject:(ccTime)dt {
}
I have then subclassed "FMSprite" into "FMObject", which stores a b2Body etc.
In order to move the body, I must first move a sprite and track its acceleration, through which I can find the required force (using the mass) needed to follow the sprite's motion. Since I can't move the object's sprite (which is synchronized to the body), I make another sprite called a "beacon", add it as a child to the object, and move it around. All we need to do then is to have a function to synchronize the position of the box2d body with this beacon sprite using the forces I mentioned before.
-(void) followBeaconWithDelta:(ccTime)dt {
float forceX = [beacon getSpriteAccel:dt].x * self.mass;
float forceY = [beacon getSpriteAccel:dt].y * self.mass;
[self addForce:b2Vec2(forceX, forceY)];
}
The result is brilliant, a smooth easing motion of the b2body moving where ever you want it to, without playing around with any of its own forces, but rather copying that of a CCSprite and replicating its motion. Since it's all forces, it won't cause jittering and distortions when colliding with other b2Body objects. If anyone has any other methods to do this, please post an answer. Thanks!
FMObject
itself. – Aram Kocharyan