歩き方がおかしくなるバグ
このバグは v0.36.6 で修正されました 
プレイヤーの walk が毎フレーム実行されて、歩き始めた状態がずっと続いてしまうバグ
一度でもマップを移動した場合にしか発生しない (後述)
原因
本来時間的に重なってはいけない非同期処理 (=walk) が時間的に重なってしまったせいで、始まってすぐ中断されていたから
より詳細に言うと、「歩いているかどうか」を表す状態変数が 2 つ存在し、歩き終わった瞬間だけはその 2 つの整合性が合わなくなるバグがあったから
状態変数の 1 つは behavior で、behavior が
idle
であれば walk でき、それ以外は walk できない もう1つは
walkingObjects
という WeakSet で、元々は locate された直後に walk をキャンセルするために、歩き続けている間だけ参照を保持していた(歩いている途中に locate すると位置が戻るバグを起こさないため) ログで確認したところ、behavior が idle になってから walkingObjects から delete されるまで間があることが分かった
これは resolve のあと Promise::then は同期的にコールされないから
これはログから得られた状況から推測した結果だが、コルーチンの実行順などの細部までは検証できていない
どうやって非同期処理 (walk) が重ならないようにするか?
1. 複数の変数が不整合が起きないよう、全ての変数が同期的に (≒同時に) 変更されるようにする
2. 状態を表す変数を 1 つにする (
walkingObjects
をなくす) 2 の方がシンプルになる
唯一の問題点は、歩いている途中に locate すると位置が戻るバグを他の方法で解決する必要があること
マップを移動した場合にしか発生しない理由
マップを移動するとプレイヤーの
onenterframe
が3回ずつ呼ばれている walkImpl
は、3回ずつコールされて、3フレーム目に2回コールされたところで generator から抜けている this.parentNode.childNodes
のログを出力すると、プレイヤーの参照は残ったまま、 Proxy の参照が増えていた RPGMap::load
の中で if (Hack.player) this.scene.addChild(Hack.player);
されていた 「プレイヤーのマップを移動したら
map.load()
される」という流れと、「 map.load()
されたときにプレイヤーのマップを移動する」という流れがある 前者は必要だが、後者はいらなくないか?
ひとまず
Hack.player
は Proxy が代入されていることが分かったので、後方互換性を保つため if (Hack.player) this.scene.addChild(Hack.player.reverseProxy)
に変更した ところで、なんで2回じゃなくて3回だったんだろう。測り方が間違っていたのだろうか… 