Reprodução fantasma - armazenamento e tempo

11

Estou trabalhando em um jogo de corrida de carros e acabei de implementar um sprite fantasma para repetir corridas passadas. Uso um mecanismo de física e, depois de muita leitura, cheguei à conclusão de que a melhor maneira de armazenar os dados fantasmas para reprodução seria registrar a posição e a rotação do carro em determinados momentos, como por exemplo descrito aqui: https: // gamedev. stackexchange.com/a/8380/26261 .

Mas qual seria uma boa maneira de encontrar esses pontos no tempo durante a reprodução? Um exemplo seria um registro com esses dados:

time: +3.19932 (seconds since race start)
position:  180,40 (position at that time)
rotation: 30.4 (rotation at that time)

Mas eu tenho vários problemas com isso:

  1. Quando reproduzo, é improvável que eu atinja novamente o ponto no tempo exato em 3.19932 - mais provavelmente, terei um ponto no tempo em torno de 3.1 e precisarei encontrar o registro de correspondência mais próximo. Ao interpolar, até a correspondência mais próxima acima e abaixo. Isso parece muito ineficiente e demorado?

  2. Em qual estrutura de lista eu poderia armazenar esses registros para uma reprodução posterior? Uma matriz? Isso não significa que o tempo de busca por registros correspondentes a um determinado tempo aumentará quanto mais tempo a corrida?

  3. Qual frequência devo usar para pontos no tempo? Cada quadro seria um exagero - acho que eu deveria salvar, ou seja, cada enésimo quadro e interpolar entre eles, o que dificulta ainda mais as questões de armazenamento.

Então, essa idéia é a abordagem correta? Se sim, como eu poderia armazenar e recuperar os dados com eficiência? Observe que geralmente eu gostaria de usar a estrutura de dados acima, não os estados de jogos determinísticos e a gravação de entrada do usuário etc.

Obrigado por qualquer ajuda!

Edição: Eu sei que devo descrever o ambiente que eu uso: Cocos2D para iPhone. Existe um método update:(ccTime)delta. Idealmente, esse método seria chamado a cada 1/60 segundos, mas não há garantia - deltaé o tempo real passado desde o último clique no jogo e pode ser muito mais ou menos que 1/60. É nesse método que eu gostaria de armazenar o estado atual do jogo.

marimba
fonte
2
Excelente pergunta. Como isso mostra, uma reprodução precisa é mais complexa do que você imagina a princípio, e estou curioso para ver quais soluções as pessoas têm apresentado aqui.
Christian

Respostas:

8

Isso não significa que o tempo de busca por registros correspondentes a um determinado tempo aumentará quanto mais tempo a corrida?

Não :)

Digamos que você o armazene como uma matriz (observe que os instantâneos estão em ordem cronológica, mas não uniformemente espaçados):

snapshots = [
    {time: 0.0, position: {x,y,z}},
    {time: 0.41,    position: {x,y,z}},
    {time: 0.57,    position: {x,y,z}},
    {time: 1.10,    position: {x,y,z}},
    {time: 1.67,    position: {x,y,z}},
    {time: 2.05,    position: {x,y,z}},
    {time: 3.24,    position: {x,y,z}},
    {time: 3.86,    position: {x,y,z}},
    {time: 3.91,    position: {x,y,z}},
    {time: 5.42,    position: {x,y,z}},
    ...]

Então, quando o replay / jogo começa, você obtém o primeiro e o segundo elemento da matriz:

nextIdx = 1
previousSnapshot = snapshots[nextIdx-1]
nextSnapshot = snapshots[nextIdx]

Então, em cada quadro ( currentTimeé a hora atual neste novo jogo):

if currentTime > nextSnapshot.time
    nextIdx++
    previousSnapshot = snapshots[nextIdx-1]
    nextSnapshot = snapshots[nextIdx]

# Do your magic here, e.g.:
snapshotPairGap = nextSnapshot.time - previousSnapshot.time
ratio = (currentTime - previousSnapshot.time) / snapshotPairGap
ghostPosition = {
    x: previousSnapshot.position.x + ratio*(nextSnapshot.position.x - previousSnapshot.position.x)
    y: previousSnapshot.position.y + ratio*(nextSnapshot.position.y - previousSnapshot.position.y)
    z: previousSnapshot.position.z + ratio*(nextSnapshot.position.z - previousSnapshot.position.z)
}

Obviamente, isso pode ser otimizado armazenando em cache alguns dos cálculos. Não há pesquisa na matriz, apenas pesquisando índices específicos.

Supr
fonte
SIM! Preciso tentar isso mais tarde, mas parece ser o que eu estava procurando. Obrigado!!
marimba
15

Não é muito difícil. Você pode armazenar seus dados em pontos arbitrários no tempo (quanto mais, melhor) e pode interpolar os valores dos dados com base no carimbo de data / hora que você está procurando e nos dados de dois registros de data e hora mais próximos, por exemplo:

N | Time | Position | Rotation
1 | 0.05 | 1, 1, 1  | 0
2 | 0.15 | 1, 2, 1  | 0
3 | 0.25 | 1, 3, 2  | 30

Agora imagine que você deseja obter posição e rotação no tempo 0,10. Como 0,10 está entre os pontos '1' (significando 0,05 tempo) e '2' (significando 0,15 tempo), você precisa interpolar esses pontos.

timestamp = 0.10
factor = (timestamp - Time[1]) / (Time[2] - Time[1])
position = Lerp(Position[1], Position[2], factor)
rotation = Lerp(Rotation[1], Rotation[2], factor)

Lerpé apenas interpolação linear .

Então, vamos preencher as lacunas com alguns exemplos (*).

N | Time  | Position    | Rotation
1 | 0.05  | 1, 1,    1  | 0
* | 0.075 | 1, 1.25, 1  | 0
* | 0.10  | 1, 1.5,  1  | 0
2 | 0.15  | 1, 2,    1  | 0
* | 0.20  | 1, 2.5,  2  | 15
3 | 0.25  | 1, 3,    2  | 30

HTH.

Marcin Seredynski
fonte
5
+1. Interpolação é a resposta simples e eficaz aqui. Pode ser verdade que a interpolação cúbica possa fornecer resultados um pouco melhores quando o veículo estiver girando, mas linear funcionará bem se os intervalos forem pequenos o suficiente.
Kylotan
Obrigado por mostrar como interpolar! Isso será muito útil para o meu jogo. Mas digamos que eu gostaria de recuperar no horário 41.15, dentro da matriz. Você começaria a pesquisar em toda a matriz até encontrar um registro> 41.15?
marimba
1
A pesquisa linear simples poderia funcionar para você, mas busca binária é melhor, quando você tem um array ordenado: en.wikipedia.org/wiki/Binary_search_algorithm
Marcin Seredynski