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 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 allready 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];
}
}