ゴーストの再生-ストレージとタイミング


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のストレージの質問がさらに難しくなります。

それで、このアイデアは正しいアプローチですか?はいの場合、どのようにしてデータを効率的に保存および取得できますか?私は通常、確定的なゲーム状態やユーザー入力の記録などではなく、上記のデータ構造を使用したいと思います。

助けてくれてありがとう!

編集:私が使用する環境を説明する必要があることを理解しています:Cocos2D for iPhone。方法がありupdate:(ccTime)deltaます。理想的には、このメソッドは1/60秒ごとに呼び出されますが、保証はありません。これはdelta、最後のゲームティックから経過した実際の時間であり、1/60よりもはるかに長い場合と少ない場合があります。このメソッドで、現在のゲーム状態を保存します。


2
素晴らしい質問です。これが示すように、正確な再生は、最初に考えるよりも複雑であり、人々がここで考え出した解決策を知りたいと思います。
クリスチャン

回答:


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

次に、リプレイ/ゲームが始まると、配列から最初と2番目の要素を取得します。

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

もちろん、これはいくつかの計算をキャッシュすることで最適化できます。配列を検索するのではなく、特定のインデックスを検索するだけです。


はい!後で試してみる必要がありますが、探していたようです。ありがとう!!
マリンバ

15

難しいことではありません。任意の時点でデータを保存できます(多いほど良い)。探しているタイムスタンプと2つの最も近い記録されたタイムスタンプからのデータに基づいて、データの値を補間できます。例:

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次補間の方が少し良い結果が得られるかもしれませんが、間隔が十分に狭い場合は線形がうまく機能します。
カイロタン2013

補間する方法を示してくれてありがとう!これは私のゲームに非常に役立ちます。しかし、配列の奥深く、時刻41.15に取得したいとします。41.15を超えるレコードが見つかるまで、配列全体の検索を開始しますか?
マリンバ

1
単純な線形検索があなたのために働く可能性がありますが、バイナリ検索は、優れているあなたは、ソートされた配列がある場合:en.wikipedia.org/wiki/Binary_search_algorithm
マルチンSeredynski
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.