Não é tão difícil criar um movimento de carro bastante bom (mas este post será bastante longo). Você precisará "simular" algumas forças básicas para tornar o carro fisicamente plausível.
(Todos os exemplos de código são pseudocódigo.)
Aceleração
Primeiro, você obviamente precisará de aceleração. Algo tão simples quanto a seguinte linha faria:
acceleration_vector = forward_vector * acceleration_input * acceleration_factor
forward_vector
- Um vetor apontando na mesma direção que o carro.
acceleration_input
- A entrada deve estar no intervalo [-1, 1].
acceleration_factor
- O valor da aceleração (pixels / segundo ^ 2 ou quaisquer que sejam suas unidades).
Direção
A direção também é bastante simples. Em princípio, o que você fará é girar o vetor para frente do carro para apontá-lo em outra direção.
steer_angle = steer_input * steer_factor
new_forward_vector = rotate_around_axis(forward_vector, up_vector, steer_angle)
Você pode encontrar uma complicação aqui, no entanto. Se sua entrada for através de um teclado, seu valor será -1 ou 1, o que significa que seu carro giraria instantaneamente. Você pode corrigir isso usando uma interpolação linear muito simples (lerping):
amount = time_since_last_frame * steer_lerp_factor
forward_vector = lerp(forward_vector, new_forward_vector, amount)
A quantia deve depender do tempo, para que seu movimento não dependa da taxa de quadros. A quantidade deve estar entre [0, 1] e quanto menor, mais suave será a transição entre os vetores antigos e novos.
(Nesse ponto, você descobrirá que o carro dirigirá mesmo que esteja parado. Para evitar isso, multiplique steer_angle
por current_speed / max_speed
, onde max_speed
é uma constante definida por você.)
Movendo
Agora aplicaremos a aceleração e moveremos o carro com um certo número de pixels com base em sua velocidade, aceleração e direção. Também queremos limitar a velocidade do carro para que ele não se mova infinitamente rápido.
current_speed = velocity_vector.norm()
if (current_speed < max_speed)
{
velocity_vector += acceleration_vector * time_since_last_frame
}
position_vector += velocity_vector * time_since_last_frame
Seu carro está deslizando agora
Se eu estiver certo, seu carro deve parecer deslizar sempre que você estiver virando como se estivesse no gelo. Isso ocorre porque não há atrito. Em um carro real, há um alto atrito lateral (devido às rodas não poderem girar para o lado: P).
Você precisará reduzir a velocidade lateral. Ao não reduzi-lo completamente, você também pode fazer o carro parecer estar à deriva.
lateral_velocity = right_vector * dot(velocity_vector, right_vector)
lateral_friction = -lateral_velocity * lateral_friction_factor
Como estamos falando de fricção, você também pode querer ter uma força (de fricção) que reduz sua velocidade, de modo que, quando você para de acelerar, seu carro acaba parando.
backwards_friction = -velocity_vector * backwards_friction_factor
Seu código para mover o carro agora deve ficar assim:
// Friction should be calculated before you apply the acceleration
lateral_velocity = right_vector * dot(velocity_vector, right_vector)
lateral_friction = -lateral_velocity * lateral_friction_factor
backwards_friction = -velocity_vector * backwards_friction_factor
velocity_vector += (backwards_friction + lateral_friction) * time_since_last_frame
current_speed = velocity_vector.norm()
if (current_speed < max_speed)
{
velocity_vector += acceleration_vector * time_since_last_frame
}
position_vector += velocity_vector * time_since_last_frame
Notas finais
Mencionei como você deve aplicar o lerping à direção; Eu acho que você pode precisar fazer o mesmo para a aceleração e, possivelmente, também para o ângulo de direção (você terá que armazenar seus valores do quadro anterior e lerp a partir disso). Todos os vetores relativos ao carro (frente, direita, cima) também devem ter o comprimento 1.
Além disso, o atrito é um pouco mais complicado do que mostrei aqui. Você deve sempre garantir que seu comprimento nunca seja maior que o da aceleração necessária para fazer o carro parar (caso contrário, o atrito faria o carro se mover na direção oposta). Então você deve ter algo como:
dt = time_since_last_frame
backwards_friction.resize(min(backwards_friction.norm(), velocity_vector.norm() / dt))
lateral_friction.resize(min(lateral_friction.norm(), lateral_velocity.norm() / dt))