Como implementar uma viga de trator?


8

Estou trabalhando em um jogo em que o jogador pode pegar objetos usando algo como um raio de trator e carregá-los.

Atrair o objeto em direção ao centro do feixe não é difícil. Mas quando o objeto estiver próximo o suficiente do centro, preciso mantê-lo lá enquanto o jogador se move, e é com isso que estou tendo problemas. Posso pensar em duas maneiras de fazer isso, e ambas têm problemas:

  1. Atualize a posição do objeto sempre que a posição do jogador mudar, mantendo-o centralizado no raio.

  2. Atualize a velocidade do objeto para apontar diretamente para o centro do feixe, quanto mais longe, maior a velocidade.

Mover e girar funciona bem com as duas abordagens, mas a física está errada quando o objeto carregado colide com outros objetos:

Com a primeira abordagem, a física é completamente ignorada. O objeto carregado simplesmente empurra qualquer coisa para fora do caminho. Isso ocorre porque as mudanças de posição devem ser feitas apenas como parte da física do mundo, com base na velocidade.

Com a segunda abordagem, a física basicamente se comporta como deveria, mas reage exageradamente. O problema é: Para manter o objeto transportado no centro do feixe, mesmo quando estiver girando e se movendo, preciso usar valores altos de velocidade. Assim, uma vez que o objeto carregado toca outro, fica com velocidade demais da colisão.

Como posso implementar isso corretamente? Meu melhor palpite agora é seguir a segunda abordagem e adicionar um tratamento especial para objetos transportados à física mundial, reduzindo a velocidade a valores sãos para colisões ou quando o jogador parar de carregá-los. Mas isso parece uma solução bastante deselegante.

Edit: Adicionando algum pseudo-código para ilustrar como funciona agora (essa seria a segunda abordagem acima)

void attract_object(object, ticks) {
    Vector distance = beam_center - object.center;
    // If the object is not close to the beam center, attract it slowly
    if (magnitude(distance) > 10) {
        object.velocity += distance.normalized() * ticks * 0.1;
        return;
    }

    // Here comes the part we're talking about. That magic 0.5 is just high enough
    // that the object isn't lost while moving around. But it's still so high that
    // other objects are repelled with way too much force.
    object.velocity = distance * ticks * 0.5;
}

Pelo que vejo, isso acontece quando o objeto carregado empurra outro objeto:

  1. O objeto carregado colide com outro objeto
  2. As velocidades dos objetos são distribuídas adequadamente, de modo que o objeto carregado é empurrado para longe do centro do feixe no processo
  3. O código acima faz com que o objeto carregado retorne ao centro do feixe, com tanta velocidade que ele retornará rapidamente
  4. Quando o objeto transportado se move de volta para o centro do feixe, metade de sua alta velocidade é transferida para o outro objeto, repelindo-o violentamente. Como a velocidade inicial do objeto transportado parece ser sã, posso imaginar que as etapas 2 a 4 são repetidas várias vezes, construindo uma velocidade tão alta.

Essa parece ser a causa. Eu não consigo pensar em uma boa maneira de corrigi-lo :(


11
Bem-vindo às complexidades de um reator de fusão Tokomak. Você tem o benefício de precisar apenas construir um modelo matemático funcional, não a garrafa magnética funcional, mas a matemática é idêntica e não trivial. O que você está tentando é factível, mas você precisará pensar cuidadosamente em seu modelo matemático antes de codificar.
Pieter Geerkens

Respostas:


1

Essencialmente, o que você está procurando é fazer com que o objeto 'com vigas' se comporte exatamente como se você o pegasse com as mãos.
Uma opção seria fazê-lo compartilhar as velocidades de aceleração a / o da 'mão' que a segura, em vez de ajustar sua velocidade para preencher a lacuna com o centro do feixe.

Digamos que o centro do feixe seja a mão segurando. se seu personagem girar 90 graus para a esquerda em 1 segundo, a velocidade da mão seria:

If R = length of the arm: which is the radius of the rotation circle
R^2 *PI /4 would be the distance traveled over a second.
faça o tempo decorrido do quadro para encontrar a velocidade que você deve aplicar ao seu objeto. Encontre o normal horizontal para o feixe para encontrar seu vetor de direção.

O que quero dizer é que você não precisa resolver problemas se tentar outras implementações que não causam isso em primeiro lugar.

Eu iria brincar com a pistola de gravidade no HL2 para obter alguma inspiração sobre o problema, mas tenho outros planos para hoje.

EDIT: desculpe, eu pensei que era para uma arma 3D, mas é essencialmente a mesma coisa com 2D, exceto que os eixos são diferentes (e não há geometria complexa)


Depois de tentar vários hacks, foi isso que me colocou no caminho certo, o resultado parece bom. A velocidade na colisão ainda é um pouco alta demais, mas acho que posso descobrir isso. Possivelmente apenas por não atrair objetos com alta velocidade em uma direção diferente.
futlib

Talvez quando a 'mão' colidir com uma parede, você possa calcular a posição plausível mais próxima (que não está colidindo) da mão e usá-la como a 'mão temporária' durante a colisão. Costumo gostar de enfrentar problemas de outra perspectiva. Estou feliz que eu poderia ajudar com isso;).
Icosamuel

4

Que tal adicionar uma conexão de mola, ou seja, forçar o objeto transportado a voltar à posição de transporte com base na distância, mas ainda permitir que ele seja empurrado por objetos sólidos (como paredes).

Ajuste constantemente a velocidade do objeto transportado (alterando a aceleração com base na posição / distância) para apontar para a posição do feixe do trator (ou seja, sua segunda abordagem). Se o objeto for empurrado longe demais, solte a conexão (e o objeto).

Não sei ao certo por que você precisaria de altas velocidades. Especialmente o caso "jogador solta" indica que sua velocidade de rotação pode ser muito alta ou irrealista. Também não se esqueça de coisas como resistência do ar e gravidade.


Edit: Considerando o código atualizado, o problema é bastante trivial para encontrar:

if (magnitude(distance) > 10) {
    object.velocity += distance.normalized() * ticks * 0.1;
    return;
}

O problema aqui é o caso em que a distância do objeto à sua posição de objetivo é constantemente muito longa (ie > 10). Desde que essa condição seja verdadeira, sua velocidade simplesmente aumenta repetidamente (ou seja, indefinidamente).

Duas soluções possíveis para isso:

Defina uma velocidade máxima:

object.velocity = min(object.velocity + distance.normalized() * ticks * 0.1, max_velocity);

Aplique uma velocidade fixa em vez de acelerar:

object.velocity = distance.normalized() * ticks * magic_factor;

Acelerar enquanto está muito longe é definitivamente uma abordagem errada aqui. O que é puxar uma mola ou elástico: não importa se você o segura por um segundo ou um minuto. No final, ele acelerará da mesma maneira (considerando que não está em movimento e não há outras forças aplicadas).


Foi o que eu descrevi no método 2, não é? Isso é física básica da primavera, AFAIK. Vou adicionar um código à pergunta acima para ilustrá-lo.
futlib 7/09/13

Também removemos a parte sobre velocidade incorreta acima, na verdade está bem, apenas testei. Portanto, são apenas as colisões com outros objetos que são confusas.
Futlib 7/09/13

Sim, é essencialmente a sua segunda abordagem. Atualizando minha resposta.
Mario

Tentei as duas abordagens, mas não ajuda. Parece que o caso de magnitude (distância)> 10 não é o culpado aqui. Tentei limitar a velocidade para o caso <=, mas é o problema usual: a velocidade é tão baixa que o objeto está sendo derrubado ou tão alta que repele os outros violentamente.
futlib 8/09/13

"tão alto que repele outros violentamente": você deve mover outros objetos com base na velocidade efetiva, não na velocidade nos bastidores (por exemplo, redefinir a velocidade devido à colisão).
Mario
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.