Usando o tempo ocioso em jogos baseados em turnos (RPG) para atualização

9

Se você jogar qualquer jogo de RPG baseado em turnos, haverá grandes períodos de tempo em que nada está acontecendo porque o jogo está repetindo 'wait_for_player_input'. Naturalmente, parece sensato usar esse tempo para atualizar as coisas.

No entanto, isso imediatamente parece sugerir que precisaria ser encadeado. Esse tipo de design é possível em um único thread?

loop:  
if not check_something_pressed:  
    update_a_very_small_amount  
else  
  keep going

Mas se dissermos que 'a_very_small_amount' está atualizando apenas um único objeto a cada loop, a atualização será muito lenta.

Como você faria isso, de preferência em um único segmento?

EDIT: Marquei esta linguagem como agnóstica, pois isso parece sensato, embora algo mais específico ao Python seja ótimo. ;-)

Segunda edição: Este jogo não planeja ter nenhum componente animado; ou seja, atualmente estou executando-o como uma entrada de espera pelo jogador, atualizo tudo e empate. Portanto, em vez de X FPS, depende da velocidade do usuário.

O Pato Comunista
fonte

Respostas:

7

Sim, é possível fazer em um único segmento. De um modo geral, você desejará atualizar os objetos a cada quadro e não apenas quando houver ciclos de reposição. Suas animações e movimentos serão desconectados da taxa de quadros e parecerão um pouco irregulares se você não o fizer. Se você está falando mais sobre atualizações de IA ou algo mais que não precisa ser em tempo real, eu colocaria um cronômetro nele. Você deve saber qual é a sua taxa de quadros-alvo e o tempo ocioso será o que resta depois que todo o resto for concluído.

Digamos que você esteja direcionando 60 FPS para o seu jogo. Isso deixa você com 16.667 ms para executar todo o trabalho necessário para cada quadro. No início do jogo, obtenha a hora atual usando o cronômetro de maior resolução disponível, adicione 16.667 ms e guarde-o em algum lugar. Eu acho que a função em python é time (), embora tenha sido um tempo desde que eu trabalhei na linguagem. Após a conclusão do processamento, insira um loop que verifique a hora atual em relação à hora que você gravou. Se o horário atual for menor que o horário de término do quadro, atualize_a_very_small_amount. Não me preocuparia muito com o processamento que passou do final do quadro, já que sua pequena atualização deve ser rápida no processamento. Seria apenas um pequeno atraso para o início do próximo quadro e você parece ter tempo de inatividade suficiente para lidar com isso.

Após o término do processamento do quadro, adicione 16,667 ms ao tempo que foi armazenado para o final do último quadro para descobrir onde o final do próximo quadro deve estar. Se você usar o tempo atual + 16,667 ms e o processamento terminar, o final do próximo quadro será adiado por quanto tempo o último quadro atropelar.

Re: Segunda Edição

Para esclarecer, eu uso o termo taxa de quadros aqui para indicar uma iteração no loop principal. Se for baseado na velocidade de entrada do usuário, imagino que seu objetivo seja simplesmente fazer com que o jogo pareça responsivo. Caso contrário, você pode apenas verificar a entrada e atualizar tudo a cada vez no loop, mesmo que demore 10 segundos para fazê-lo. Para se sentir responsivo, você provavelmente desejará verificar a entrada em torno de 20 vezes por segundo, o que fornece uma taxa de quadros efetiva de 20 FPS, mesmo se você não estiver desenhando esses quadros. Isso daria a você 50 ms para atualizar as coisas antes de precisar verificar a entrada novamente.

Joel Baker
fonte
2

Para o Python especificamente, você pode tentar usar as Coroutines para fazer cálculos em várias chamadas de atualização.

... as corotinas são componentes de programa que generalizam sub-rotinas para permitir vários pontos de entrada para suspender e retomar a execução em determinados locais ...

O PEP-0342 detalha a implementação da rotina em Python.

Você pode criar seu próprio planejador e tarefas que podem simular multithreading em um único encadeamento. É da mesma maneira que as estruturas Javascript permitem o processamento multithread como se o Javascript fosse executado em um único processo.

David Young
fonte
1

Sim, isso é possível. Seu jogo terá algum tipo de loop do jogo principal. Algo assim:

while(gameRunning)
{
  checkUserInput()
  updateGame()
  renderGame()
}

No updateGame, você pode interagir com os objetos do jogo e atualizá-los "um pouco". Se você estiver fazendo algum cálculo pesado nesse método, o jogo simplesmente travará. Portanto, você precisa de uma maneira de dividir esses cálculos para executar várias iterações do ciclo do jogo.

Como dividi-los, depende do tipo de jogo que você está construindo. Suponha que você tenha uma rotina de localização de caminhos que use A * para calcular um caminho através de um labirinto. Você precisaria interromper o algoritmo após um certo tempo ou após uma quantidade fixa de iterações, manter o caminho calculado até o momento e retornar o controle de volta ao loop do jogo. A localização do caminho continuará na próxima vez que updateGame for chamado. Você pode até mover o personagem ao longo do caminho parcial enquanto ele ainda está sendo calculado.

Na maioria das vezes, você não precisa se preocupar com a demora da chamada updateGame.

"Otimização prematura é a raiz de todo o mal!"
Se você encontrar um gargalo de desempenho em sua rotina de atualização, é hora de investigar e otimizar. Como você não pode saber antecipadamente quais partes do seu jogo levarão mais tempo para calcular e podem perder tempo otimizando as coisas erradas.

Stephen
fonte
1

A maneira como entendo sua pergunta é que basicamente você está perguntando sobre multitarefa cooperativa.

A infraestrutura básica seria uma lista de ponteiros de função, todos chamados de maneira round-robin (a menos que seja necessária uma priorização mais sofisticada), e todas essas funções fariam "um pouco de trabalho", pequeno o suficiente para não causar o jogo tornar-se lento. Eu fiz isso no DOS antes que os threads estivessem quentes, e também em dispositivos móveis que não suportavam encadeamento.

Uma pergunta melhor, na minha opinião, é o que fazer enquanto espera o jogador se mover. Que tipos de coisas seriam tão intensivos para o processador que não podem ser executados de qualquer maneira, enquanto, ao mesmo tempo, são tão opcionais que, se o jogador martelar algumas teclas com movimento rápido o suficiente, o jogo não terá hora de processá-los. Assim, coisas como o cálculo de A * para algumas milhares de IAs estão disponíveis.

A melhor solução, na minha opinião, é simplesmente ceder / dormir e deixar que o gerenciamento de energia do computador faça seu trabalho. Os usuários de laptops gostarão mais de você por isso.

Jari Komppa
fonte