• Jogos
  • 02. Física
  • Simulação e Física

Simulação e Física#

Nossa discussão inicial leva em conta que todo jogo precisa criar um conjunto de regras que rege seu mundo. Inclusive, alguns jogos definem suas próprias regras que podem ou não ser inspiradas em conceitos de física "de verdade".

Em muitos casos o foco é a física newtoniana e os Movimentos Retilíneo Uniforme e Retilíneo Uniformemente Variado são muito presente. Neste handout iremos implementar esses dois movimentos em jogos simples em Pygame.

Movimento Retilíneo Uniforme#

Exercise 1

Vamos retomar a questão do Movimento Retilíneo Uniforme discutida no início da expositiva de hoje. Suponha que definimos a velocidade de um objeto no jogo como 50pixels/s.

A última atualização do nosso jogo demorou 25ms. Qual seria o incremento na posição atual deste objeto?

Answer

Nosso \(\Delta t = \frac{25}{1000}s\). Seguindo o raciciocínio trabalhado, fazemos \(50 \Delta t = 50 \frac{25}{1000} = 1.25\).

Exercise 2

Vamos continuar com nosso loop do jogo levando 25ms. Quantas atualizações do estado do jogo serão feitas em 1 segundo?

Answer

Faremos \(\frac{1}{\Delta t}\) atualizações por segundo. Logo, o valor correto é 40.

Agora vamos desenvolver nosso primeiro programa usando física. Abra o exercício Bola Bate Rebate no VSCode e continue seguindo as instruções.

Acessar exercício

Durante essa seção iremos passar cada vez maior quantidade de testes do exercício. Para começar, leia o código fornecido e se familiarize com ele.

Exercise 3

No código jogo.py temos a definição da variável que guarda o estado do jogo. Olhando para esta variável, em que coordenadas a bola começa no jogo?

Answer

O dicionário state contém a chave ball_pos, que representa a posição inicial da bola como uma lista em que o primeiro elemento é a coordenada x e o segundo a coordenada y.

Exercise 4

Para deixar nossos nomes mais consistentes, vamos renomear a função recebe_eventos. Como agora ela atualiza o estado do jogo, incluindo tanto receber eventos quanto fazer a simulação física, agora ela deverá se chamar atualiza_estado. Essa função deverá receber o estado atual do jogo como argumento.

Agora vamos implementar o Movimento Retilíneo Uniforme em uma simulação simples. Seu jogo deverá ter o comportamento abaixo no fim deste guia.

Um ponto importante desse nosso primeiro jogo com física é que decompomos o movimento da bola em dois MRUs: um na horizontal e um na vertical. Assim usamos sempre o caso mais simples 1D mesmo com movimentos em 2D.

Exercise 5

Precisamos guardar a velocidade da bola em pixels por segundo no estado inicial do jogo. Faça isso criando uma chave "ball_vel" no estado do jogo e guardando a velocidade horizontal igual a 400 pixels por segundo e a vertical 300 pixels por segundo. Guarde-os em uma lista, igual ao feito com a posição.

Seu código deve passar nos testes

  • test_inicializa_estado_jogo_com_velocidade

Em diversos momentos da nossa expositiva falamos sobre a "duração do gameloop" querendo dizer "a quantidade de tempo entre o momento atual e a última vez que atualizamos o estado do jogo". Ou seja, precisamos contar tempo dentro de nosso programa.

Exercise 6

A Pygame tem recursos que nos ajudam a medir tempo durante nossos jogos. Consulte a página de documentação de pygame.time e responda. Qual é a função que poderíamos usar para medir a quanto tempo a última atualização executou?

Answer

get_ticks retorna o número de ms desde o início do programa. Podemos salvar o tempo de cada atualização no estado do jogo e pegar a diferença entre o tempo atual e o armazenado.

Exercise 7

O cálculo do nosso \(\Delta t\) será feito com a função pygame.time.get_ticks. Isso é feito em três partes:

  1. guardamos o tempo da última atualização na chave "last_updated" do estado do jogo. Esta chave começa com o valor 0.
  2. no início de cada cada execução de atualiza_estado, chamamos pygame.time.get_ticks e guardamos esse valor.
  3. calculamos \(\Delta t\) como a diferença entre o valor acima e o valor de "last_updated".
  4. no fim de cada execução de atualiza_estado sobrescrevemos o valor de "last_updated" com o valor guardado no item 2.

Implemente a lógica acima no seu jogo. Seu código deve passar nos testes

  • inicializa_estado_jogo_com_last_updated
  • atualiza_estado_chama_get_ticks_e_atualiza_last_updated

Exercise 8

Finalmente, como temos \(\Delta t\) podemos atualizar a posição da bola. Faça isto agora. Você deve atualizar tanto a posição x quando a posição y dentro de atualiza_estado seguindo a fórmula que deduzimos do MRU:

  • prox_posicao = posicao_atual + velocidade * delta_t

Seu código deverá passar nos seguintes testes:

  • test_atualiza_estado_delta_t_1000_movimento_vertical
  • test_atualiza_estado_delta_t_500_movimento_vertical
  • test_atualiza_estado_delta_t_1000_movimento_horizontal
  • test_atualiza_estado_delta_t_500_movimento_horizontal
  • test_atualiza_estado_delta_t_200_movimento_composto

Estamos quase acabando nosso primeiro jogo. Execute e veja o que está faltando para sua simulação ter os resultados esperados.

A bola não rebate nas bordas!

Exercise 9

Para que isto aconteça, quais partes do estado do jogo você precisaria modificar?

Answer

Para que a bola rebate nas bordas precisamos agora mexer em sua velocidade!

A colisão com a borda será feita da mesma forma que o movimento: 1 dimensão por vez. Ou seja, primeiro vemos se a bola está saindo da tela na vertical e tratamos esse caso. Depois fazemos o mesmo para a horizontal.

Exercise 10

Sabendo que a nossa tela do jogo tem largura 640 e altura 480, qual seria a condição a ser checada para ver se a bola, que tem raio 10, saiu da tela na coordenada horizontal? Considere nas alternativas abaixo que

x = state['ball_pos'][0]
y = state['ball_pos'][1]

Answer

A bola sai da tela se sua extremidade esquerda tiver coordenada x menor que 0 ou se sua extremidade direita tiver coordenada x maior que a largura da tela (640). A posição que guardamos é a do centro da bola, logo, obtemos as extremidades adicionando/subtraindo o valor do raio do centro. Portanto, a primeira alternativa é a correta.

A segunda alternativa seria promissora também, mas a condição descrita é para a bola estar dentro da tela, não fora.

Sabendo disso, implemente a bola rebatendo em seu jogo. Leve em conta as seguintes dicas:

  1. Se você detectar que a bola está fora da tela em uma componente, deve inverter a velocidade da bola nesta componente
  2. A bola nunca pode sair da tela, se ela sair coloque-a de volta na tela no ponto da colisão

Movimento Retilíneo Uniformemente Variado#

A implementação do MRUV segue os mesmos passos do MRU. Seu trabalho aqui será, dado o código inicial com a estrutura básica que fizemos na parte 1, implementar um programa com o comportamento abaixo:

Para este exercício não serão disponibilizados testes. Você deverá conferir o funcionamento do seu código comparando o resultado obtido com a animação acima.

Acessar exercício