配列処理に失敗してたところを探した話
妹「ヒジョーに困ったことになってるんだけど、何が困ってるのかよくわからないので困っている。完璧な計画だったはずなんだけど……」
「どういう計画だったの?」
妹「基本的には近くに操作可能な物があるとわかるようになるという仕組み。隠れられる茂みとか、動かせる岩とか、あと拾えるアイテムとかも。色々重なってる場合は、一番近くにあるものが反応する。ボタンを押した時に反応するアクターを決めるのと、あと押さなくても見た目でわかるようにするのと2つの役割がある。今まで作ったのだと重なってる時に全部反応するからちょっと困ったことになっていた」
妹「問題なのはたくさんのアクターが近くにある場合。配置で避けられる部分もあるけど、アイテムとかはプレイヤーが持ち運ぶこともあるから。それでこうやってオーバーラップするたびに判定用の配列にアクターを追加してる。これを使って、操作ボタンごとに一番近いやつが反応するようしたい。茂みの近くだから岩が動かせないとかはおかしいから」
「ふんふん。だいたいやりたいことはわかった気がする。それで何が上手くいってないの?」
妹「それがわからないのが困っている。多分この中のどこかがおかしいだと思うんだけど……」
左側
右側
妹「これのもっと左の方はイベントティックにつながってる。で、このまま動かすと、どの茂みも無反応になる。最後のRemoveを実行しなければ茂みには入れるんだけど、それだと別々のボタンで操作できる物がある時に対応出来ない」
「えっと、前半のForEachLoopでオーバーラップした時に作ってる配列のアクターごとの距離を調べてて、後半のForLoopで近いアクターから順に何ボタンに反応するのか確かめると。DoOnceだから先に登録した方、つまり近いアクターが優先される。で、処理を済ませたアクターは配列から除去して、残ったアクターの中で一番近いやつを探す……と」
妹「そうそう。何度見ても完璧な……あ、一番近いやつ探すのにMaxOfFloatArray使っちゃってる。MaxじゃなくてMinにしないと遠い方優先になってしまう……」
「それはそうかもしれないけど、でもそこは重要な問題ではなさそうな気がするなあ。両方重なってて、どっちにも入れてないわけだから。これはまたPrintStringまみれにしていくしかないね。前半はとりあえず問題なさそうだから後半から」
「2つしかない配列なのに0、1、2と3回ループしてるのは……ForLoopのLastIndexに配列のLengthを繋いでるからか」
妹「あ、ほんとだ。LengthとLastIndexは毎回どっちにしたらいいのか混乱する……」
「たしかにややこしいんだけど、この場合はピンにlastindexって書いてあるからね。でもこれも致命的な問題じゃない」
妹「ループの途中で配列からRemoveしちゃうとループが狂ってまずいのかな?」
「それはよくないね。いったんNullにするとかで削除以外の方法を使うとか、LastIndexを別の変数にSETしておくとか。でも今回の場合そういう話でもなさそうな。それが原因だとしたら最初の1回はループが回るはずだから、どこにも入れないというのは変な……このインタラクティブアクターって配列はさ、オーバーラップした時に作ってるんだよね? これの中身Removeしたら、次のティックが回ってきた時には配列空っぽになってない?」
妹「……おやあ?」
「やっぱりそうだ。これ消しちゃいけないやつだ。事前に配列をコピーして作業用にするか、Removeせずに処理するようにしないといけない。色々手はあるけど、Removeはせずに、元々の設計を活かした形で考えるとこんな感じ」
妹「Removeしないで……代わりにアクターの距離が入った配列になんかしてるみたいな?」
「そもそもなんでRemoveしたかったかというと、MinOnFloatValueに何度も引っかかりたくなかったからでしょ? その処理をやってるのはアクターの距離配列だから、ここに一番遠い距離、つまり一番大きな数字を入れてやればいい。99999とかでも良かったけど、3D空間の座標って結構大きい数字もありえるから、念のため配列の中で一番大きい数字よりさらに1大きくした。こうすると距離の近い順が1つ繰り上がるから、最後までやれば全部チェックできる」
妹「アクターの距離のクリアが足してあるのは?」
「それはイベントティックのたびに作ってる配列だから、ほうっておくとどんどん中身が増えちゃうんだよ。途中でremoveしないようにしたから、最後にまとめてクリアしてる」
妹「ほうほう。これで私の当初の計画通りの仕組みが出来たのか。ちゃんと一番近い茂みに入れるようになった」