アイソ×トリック

=前置き=

wonderflに上げた「アイソ×トリック(Isome-Trick)」の、「ネタの解説」と「コードの解説」。特に、コードの方は数カ月後に読みなおして理解できる自信がないので、自分のためにまとめておく。

=ネタ=

=思いつき方=

今回のはContinuity: A Flash Puzzle Platformerにインスパイアされて思いついた。
Continuityを「15パズル(4x4マス上のブロックを動かすアレ)のアクションゲームへの流用」と捉えて、「特殊な平面をアクションゲームに流用する」という風に一段階メタに上げて、それを「立方体の平面をアクションゲームに使う」という風に一段階ベタに下げたのが最初に考えたネタ。しかし、これは作成コストやステージ設計の困難さなどから一度頓挫し、その後に「壁と床と壁」をまたぐようなゲームを考えているときに「立方体の3面だけなら作りやすいかも」と思いついたのでこの形式になった。

=特性=

今までの「プレイヤーが特殊な能力を持つ」のとは違い、これは「ステージが特殊な能力を持つ」という状態に近い。そのため、このステージにさらに「アクトレーザー」を加えたり、「シロクロシリーズ」を加えることが可能。アクトレーザーは擬似的に屈折に見えるし、シロクロシリーズに至ってはおそらく何が何だかわからない状態になるだろう。

そういうわけで、「組み合わせ」を考慮すると「プレイヤーじゃないものが特殊な能力を持つ」というパターンをもうちょっと考えていきたい。それが「プレイヤーの能力」とシナジーを持てばなお良し。

=コード=

=描画方法について=

左の壁」「右の壁」「床」の3面を用意し、それに対応したレイヤーをまるごとその面に描画する、というのが概要。

プレイヤーに関しては全てのレイヤーに登録しておいて、全てのレイヤーで描画している。面をまたがない限りはプレイヤーは面の範囲外になるので、自分の居ない面に関しては描画時にクリッピングされる。

アイソメトリック(あるいは平行投影)による描画はDisplacementMapFilterによる実現方法なども考えたが、将来的に「立方体上を移動するアクションゲーム」に拡張する可能性も考え、PV3Dを使って実現することにした。

=面をまたぐ処理について=

「一周」すればわかるとおり、面をまたぐと「重力の方向変更」や「プレイヤーの回転表示」が必要になってくる。今回はこれを「面ごとにBaseMatrixを用意する」という形にして対応した。

まず、「表示位置」と「計算用の位置」を切り分けた。「表示位置」は面ごとに存在するが、「計算用の位置」は常に一つ。この「計算用の位置」に「それぞれの面のBaseMatrix」をかけることで「それぞれの面の表示位置」を計算するようにした。

「プレイヤーの回転」にもBaseMatrixを使っている。これは単純に「BaseMatrixの回転成分」を「プレイヤーの回転量」として使っているだけ。

「重力の方向」や「入力で移動する方向」に関しては内部では特に何もしていない。普通に「重力は下に働く」「左入力で左に動く」としている。ただし、それは「計算用の位置」に対して行われ、これにBaseMatrixがかかることで見た目上は回転しているように見える。

BaseMatrixは「片方の面の移動は、もう片方の面からどう見えるか」という考えに基づいて計算される。今回は隣接する面だけ考えれば良いので、「左に隣接している面での位置は、この面ではどういう位置になるか」「隣接している面が90度回転している場合はどうか」という風に考えれば良い。
これは「どの辺とどの辺をつなげるか」の組み合わせなので、全てのパターンを場合分けしてもなんとかなるものの、今回は「上の辺で隣接するように基本的な回転」×「上の辺を実際の接続辺になるように回転」と分けることで多少簡略化している。

あとは面をまたぐたびに「前回のBaseMatrix」に「次の面のBaseMatrix」をかけ合わせていけば良い。隣接する面の描画用のMatrixも同様に「今居るBaseMatrix」に「その面のBaseMatrix」をかければOK。

=問題=

ちゃんと調べてはいないが、面をまたぐ際の計算で誤差が蓄積していく可能性がある。微小な誤差であっても、移動アルゴリズムは1ドット単位でのチェック方法になっているため、見えない壁のようにひっかかる可能性がある。現状でも、上の中央ブロックからジャンプして左を押し続けると、見えないブロックに乗っているような挙動になる。(原因が誤差かは未調査)


→その後、拡張の際にMatrixの誤差の問題であることがわかった。円周率(PI)は非整数であり、プログラム上だと桁数が限られるため、SinやCosの値が微妙に0や1とズレてしまうのが原因だった。