Como implementar a viagem no tempo em um jogo?

10

Eu queria saber como implementar a viagem no tempo em um jogo. Nada super complexo, apenas a reversão do tempo como no Braid, onde o usuário pode retroceder / avançar rapidamente em 30 segundos ou o que for.

Eu pesquisei bastante na web, mas meus resultados geralmente se referiam ao uso do tempo como "são 3:00" ou um cronômetro e tal.

A única coisa que eu conseguia pensar era usar duas matrizes, uma para a posição x do jogador e a outra para a posição y do jogador, e depois iterando através dessas matrizes e colocando o personagem nessa posição à medida que elas retrocedem / avançam rapidamente. Isso poderia funcionar? Se funcionasse, qual seria o tamanho da matriz e com que frequência devo armazenar os xey do jogador? Se não funcionar, o que mais eu poderia tentar?

Desde já, obrigado!

Alex
fonte
11
Você não lê as notícias ( ust.hk/eng/news/press_20110719-893.html )? Eles acabaram de mostrar que a viagem no tempo não é possível. Portanto, é impossível codificar.
Você precisa pensar em uma possível semântica para a viagem no tempo; somente depois você poderá começar a pensar na implementação.
Pa Elo Ebermann 26/07
2
Aprenda um pouco de matemática vetorial . Se você está propondo duas matrizes separadas, sugere que nunca trabalhou com elas. Eu os considero vitais para um programador de jogos saber por quanto eles podem simplificar as coisas.
28811 doppelgreener
5
import universal.back2future.FluxCapacitor;
Jhocking

Respostas:

6

A ideia do array é basicamente como foi implementada no Braid. Quando as únicas coisas que agem em um personagem são a gravidade, a entrada do teclado / joypad e outros personagens, você praticamente precisa armazenar a posição e a velocidade em cada etapa para saber tudo o que é importante sobre esse personagem. Se você armazenar 10 snapshots por segundo por caractere, ainda será menor que 50K por um minuto da história de um personagem - facilmente gerenciável na maioria dos sistemas, e você poderá encontrar maneiras mais eficientes do que isso.

Kylotan
fonte
Este é o caminho certo. Por exemplo, armazenar comandos ativos: pressiona a tecla com carimbos de data e hora. Você pode extrapolar a maioria dos outros comportamentos se o sistema for determinístico. O rastreador de 10 qps é bom para um sistema de rebobinagem, e menos ainda pode ser aceitável, desde que os comandos reais sejam mantidos, com efeito o estado ou as mudanças de estado dos objetos.
karmington
4

Leia-se sobre o padrão de comando . Ele fornece ações para desfazer (e para refazê-las posteriormente). Isso lidaria com mais do que apenas a posição de um navio, mas todas as ações que o jogador executa.

Mas acho que sua ideia de array também é sólida.


fonte
Provavelmente não é uma boa idéia - além de consumir mais memória (toda vez que você adiciona a velocidade de uma entidade à sua posição, é necessário armazená-la como uma "ação") , ela praticamente exige matemática de ponto fixo, pois se você usar flutuante - ponto para suas posições / velocidade, (x+v)-vpode não ser igual a x.
BlueRaja - Danny Pflughoeft
1
@BlueRaja - Não veja onde essa abordagem exigiria muita memória. A 100 FPS e armazenando os últimos 10 segundos, você precisa armazenar 1000 n-tupels de comandos, onde n é no máximo 5 ou mais. Ou, melhor ainda, você armazena apenas a posição absoluta em cada um desses quadros e, para retroceder, basta animar o personagem para trás ao longo desse caminho. Isso também eliminaria quaisquer possíveis problemas de ponto flutuante e o levaria de volta exatamente para onde você começou.
quer
3

Em vez de ter duas matrizes separadas, você provavelmente deve ter uma classe que descreva a posição do jogador (provavelmente já existe uma classe Point em Java ... eu sou um cara de C # ultimamente) e use uma única matriz para manter posições anteriores.

Você precisa configurar um "buffer de anel", o que significa que, ao chegar ao final da matriz, você volta ao início da matriz, substituindo as entradas mais antigas. Se você voltar no tempo, o oposto será verdadeiro (quando chegar ao início, circule até o fim).

Se você deseja armazenar 30 segundos em dados passados, é necessário conhecer sua taxa de quadros se desejar pré-alocar espaço e usar uma matriz de tamanho fixo. Se você renderiza o jogo em 10 quadros / segundo, vezes 30 segundos, são 300 elementos.

Eric J.
fonte
2

O GDCVault tem uma palestra de Jon Blow (o criador do Braid ) em seu site chamado The Implementation of Rewind in Braid por US $ 3,95. Aposto que tem a informação que você deseja;)

Edição: Provavelmente não estará em Java, mas as idéias devem valer.

NoobsArePeople2
fonte
Você sabe se eles também vendem a transcrição ou apenas o áudio?
o0 '.
Não consegui encontrar nada através de uma pesquisa rápida no site. Talvez você possa usar algum software de transcrição ?
precisa saber é o seguinte
1

Como Erik J disse , armazenar as posições passadas do jogador como uma coleção de objetos pontuais em um buffer de anel parece razoável.

No entanto, eu sugeriria o uso de uma fila para implementar o buffer. É muito mais barato atualizar do que uma matriz e você não precisa conhecer sua taxa de quadros com antecedência:

update():
   time_queue.push(player.positions)
   if current_time() - start_time > n:
       queue.pop()

Isso ainda não considera as taxas de quadros variáveis ​​ou o que deveria acontecer se você realmente viaja no tempo; portanto, sugiro que você armazene um carimbo de data / hora em cada entrada e verifique se:

update():
    time_queue.push({
        'pos': player.positions,
        'time': current_time()
    })
    first_entry = queue.peek()
    while current_time() - first_entry['time'] > n:
       queue.pop()
       first_entry = queue.peek()

Espero que isto ajude!

HumanCatfood
fonte
1

Existe um padrão de design chamado Memento , acho que é o ponto de partida para um jogo como o Braid

O padrão de lembrança é um padrão de design de software que fornece a capacidade de restaurar> um objeto ao seu estado anterior (desfazer via reversão).

O padrão de lembrança é usado por dois objetos: o originador e o zelador. O originador é algum objeto que possui um estado interno. O zelador fará algo com o remetente, mas deseja poder desfazer a alteração. O zelador primeiro pede ao autor um objeto de lembrança. Em seguida, realiza qualquer operação (ou sequência de operações) que iria realizar. Para reverter para o estado anterior às operações, ele retorna o objeto de lembrança ao originador. O objeto de lembrança em si é um objeto opaco (aquele que o zelador não pode ou não deve mudar). Ao usar esse padrão, deve-se tomar cuidado se o originador puder alterar outros objetos ou recursos - o padrão de lembrança opera em um único objeto.

http://en.wikipedia.org/wiki/Memento_pattern

Informações adicionais aqui: http://dofactory.com/Patterns/PatternMemento.aspx

Marcelo Assis
fonte
2
Não vejo como isso deve ajudar. Parece que é uma "reversão instantânea", enquanto o OP pediu algo suave como o de Braid. Talvez eu tenha interpretado mal alguma coisa?
o0 '.
Esse padrão não implica em uma única reversão; você pode criar uma "linha do tempo" de ações, por exemplo. Aqui está uma postagem de blog de um brasileiro sobre o uso desse padrão: abrindoojogo.com.br/padroes-de-projeto-memento Aqui está um exemplo feito no Flash: abrindoojogo.com.br/files/fasteroids.swf Move: Arrows | Atire: Espaço | Rewind Actions: Backspace
Marcelo Assis
1

Houve um jogo lançado para o XBox360 que envolvia manipulação de tempo. Era medíocre, então não me lembro do título no momento. De qualquer forma, em uma entrevista com um desenvolvedor, eles descreveram como gerenciavam a manipulação do tempo:

Todos os quadros X (com valores mais baixos de X levando a uma manipulação mais refinada), você tira um "instantâneo" do mundo do jogo nesse ponto e o associa ao tempo no jogo.

Durante o jogo normalmente, com o tempo, cada entrada que uma reação contribui para o instantâneo definido em algum momento no futuro.

O mundo do jogo itera entre o instantâneo no momento atual e os quadros X do instantâneo no futuro.

Então, quando você quiser reverter o tempo, apenas defina a direção do tempo para trás, para que ele itere entre o presente e os quadros X do snapshot no passado (enquanto desabilita a capacidade de criar snapshots futuros).

Jordaan Mylonas
fonte
+1 para o ponto sobre a granularidade dos instantâneos.
Omar Kooheji
0

Você pode simplesmente tratar o tempo virtual do jogo como outra dimensão espacial. Então, o que é viajar no tempo de fora é um simples movimento dimensional n + 1 no universo virtual do jogo.

Supondo que haja alguma interação do usuário e algum tipo de sistema de física que determine o comportamento do seu universo quando não houver entrada do usuário, tudo o que você precisa registrar é o efeito das interações do usuário (por exemplo, alterações nos vetores de velocidade / aceleração da dimensão n + 1) , pois sua física deve ser reversível no tempo.

Dessa forma, você precisaria de muito menos memória para calcular o estado do universo do jogo em uma determinada coordenada de tempo.

biziclop
fonte
Como mencionei em outro comentário, a física em jogos de vídeo geralmente não tempo reversível, porque quando usando números de ponto flutuante, (x+v)-vpode não ser igual ax
BlueRaja - Danny Pflughoeft
0

Digamos que uma entidade tenha velocidade, rotação, posição xey. Estes são, com aceleração, os estados básicos do movimento, valores, como você o chama. Você pode salvar os dados de duas maneiras:
1. Salvar rotação, posição xey
2. Salvar rotação, velocidade x velocidade y

Se você tiver uma velocidade fixa, também poderá salvar a rotação ou apenas uma velocidade para os dois eixos.

Salvar rotação é necessário nos jogos, a menos que sua entidade tenha uma rotação estática, o que não é na maioria dos casos.

Dito isto, é necessário usar uma lista de objetos para vários valores. Se você possui um bom sistema de timer, que, por exemplo, chama métodos de atualização 20 vezes em um segundo e, portanto, é independente de fps, é possível criar uma matriz de 20 * 30 objetos para armazenar os valores de movimento necessários nos últimos 30 segundos . O uso de uma matriz simples não é recomendado, pois você precisaria mover todos os elementos que um índice deixou para cada chamada de atualização.

Com base nisso, você pode percorrer a lista ou o que quer que seja voltar. Se você realmente deseja obter um efeito "realista", use esta técnica para todos os objetos em movimento. Mas isso está relacionado ao design do jogo.

Se você destruir objetos, lembre-se de agrupá-los, para garantir que você não estresse seu amigo, Sr. Garbage;)

Marco
fonte