I got a quite strange bug. It is difficult to explain it in two words, but i'll try to do this in short.
- My b2Body has restitution, friction, density, mass and collision group.
- I'm controlling my b2Body via setting linear velocity to it (called on every iteration):
(void)moveToDirection:(CGPoint)direction onlyHorizontal:(BOOL)horizontal {
b2Vec2 velocity = [controlledObject getBody]->GetLinearVelocity();
double horizontalSpeed = velocity.x + controlledObject.acceleration * direction.x;
velocity.x = (float32) (abs((int) horizontalSpeed) < controlledObject.runSpeed
? horizontalSpeed
: controlledObject.maxSpeed * direction.x);
if (!horizontal)
{
velocity.y = velocity.y + controlledObject.runSpeed * direction.y;
}
[controlledObject getBody]->SetLinearVelocity(velocity);
}
- My floor is static b2Body, it has as restitution, friction, density, mass and same collision group
- in some reason, I'm setting b2Body's friction of my Hero to zero when it is moving, and returning it to 1 when he stops.
- When I'm pushing run button, hero runs. when i'm releasing it, he stops. All of this works perfect.
- On jumping, I'm setting linear velocity to my Hero:
(void)jump {
b2Vec2 velocity = [controlledObject getBody]->GetLinearVelocity();
velocity.y = velocity.y + [[AppDel cfg] getHeroJumpVlelocity];
[controlledObject getBody]->SetLinearVelocity(velocity);
}
- If I'll run, jump, and release run button, while he is in air, all will work fine.
- And here is my problem: If I'll run, jump, and continue running on landing (or when he goes from one static body to another: there is small fall, probably), Hero will start move, like he has no friction, but he has! I checked this via beakpoints: he has friction, but I can move left of right, and he will never stop, until i'll jump (or go from one static body to another), with unpressed running button.
I already tried:
- Set friction to body on every iteration
- double-check am I setting friction to right fixture.
- Set Linear Damping to Hero: his move slows down on gugged moveing.
A little more code:
I have a sensor and body fixtures in my hero:
(void) addBodyFixture
{
b2CircleShape dynamicBox;
dynamicBox.m_radius = [[AppDel cfg] getHeroRadius];
b2FixtureDef bodyFixtureDef;
bodyFixtureDef.shape = &dynamicBox;
bodyFixtureDef.density = 1.0f;
bodyFixtureDef.friction = [[AppDel cfg] getHeroFriction];
bodyFixtureDef.restitution = [[AppDel cfg] getHeroRestitution];
bodyFixtureDef.filter.categoryBits = 0x0001;
bodyFixtureDef.filter.maskBits = 0x0001;
bodyFixtureDef.filter.groupIndex = 0;
bodyFixtureDef.userData = [NSNumber numberWithInt:FIXTURE_BODY];
[physicalBody addFixture:bodyFixtureDef];
}
(void) addSensorFixture
{
b2CircleShape sensorBox;
sensorBox.m_radius = [[AppDel cfg] getHeroRadius] * 0.95;
sensorBox.m_p.Set(0, -[[AppDel cfg] getHeroRadius] / 10);
b2FixtureDef sensor;
sensor.shape = &sensorBox;
sensor.filter.categoryBits = 0x0001;
sensor.filter.maskBits = 0x0001;
sensor.filter.groupIndex = 0;
sensor.isSensor = YES;
sensor.userData = [NSNumber numberWithInt:FIXTURE_SENSOR];
[physicalBody addFixture:sensor];
}
Here I'm tracking is hero in air:
void FixtureContactListener::BeginContact(b2Contact* contact)
{
// We need to copy out the data because the b2Contact passed in
// is reused.
Squirrel *squirrel = (Squirrel *)contact->GetFixtureB()->GetBody()->GetUserData();
if (squirrel)
{
[squirrel addContact];
}
}
void FixtureContactListener::EndContact(b2Contact* contact)
{
Squirrel *squirrel = (Squirrel *)contact->GetFixtureB()->GetBody()->GetUserData();
if (squirrel)
{
[squirrel removeContact];
}
}
Here is Hero's logic on contacts:
- (void) addContact
{
if (contactCount == 0)
[self landing];
contactCount++;
}
- (void) removeContact
{
contactCount--;
if (contactCount == 0)
[self flying];
if (contactCount <0)
contactCount = 0;
}
- (void)landing
{
inAir = NO;
acceleration = [[AppDel cfg] getHeroRunAcceleration];
[sprite stopAllActions];
(running ? [sprite runAction:[self runAction]] : [sprite runAction:[self standAction]]);
}
- (void)flying
{
inAir = YES;
acceleration = [[AppDel cfg] getHeroAirAcceleration];
[sprite stopAllActions];
[self flyAction];
}
Here is Hero's moving logic:
- (void)stop
{
running = NO;
if (!inAir)
{
[sprite stopAllActions];
[sprite runAction:[self standAction]];
}
}
- (void)left
{
[physicalBody setFriction:0];
if (!running && !inAir)
{
[sprite stopAllActions];
[sprite runAction:[self runAction]];
}
running = YES;
moveingDirection = NO;
[bodyControls moveToDirection:CGPointMake(-1, 0) onlyHorizontal:YES];
}
- (void)right
{
[physicalBody setFriction:0];
if (!running && !inAir)
{
[sprite stopAllActions];
[sprite runAction:[self runAction]];
}
running = YES;
moveingDirection = YES;
[bodyControls moveToDirection:CGPointMake(1, 0) onlyHorizontal:YES];
}
- (void)jump
{
if (!inAir)
{
[bodyControls jump];
}
}
And here is my update method (called on every iteration):
- (void)update:(NSMutableDictionary *)buttons
{
if (!isDead)
{
[self updateWithButtonName:BUTTON_LEFT inButtons:buttons whenPressed:@selector(left) whenUnpressed:@selector(stop)];
[self updateWithButtonName:BUTTON_RIGHT inButtons:buttons whenPressed:@selector(right) whenUnpressed:@selector(stop)];
[self updateWithButtonName:BUTTON_UP inButtons:buttons whenPressed:@selector(jump) whenUnpressed:@selector(nothing)];
[self updateWithButtonName:BUTTON_DOWN inButtons:buttons whenPressed:@selector(nothing) whenUnpressed:@selector(nothing)];
[sprite setFlipX:(moveingDirection)];
}
[self checkPosition];
if (!running)
[physicalBody setFriction:[[AppDel cfg] getHeroFriction]];
else
[physicalBody setFriction:0];
}
- (void)updateWithButtonName:(NSString *)buttonName inButtons:(NSDictionary *)buttons whenPressed:(SEL)pressedSelector whenUnpressed:(SEL)unpressedSelector
{
NSNumber *buttonNumber = [buttons objectForKey:buttonName];
if (buttonNumber == nil)
return;
if ([buttonNumber boolValue])
[self performSelector:pressedSelector];
else
[self performSelector:unpressedSelector];
}
- (void)checkPosition
{
b2Body *body = [self getBody];
b2Vec2 position = body->GetPosition();
CGPoint inWorldPosition = [[AppDel cfg] worldMeterPointFromScreenPixel:CGPointMake(position.x * PTM_RATIO, position.y * PTM_RATIO)];
if (inWorldPosition.x < 0 ||
inWorldPosition.x > WORLD_WIDGH / PTM_RATIO ||
inWorldPosition.y <= 0)
{
[self kill];
}
}