Eu vim contra outra questão no meu pequeno jogo de bola quicando.
Minha bola está girando bem, exceto nos últimos momentos em que está prestes a descansar. O movimento da bola é suave para a parte principal, mas, no final, a bola estremece por um tempo enquanto se acomoda na parte inferior da tela.
Eu posso entender por que isso está acontecendo, mas não consigo suavizá-lo.
Ficaria grato por qualquer conselho que possa ser oferecido.
Meu código de atualização é:
public void Update()
{
// Apply gravity if we're not already on the ground
if(Position.Y < GraphicsViewport.Height - Texture.Height)
{
Velocity += Physics.Gravity.Force;
}
Velocity *= Physics.Air.Resistance;
Position += Velocity;
if (Position.X < 0 || Position.X > GraphicsViewport.Width - Texture.Width)
{
// We've hit a vertical (side) boundary
// Apply friction
Velocity *= Physics.Surfaces.Concrete;
// Invert velocity
Velocity.X = -Velocity.X;
Position.X = Position.X + Velocity.X;
}
if (Position.Y < 0 || Position.Y > GraphicsViewport.Height - Texture.Height)
{
// We've hit a horizontal boundary
// Apply friction
Velocity *= Physics.Surfaces.Grass;
// Invert Velocity
Velocity.Y = -Velocity.Y;
Position.Y = Position.Y + Velocity.Y;
}
}
Talvez eu deva também salientar isso Gravity
, Resistance
Grass
e Concrete
são do tipo que todos são Vector2
.
Respostas:
Aqui estão as etapas necessárias para melhorar seu loop de simulação física.
1. Timestep
O principal problema que vejo no seu código é que ele não leva em consideração o tempo da etapa da física. Deveria ser óbvio que há algo errado,
Position += Velocity;
porque as unidades não coincidem. OuVelocity
não é realmente uma velocidade ou algo está faltando.Mesmo se seus valores de velocidade e gravidade são escalados de modo que cada quadro acontece em uma unidade de tempo
1
(o que significa que , por exemplo.Velocity
Na verdade significa a distância percorrida em um segundo), o tempo deve aparecer em algum lugar no seu código, quer implicitamente (fixando as variáveis de modo a que seus nomes refletem o que eles realmente armazenam) ou explicitamente (introduzindo um timestep). Acredito que a coisa mais fácil a fazer é declarar a unidade de tempo:E use esse valor em qualquer lugar que for necessário:
Observe que qualquer compilador decente simplificará as multiplicações
1.0
, para que essa parte não torne as coisas mais lentas.Agora
Position += Velocity * TimeStep
ainda não é bem exato (veja esta pergunta para entender o porquê), mas provavelmente o fará por enquanto.Além disso, isso precisa levar tempo em consideração:
É um pouco mais difícil de corrigir; uma maneira possível é:
2. Atualizações duplas
Agora verifique o que você faz ao pular (apenas o código relevante é mostrado):
Você pode ver que
TimeStep
é usado duas vezes durante o salto. Isso basicamente dá à bola o dobro do tempo para se atualizar. Isto é o que deveria acontecer:3. Gravidade
Verifique esta parte do código agora:
Você adiciona gravidade por toda a duração do quadro. Mas e se a bola realmente saltar durante esse quadro? Então a velocidade será invertida, mas a gravidade adicionada fará com que a bola acelere para longe do chão! Portanto, o excesso de gravidade terá que ser removido ao saltar e , em seguida, adicionado novamente na direção correta.
Pode acontecer que mesmo adicionar novamente a gravidade na direção correta faça com que a velocidade acelere demais. Para evitar isso, você pode pular a adição de gravidade (afinal, não é muito e dura apenas um quadro) ou a velocidade da pinça para zero.
4. código fixo
E aqui está o código totalmente atualizado:
5. Adições adicionais
Para uma estabilidade de simulação ainda melhor, você pode decidir executar sua simulação de física em uma frequência mais alta. Isso é trivial pelas alterações acima
TimeStep
, porque você só precisa dividir seu quadro em quantos blocos desejar. Por exemplo:fonte
velocity += gravity
está errado e sóvelocity += gravity * timestep
faz sentido. Pode dar o mesmo resultado no final, mas sem um comentário dizendo "Eu sei o que estou fazendo aqui", ainda significa um erro de codificação, um programador desleixado, uma falta de conhecimento sobre física ou apenas um código de protótipo que precisa ser melhorado.gravity
é na verdade ... não a gravidade. Mas posso deixar isso mais claro no post.Adicione uma verificação para interromper o salto, usando uma velocidade vertical mínima. E quando você conseguir o salto mínimo, coloque a bola no chão.
fonte
Então, acho que o problema de por que isso está acontecendo é que sua bola está se aproximando de um limite. Matematicamente, a bola nunca para na superfície, ela se aproxima da superfície.
No entanto, seu jogo não está usando um tempo contínuo. É um mapa, que está usando uma aproximação à equação diferencial. E essa aproximação não é válida nessa situação limitadora (você pode, mas teria que tomar etapas menores e menores, o que eu suponho que não é viável.
Fisicamente falando, o que acontece é que, quando a bola está muito próxima da superfície, ela gruda nela se a força total estiver abaixo de um determinado limite.
A resposta @Zhen seria boa se o seu sistema for homogêneo, o que não é. Tem alguma gravidade no eixo y.
Então, eu diria que a solução não seria que a velocidade estivesse abaixo de um determinado limite, mas a força total aplicada na bola após a atualização deveria estar abaixo de um determinado limite.
Essa força é a contribuição da força exercida pela parede na bola + a gravidade.
A condição deve ser algo como
if (newVelocity + Physics.Gravity.Force <limite)
observe que newVelocity.y é uma quantidade positiva se o salto estiver na parede inferior e a gravidade for uma quantidade negativa.
Observe também que newVelocity e Physics.Gravity.Force não têm as mesmas dimensões, como você escreveu em
o que significa que, como você, estou assumindo que delta_time = 1 e ballMass = 1.
Espero que isto ajude
fonte
Você tem uma atualização de posição dentro de sua verificação de colisão, é redundante e incorreta. E acrescenta energia à bola, potencialmente ajudando-a a se mover perpetuamente. Juntamente com a gravidade não sendo aplicada em alguns quadros, isso gera um movimento estranho. Remova.
Agora você pode ver uma questão diferente: a bola fica "presa" fora da área designada, saltando perpetuamente para frente e para trás.
Uma maneira simples de resolver esse problema é verificar se a bola se move na direção correta antes de alterá-la.
Assim você deve fazer:
Para dentro:
E semelhante para a direção Y.
Para que a bola pare bem, você precisa parar a gravidade em algum momento. Sua implementação atual garante que a bola sempre ressurja, pois a gravidade não a freia enquanto estiver no subsolo. Você deve mudar para sempre aplicar a gravidade. No entanto, isso leva a bola a afundar lentamente no chão depois de se estabelecer. Uma solução rápida para isso é, após aplicar a gravidade, se a bola estiver abaixo do nível da superfície e se mover para baixo, pare-a:
Essas alterações no total devem fornecer uma simulação decente. Mas observe que ainda é uma simulação muito simples.
fonte
Tenha um método mutador para toda e qualquer alteração de velocidade; nesse método, você pode verificar a velocidade atualizada para determinar se está se movendo devagar o suficiente para colocá-la em repouso. A maioria dos sistemas de física que conheço chama isso de 'restituição'.
No método acima, limitamos a rejeição sempre que ela está no mesmo eixo da gravidade.
Outra coisa a considerar seria detectar sempre que uma bola colidisse com o solo e, se estiver se movendo bastante devagar no momento da colisão, ajuste a velocidade ao longo do eixo da gravidade para zero.
fonte
Outra coisa: você está se multiplicando por uma constante de atrito. Mude isso - diminua a constante de atrito, mas adicione uma absorção de energia fixa em um salto. Isso irá amortecer os últimos saltos muito mais rapidamente.
fonte