Перемещение тел Box2d подобно объектам CCSprite

В cocos2d вы можете упростить CCSprites и перемещать их различными способами. Самое главное - у них может быть easing in/out. Для большинства игр это желательно для плавного движения и т. д.

id action = [CCMoveTo actionWithDuration:dur position:pos];
move = [CCEaseInOut actionWithAction:action rate:2];
[self runAction: move];

При перемещении тела box2d прикрепленный к нему спрайт обновляется после выполнения шага box2d(). Перемещение спрайта и последующее обновление тела здесь не вариант, так как это полностью противоречит цели физического фреймворка.

Таким образом, другой вариант, который я успешно реализовал, состоит в том, чтобы вычислить смещение, скорость и ускорение спрайта, рассматривая его как самостоятельную механическую сущность. Каждый раз, когда я вызываю свой update() для спрайта, чтобы персонаж мог решить, куда двигаться и т. д., мой суперкласс также сохраняет предыдущую позицию и скорость. Они сохраняются как значения, совместимые с box2d, путем деления на PTM_RATIO.

В подклассе CCSprite, называемом 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 {

}

Затем я разделил "FMSprite" на "FMObject", в котором хранится b2Body и т.д.

Чтобы переместить тело, я должен сначала переместить спрайт и отследить его ускорение, с помощью которого я могу найти необходимую силу (используя массу), необходимую для отслеживания движения спрайта. Поскольку я не могу перемещать спрайт объекта (который синхронизирован с телом), я создаю другой спрайт, называемый «маяком», добавляю его как дочерний объект к объекту и перемещаю его. Все, что нам нужно сделать, это иметь функцию для синхронизации положения тела box2d с этим спрайтом-маяком, используя силы, о которых я упоминал ранее.

-(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)];
}

Результат блестящий, плавное плавное движение тела b2body, перемещающегося туда, куда вы хотите, без игры с какими-либо его собственными силами, а скорее копируя движение CCSprite и повторяя его движение. Поскольку это все силы, он не будет вызывать дрожание и искажения при столкновении с другими объектами b2Body. Если у кого-то есть другие способы сделать это, пожалуйста, напишите ответ. Спасибо!


person Aram Kocharyan    schedule 13.12.2011    source источник
comment
Привет, спасибо за этот пост. Хотя я не понял одного. Зачем нужен спрайт-маяк? если объект типа FMObject (который является CCSprite) перемещается с помощью некоторого пользовательского действия, почему бы не сделать так, чтобы b2Body в FMObject следовал за родительским спрайтом. Я не уверен, что означает «синхронизированный с телом». Зачем вам отдельно нужен спрайт маяка и перемещать его?   -  person Aks    schedule 16.06.2014
comment
Прошло почти 3 года с тех пор, как я опубликовал это, но я думаю, что вы можете быть правы. Я не вижу причины наличия спрайта маяка. Я думаю, что это была просто деталь реализации. Вы должны иметь возможность заменить сам FMObject.   -  person Aram Kocharyan    schedule 16.06.2014


Ответы (1)


То, что я делаю, отличается от вашего, но я также могу перемещать тела Box2d, такие как объекты CCSprite, и даже использовать CCAction. Самое главное — создать объект, содержащий ccSprite и b2body.

@interface RigidBody : CCNode {
    b2Body *m_Body;
    CCSprite *m_Sprite;
}

Затем перепишите метод setPosition.

-(void)setPosition:(CGPoint)position
{
    CGPoint currentPosition = position_;
    b2Transform transform = self.body->GetTransform();
    b2Vec2 p = transform.p;
    float32 angle = self.body->GetAngle();
    p += [CCMethod toMeter:ccpSub(position, currentPosition)];
    self.body->SetTransform(p, angle);  
    position_ = position;
}

Метод setPosition вычисляет, насколько изменится позиция, и устанавливает его в b2body.

Надеюсь, я понял ваш вопрос, и ответ будет полезен для вас...

person Ringo_D    schedule 08.05.2012
comment
Это работает для позиционирования, но SetTransform будет каждый раз устанавливать положение тела, а не использовать силы. Это означает, что когда два объекта RigidBody сталкиваются, они создают эффект дрожания, а не плавно отскакивают друг от друга. Метод addForce более трудоемок, но обеспечивает плавность физики. - person Aram Kocharyan; 08.05.2012
comment
Да, но с помощью силы трудно удерживать тело на постоянной скорости и в определенном положении. Я никогда не ошибаюсь, когда использую свой метод, но я понятия не имею, являются ли оба тела b2_dynamicBody. - person Ringo_D; 08.05.2012