고스트 재생-저장 및 타이밍


11

저는 자동차 경주 게임을하고 있는데 과거 경주를 다시 플레이하기위한 유령 스프라이트를 구현했습니다. 나는 물리 엔진을 사용하고 많은 독서 후에 고스트 데이터를 저장하는 가장 좋은 방법은 예를 들어 https : // gamedev에 설명 된 것처럼 주어진 시점에서 자동차의 위치와 회전을 기록하는 것이라고 결론을 내 렸습니다. stackexchange.com/a/8380/26261 .

그러나 재생 중에 해당 시점을 찾는 좋은 방법은 무엇입니까? 예를 들어이 데이터가있는 레코드가 있습니다.

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

그러나 나는 그것에 몇 가지 문제가 있습니다.

  1. 다시 재생할 때 3.19932의 정확한 시점에 다시 도달 할 가능성은 거의 없습니다. 아마도 3.1 정도의 시점이 있고 가장 가까운 일치하는 레코드를 찾아야 할 것입니다. 보간 할 때 위와 아래에서 가장 가까운 일치도. 이것은 매우 비효율적이고 시간 소모적 인 것처럼 들립니까?

  2. 나중에 재생할 수 있도록 이러한 레코드를 저장할 수있는 목록 구조는 무엇입니까? 배열? 그렇다고 특정 시간과 일치하는 레코드의 검색 시간이 레이스가 길어질수록 증가한다는 것을 의미하지 않습니까?

  3. 시점에 어떤 빈도를 사용해야합니까? 각 프레임은 과도하게 추측됩니다. 오히려 모든 n 번째 프레임을 저장하고 그 사이에 보간해야하므로 2의 스토리지 질문이 훨씬 어려워집니다.

이 아이디어는 올바른 접근법일까요? 그렇다면 어떻게 효율적으로 데이터를 저장하고 검색 할 수 있습니까? 나는 일반적으로 위의 데이터 구조를 사용하고 결정적 게임 상태와 사용자 입력 기록 등을 사용하고 싶지 않습니다.

도움을 주셔서 감사합니다!

편집 : 내가 사용하는 환경을 설명해야한다는 것을 알고 있습니다 : iPhone 용 Cocos2D. 방법이 update:(ccTime)delta있습니다. 이상적으로,이 방법은 1/60 초마다 호출되지만, 보장은 없습니다- delta마지막 게임 시간 이후 실제 시간이 지났으며 1/60보다 많거나 적을 수 있습니다. 이 방법으로 현재 게임 상태를 저장하고 싶습니다.


2
훌륭한 질문입니다. 이것이 보여 주듯이, 정확한 리플레이는 처음에 생각하는 것보다 더 복잡하며, 사람들이 여기에서 어떤 솔루션을 생각해 냈는지 궁금합니다.
Christian

답변:


8

그렇다고 특정 시간과 일치하는 레코드의 검색 시간이 레이스가 길어질수록 증가한다는 것을 의미하지 않습니까?

아뇨 :)

그것을 배열로 저장한다고 가정하십시오 (스냅 샷은 시간 순서대로 정렬되지만 간격이 균일하지는 않습니다).

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}},
    ...]

그런 다음 재생 / 게임이 시작되면 배열에서 첫 번째와 두 번째 요소를 얻습니다.

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

그런 다음 각 프레임에서 ( currentTime이 새로운 게임의 현재 시간입니다) :

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)
}

물론 이것은 일부 계산을 캐싱하여 최적화 할 수 있습니다. 배열을 검색하지 않고 특정 색인을 검색합니다.


예! 나중에 이것을 시도해야하지만 이것이 내가 찾던 것 같습니다. 감사!!
marimba

15

너무 어렵지 않습니다. 임의의 시점에 데이터를 저장할 수 있으며 (더 많을수록 좋습니다), 원하는 타임 스탬프와 가장 근접하게 기록 된 두 타임 스탬프의 데이터를 기반으로 데이터 값을 보간 할 수 있습니다.

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

이제 0.10 시간에 위치와 회전을 원한다고 상상해보십시오. 0.10이 '1'(0.05 시간을 의미)과 '2'(0.15 시간을 의미) 사이에 있으므로 이들을 보간해야합니다.

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그냥 선형 보간 입니다.

몇 가지 예 (*)로 차이를 채워 봅시다.

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.


5
+1. 보간은 여기서 간단하고 효과적인 답변입니다. 차량이 회전 할 때 3 차 보간이 약간 더 나은 결과를 제공 할 수 있지만 간격이 충분히 작 으면 선형이 잘 작동합니다.
Kylotan

보간 방법을 보여 주셔서 감사합니다! 이것은 내 게임에 매우 유용합니다. 그러나 41.15 시간에 배열 내부의 깊이를 검색하고 싶다고 가정 해 봅시다. 41.15보다 큰 레코드를 찾을 때까지 전체 배열을 검색하기 시작 하시겠습니까?
marimba 2013

1
간단한 선형 검색이 도움이 될 수 있지만 정렬 된 배열이있는 경우 이진 검색이 더 좋습니다. en.wikipedia.org/wiki/Binary_search_algorithm
Marcin Seredynski
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.