🐡

Three.js Cannon.es 調査資料 - 非同期処理時の地図データ更新

2024/11/01に公開

この記事のスナップショット

(スナップショットは特にありません..)

関連するソースは以前の記事「マップ分割(3)3Dデータ」のものになります。

ソース「マップ分割(3)3Dデータ」

https://github.com/fnamuoo/webgl/blob/main/028

概要

データファイルをバイナリ化して同期処理させるために async/await をつけました。
動作の途中、地図データ更新(データファイルを読み込む)のために処理が止まっている想定だったのに裏で動いていておかしな挙動に。

結果から言うと、requestAnimationFrame() がループの先頭にあったために変な挙動になっていて、ループの最後にもってくれば期待したとおりの挙動に。

やったこと

物理計算やレンダリングを一定間隔で繰り返し行うため requestAnimationFrame() を使うのですが、これを関数の先頭に置いていたために期待しない挙動、マップを移動させたときに車が何かにぶつかるような挙動をしていていました。

失敗していたコード
  async function animate() {
    requestAnimationFrame(animate)
    world.step(timeStep);

    レンダリング();

    if (中央のブロックから自車がはみ出た?) {
      中央のブロック内に引き戻す座標操作();
      地図データ更新();
    }
  }

感覚的には requestAnimationFrame() で処理がフォークされ、新しいプロセス/スレッドが作成されて、引数にある関数animateが実行されるイメージでしょうか。

失敗コードのシーケンス図っぽいもの
   [animate:000]                   [animate:001]                   [animate:002]
        | requestAnimationFrame()      :
        | ---------------------------> |requestAnimationFrame()       :
        |                              | ---------------------------> |
        |                              |                              | ...
        | world.step()                 |                              |
        |                              | world.step()                 |
        | ...                          |                              |
        |                              | (こちらで処理が走ってしまう)    | (こちらで処理が走ってしまう)
        |(地図データ更新で一時停止)      |

上記の状態だと、animate:000で一時停止させているつもりでも、animate:001 や animate:002 で処理が進んでしまうのでおかしな挙動になっていたのかと予想。

requestAnimationFrame を末尾に持ってきた場合は期待した通りの結果になりました。
animete:000 の一時停止で止まった後に requestAnimationFrame() で新しい animete:001 が走り出すので期待した結果になっているのかと。

期待した通りのコード
  async function animate() {
    world.step(timeStep);
    ...
    requestAnimationFrame(animate)
  }
成功コードのシーケンス図っぽいもの
   [animate:000]                   [animate:001]                   [animate:002]
        |                              :                               :
        | world.step()                 :                               :
        |                              :                               :
        | ...                          :                               :
        |                              :                               :
        |(地図データ更新で一時停止)      :                               :
        |                              :                               :
        | requestAnimationFrame()      :                               :
        | ---------------------------> |                               :
        |                              | world.step()                  :
        x                              |                               :

以前の記事「マップ分割(3)3Dデータ」では、上記のとおりに修正済みです。

感想

requestAnimationFrame() に気づくまで、あれこれ悩んで console.log を入れまくりました。
非同期処理/スレッド処理は..手ごわいですね。(意地を張って、難しいとは言わない)

バイナリデータにする前、テキストデータを扱っていた時に「失敗していたコード」でも期待通りに動いていたのは async を 付けていなかったから なんですかね?
見切り発車で着手、勉強不足なことが露呈しましたが、解決できたので良しとします。(汗

Discussion