Como eu moveria um personagem em um RPG com Bullet Physics / Ogre3D?

9

Ultimamente, tenho tido problemas em mudar meu personagem no meu jogo Ogre3D. Basicamente, estou movendo o personagem com a RigidBody->translate()função de bala , mas ao fazê-lo e esbarrar em uma parede, passo um pouco por ela e depois sou devolvido. Gostaria de saber se existe outra boa maneira de mover meu personagem (que tem uma forma de colisão de esfera) em um mundo simples do tipo avião com paredes?

As bibliotecas que estou usando que são relativas a isso são 'Ogre3D' e 'Bullet Physics'.

Molmasepic
fonte

Respostas:

9

Embora eu não tenha trabalhado com o mecanismo de física de balas especificamente, fiz algo muito semelhante em outro mecanismo de física. A maneira que resolvi foi definir a velocidade linear do corpo rígido em vez de traduzi-la diretamente. Movimentos e colisões foram então tratados automaticamente pela fase de atualização do mecanismo de física.

A partir da documentação , parece haver um btRigidBody::setLinearVelocitymétodo que você pode usar. Então, por exemplo, se você não deseja que nenhuma aceleração aconteça, apenas defina a velocidade linear para um valor apropriado sempre que o personagem estiver se movendo e defina-o novamente como (0,0,0) quando o personagem for interrompido (por exemplo, quando o jogador soltar a tecla).

Quanto a quais valores usar, a abordagem usual seria começar com a velocidade desejada do seu personagem (como um flutuador / escalar) e depois multiplicá-lo por um vetor normalizado que está apontando na direção que você deseja mover. Pelo que vejo, a btVector3classe já possui métodos para tudo isso.

Como alternativa, você pode considerar tratar o personagem como um objeto físico completo e lidar com o movimento usando os métodos applyForceou applyImpulse. Isso resultaria em uma aceleração do corpo, para que seus personagens tenham impulso e os resultados provavelmente serão mais agradáveis ​​dessa maneira. Mas você precisa tomar algumas medidas extras, por exemplo, garantindo que a velocidade linear nunca exceda um determinado limite, apertando-a ou brincando com amortecimento / fricção. Portanto, será um pouco mais difícil de implementar e ajustar.

Experimente as duas abordagens e escolha a que se comporta mais próxima das suas necessidades.

David Gouveia
fonte
Tudo bem, obrigado pela resposta rápida, vou tentar isso imediatamente.
Molmasepic
O truque LinearVelocity funcionou como esperado como um encanto! Houve algumas torções que eu tive que consertar, mas está funcionando 100% Muito obrigado pela resposta!
Molmasepic
9

Para constar, minha experiência com a física está usando o Chimpunk em um mecanismo de jogo 2D, mas tenho certeza de que esse conceito se traduz em 3D muito bem.

Estou assumindo que seu personagem é um corpo de física com peso e tal. A melhor maneira de fazer isso é fazer uma simulação muito simplificada da caminhada. Pense assim: se você estiver em pé, seus pés terão muito atrito, para não deslizar. Quando você se move, é aproximadamente equivalente a remover esse atrito (já que você não está resistindo ao movimento com os pés) e aplicar uma força de direção. Eu estou não dizer que você deve simular individualmente cada pé empurrando no chão - um corpo rígido é o que você quer.

  • Quando seu personagem não estiver tentando se mover ativamente, faça com que sua velocidade seja alta, para que ele permaneça parado.
  • Quando seu personagem estiver se movendo, diminua o amortecimento de velocidade e aplique uma força na direção do movimento. Defina seu amortecimento e força para que o personagem se mova a uma velocidade razoável.
  • Se o personagem estiver no ar, defina o amortecimento muito, muito baixo. Se você quiser ser realista, não permita que apliquem força para mudar de direção enquanto estiverem no ar. Você pode encontrar um médium feliz aqui, permitindo que eles apliquem uma força muito menor, dando a eles uma capacidade limitada de ajustar sua trajetória enquanto estiverem no ar.

Aqui é onde get fica um pouco complicado:

  • Se o personagem já estiver se movendo e mudar de direção, você precisará compensar seu momento ajustando a direção em que aplica a força. Por exemplo, se seu personagem está se movendo para o norte e vira para leste, você precisa aplicar sua força em uma direção que fica a meio caminho entre o oposto da direção atual de viagem do personagem e a direção de viagem pretendida. À medida que a direção da viagem muda, ajuste a força para que ela fique sempre a meio caminho entre as duas, e seu personagem mudará de maneira rápida e suavemente.

Se você ajustar sua força e amortecer corretamente, aplicar uma força sempre fornecerá o resultado mais realista, principalmente se o personagem for empurrar objetos. Traduzir será a pior maneira de fazê-lo, uma vez que o mecanismo de física realmente não considera isso movimento. Definir a velocidade diretamente é um pouco melhor, mas, na minha experiência, os melhores resultados podem ser obtidos usando força e amortecimento.

Espero ter explicado isso bem o suficiente. Sinta-se livre para perguntar se você precisa de esclarecimentos. :)

Lendrick
fonte
0

Para o item 2.87, parece que o método adequado é ter um retorno de chamada de tick atualizado com a taxa de atualização da simulação interna (possivelmente muitos 100s de Hz), e setWorldTransform () em corpos cinemáticos atualizará suavemente a posição:

Esta parte está no manual:

// set the rigid body as kinematic
rigid_body->setCollisionFlags(
    rigid_body->getCollisionFlags() | btCollisionObject::CF_KINEMATIC_OBJECT);
rigid_body->setActivationState(DISABLE_DEACTIVATION);
...

Essa parte foi mais complicada de descobrir:

void externalTickCallback(btDynamicsWorld *world, btScalar timeStep)
{
  // get object passed into user data point
  Foo* foo = static_cast<Foo*>(world->getWorldUserInfo());
  ... loop through all the rigid bodies, maybe foo has them
  {
    if (rigid_body->getCollisionFlags() & btCollisionObject::CF_KINEMATIC_OBJECT)
    {
      btVector3 kinematic_linear_vel = ... // get velocity from somewhere
      btTransform trans;
      rigid_body->getMotionState()->getWorldTransform(trans);
      trans.setOrigin(trans.getOrigin() + kinematic_linear_vel * time_step);
      // TODO support angular velocity
      rigid_body_->getMotionState()->setWorldTransform(trans);
    }
  }
}
...
my_dynamics_world->setInternalTickCallback(tickCallback, static_cast<void*>(this), true);

Esta foi uma documentação útil em btRigidBody.h https://github.com/bulletphysics/bullet3/blob/master/src/BulletDynamics/Dynamics/btRigidBody.h :

/// - C) Objetos cinemáticos, que são objetos sem massa, mas o usuário pode movê-los. Há interação unidirecional, e Bullet calcula uma velocidade com base no passo temporal e na transformação mundial anterior e atual.

setLinearVelocity () não funciona para objetos cinemáticos (talvez em versões anteriores?). Mas o mundo da dinâmica entenderá setWorldTransform () e as chamadas para getLinearVelocity () no objeto cinemático retornarão a velocidade definida no retorno de chamada do tick (provavelmente retornará uma média se essas velocidades mudarem de tick interno para tick).

https://github.com/bulletphysics/bullet3/issues/1204 - o pôster da questão tem a ideia certa, mas a resposta não é útil.

Lucas W
fonte