プチコン講座

第5回 疑似3Dゲームを作ろう(スキーゲーム編)

【プチコン/mkII 両対応】

 今回はプチコンを使って3Dゲームを作ろうと思います。とはいっても、3D立体視や3Dポリゴンではなく単なる「疑似3D表示ゲーム」です。
 「疑似3D」というのは見た目で遠近感があるというだけのものですが端的に言えばゲーム内で奥行きを示すパラメータを用意して遠くにあるものは小さく、近くにあるものを大きくすればいいのです。極論からいえばゲーム内で奥行き感が表現されていればそれは疑似3Dといえるかもしれません。

 疑似3Dを最も簡単に実現する方法はスプライトの拡大縮小を利用することでしょう。プチコンではSPSCALEによってスプライトを1%〜200%のサイズで表示可能となっています。スプライトによる疑似3Dはアーケードゲームにおいてもスペースハリアー、アフターバーナー、アウトランなど多数のゲームで採用されています。

 スプライトによる疑似3D表示を行う場合に注意すべき点は下記の2つでしょう。

 (1)表示座標
 (2)拡大率の変化

(1)に関しては表示を行ってみれば一目瞭然です。「遠くにある物体が一定速度で手前に近づいている」というのをスプライトの拡大縮小によって表現してみましょう。

スプライト拡大サンプル(1)
VISIBLE 1,,,,1,1
GPAGE 0
SPPAGE 0
SPSET 0,156,0,0,0,0
GLINE 0,96,255,96,2
GLINE 128,0,128,191,2
X=128
Y=96
@LOOP
 FOR P=1 TO 200
  SPSCALE 0,P
  SPOFS 0,X,Y
  VSYNC 1
 NEXT
GOTO @LOOP

 このサンプルプログラムを実行して赤い線の交点(X座標128、Y座標96)が原点(要するに消失点)となっています。しかし、原点上をどんどん近づいているのに右下に移動しているように見えます。これはスプライトの座標の基準が左上にあるためです。
 これに関しては第2回でも書いたようにスプライトの拡大表示を行うためにはその座標を補正する必要があります。この補正計算をする場合にはスプライトの中心を元にした方が都合が良いのですが、それはスプライトの縦横サイズの半分となります。そうすると16x16ドットスプライトの場合は縦横8ドットの補正が必要になります。厳密には7.5ドット分の補正ですが0.5ドット単位では表示できないため8ドット分の補正とします。
 この8ドットというのはあくまで縦横16ドットのスプライトの場合です。これをSPSCALEで拡大した場合は拡大率をP(%)したときにP/12.5ドットの補正が必要といえます。ちなみに第2回の講座で書いたようにmkIIの場合はSPHOME命令でスプライトの中心座標を基準にしておけばこの補正は不要です。

スプライト拡大サンプル(2)
VISIBLE 1,,,,1,1
GPAGE 0
SPPAGE 0
SPSET 0,156,0,0,0,0
GLINE 0,96,255,96,2
GLINE 128,0,128,191,2
X=128
Y=96
@LOOP
 FOR P=1 TO 200
  SPSCALE 0,P
  SPOFS 0,X-P/12.5,Y-P/12.5
  VSYNC 1
 NEXT
GOTO @LOOP
※サンプル(1)からの変更部分が青字になっています。

スプライト拡大サンプル(2)【mkII専用】
 VISIBLE 1,,,,1,1
 GPAGE 0
 SPPAGE 0
 SPSET 0,156,0,0,0,0
 SPHOME 0,8,8
 GLINE 0,96,255,96,2
 GLINE 128,0,128,191,2
 X=128
 Y=96
@LOOP
 FOR P=1 TO 200
  SPSCALE 0,P
  SPOFS 0,X,Y
  VSYNC 1
 NEXT
GOTO @LOOP

 これをY座標下方向に移動させてみます。
 ここでまず考えなくてはならないのはY座標をどのように変化させるかということでしょう。 拡大率200%(32x32ドットのスプライト)の時に画面中央の真下に来るようにするのにはどのようにすれば良いかを考えます。32x32ドットのスプライトが画面下(Y座標191)に達するときには左上の基準座標のY座標は160になっています。つまり拡大率が200%変わる間に原点を基準に考えると64ドット移動していることになります。
ただし、すでにY座標はスプライト中央に補正済みなのでスプライト中央座標基準でいえば合計80ドット移動しなくてはなりません。するとループ1回当たりのY座標の移動量は80/200=0.4となります。

スプライト拡大サンプル(3)
 VISIBLE 1,,,,1,1
 GPAGE 0
 SPPAGE 0
 SPSET 0,156,0,0,0,0
 GLINE 0,96,255,96,2
 GLINE 128,0,128,191,2
 X=128
 Y=96
@LOOP
 FOR P=1 TO 200
  SPSCALE 0,P
  SPOFS 0,X-P/12.5,Y-P/12.5+P*0.4
  VSYNC 1
 NEXT
GOTO @LOOP
※サンプル(2)からの変更部分が青字になっています。

スプライト拡大サンプル(3)【mkII専用】
 VISIBLE 1,,,,1,1
 GPAGE 0
 SPPAGE 0
 SPSET 0,156,0,0,0,0
 SPHOME 0,8,8
 GLINE 0,96,255,96,2
 GLINE 128,0,128,191,2
 X=128
 Y=96
@LOOP
 FOR P=1 TO 200
  SPSCALE 0,P
  SPOFS 0,X,Y+P*0.4
  VSYNC 1
 NEXT
GOTO @LOOP


 これを実行してみると何だか違和感を感じてしまいます。この違和感というのは「近づくにつれ遅くなっているように見える」というものです。

(2)違和感が生まれる原因となっているのは遠くにあるはずの小さな物体と近くにある大きな物体が画面上において同じ速度で動作しているためです。そのため見た目の移動速度は変わっていなくても近づくにつれて遅くなっているように見えるのです。
 この原因は拡大率が正しくないためです。2倍離れた場所にある物体は本来は1/2のサイズで見えるはずです。そのさらに2倍の距離にある物体は1/4のサイズに見えるはずです。つまり、一定の速度でこちらに向かって来ている物体を表現する場合には拡大率50%から拡大率100%に変わるのにかかる時間と拡大率100%から拡大率200%に変わる時間は等しくなければなりません。しかし、上記のプログラムでは拡大率が50%→100%にかかっている時間と比べて拡大率100%→200%にかかっている時間は2倍となっています。これは近づくにつれてゆっくり拡大していることになります。したがって、遅くなっているように見えているのです。

 一定速度で近づいている物体は、一定時間に一定比率で拡大する必要があります。つまり、違和感を無くすためには指数関数的な拡大が必要になるわけです。
 ループ200回で200倍のサイズにするためにはループ1回につき200^(1/200)≒1.0268456倍にしなくてはなりません。もっともプチコンの演算精度はそこまで高くはないし、そこまで厳密な表示を求めているわけではないため1.026倍にすることにしましょう。(下記コラム参考)

スプライト拡大サンプル(4)
 GPAGE 0
 SPPAGE 0
 SPSET 0,156,0,0,0,0
 VISIBLE 1,,,,1,1
 GLINE 0,96,255,96,2
 GLINE 128,0,128,191,2
 X=128
 Y=96
@LOOP
 P=1
 FOR I=1 TO 200
  SPSCALE 0,P
  SPOFS 0,X-P/12.5,Y-P/12.5+P*0.4
  P=P*1.026
  VSYNC 1
 NEXT
 GOTO @LOOP
※サンプル(3)からの変更部分が青字になっています。

スプライト拡大サンプル(4)【mkII専用】
 GPAGE 0
 SPPAGE 0
 SPSET 0,156,0,0,0,0
 SPHOME 0,8,8
 VISIBLE 1,,,,1,1
 GLINE 0,96,255,96,2
 GLINE 128,0,128,191,2
 X=128
 Y=96
@LOOP
 P=1
 FOR I=1 TO 200
  SPSCALE 0,P
  SPOFS 0,X,Y+P*0.4
  P=P*1.026
  VSYNC 1
 NEXT
 GOTO @LOOP

 これで、「近づくにつれて遅くなっているように見える」ということもなくなり「一定速度で近づいている」というのが表現できるようになりました。X座標も同様に変化させればどの座標であっても擬似3D表示を自然な感じで行うことが可能になります。

 ただし、近づくといってもどこを基準点に距離を考えているかというのは非常に分かりにくいと思います。疑似3Dの場合は奥行きをZ座標として考えた場合にはそれをどこからそのキャラを見ているのかというもの(端的に言えばそれを見ているカメラのZ座標)が分かればそこからの距離に反比例した大きさで表示すればいい(その計算式はTipsコーナーの「3D座標をスクリーン座標に変換」の項目を参照)のですがその基準となる点は非常に分かりにくいです。したがって、ここでは分かりやすくするため画面の端にキャラが見える時(言い換えれば画面上で一番キャラが近づいて見えるとき)にZ座標がゼロになるように設定しています。
 本来ならば距離ゼロではサイズが無限大になって表示ができないのですが、距離で割るのではなく指数関数的に拡大表示することで距離ゼロでも問題なく表示が可能になっています。そのため近づくにつれて誤差はあるのですが疑似3Dは厳密な3Dではなくちゃんと奥行きがあるように見えれば問題ないです。実際に上記のプログラムを動作して分かるように見た目で違和感がないし距離も感覚で分かるし計算も簡単に済むというメリットがあります。(これはいかに疑似3Dを違和感なく単純に済ませるかということで昔ポケコン用のゲームで考えたアルゴリズム)

 とはいうものの、プチコンの場合は32ビット固定小数点のみサポートしていて演算誤差が出やすいためループごとに乗算を繰り返せばそれが蓄積されて思わぬ大きな誤差になるという可能性もあるため注意が必要です。

コラム プチコンの演算精度

 プチコンは32ビットの固定小数点のみをサポートしています。32ビットのうち整数部分が19ビット、小数部分が12ビット、符号が1ビットとなっています。19ビットで表すことのできる最大数は&B1111111111111111111(「1」が19個)となるのですが、これは2の19乗-1であり、524287になります。
 符号部(1ビット)+整数部(19ビット)+小数部(12ビット)でプチコンでは数値を表現しているのですが、負の数は符号部のビットが1(true)になるため-1は&B111111111111111111111(「1」が20個で先頭の「1」は負数を示す)のように補数で表現されています。この辺はC言語等で符号付き8ビット整数において-1が0xffになるのと同じですね。そのため0 OR Aで変数Aの値の小数部が切り捨てられるのと同じように-1 AND Aで変数Aの値の小数部が切り捨てられるわけです。

 負数は補数で表現されるため-1/4096&B1111111111111111111.111111111111(符号、整数部、小数部すべて含めて「1」が32個並ぶ)となります。ここでFLOOR関数を実行すると小数部が切り捨てられて上記のように「1」が20個並んだ-1となります。つまり、FLOOR(-1/4096)=-1となるわけです。
 FLOOR関数を実行した際に負数では正数とは絶対値が異なるものになっているのですが、このように内部保存形式を理解していればその理由は簡単に分かると思います。小数部分を切り捨てて整数化=その値を超えない最大の整数となる)

 プチコンで計算させる場合にはその絶対値が524288未満にしなければOverflowとなってしまうわけですが、むしろ問題なのは小数部分の方なのです。小数部分は12ビットとなっており、1/4096単位の数しか扱えないからです。そして表示の段階では小数第3位までとなっています。それでは小数第4位以下をプログラム中に記述することは無意味かというと必ずしもそうとは言い切れません。それは構文解析の段階では小数第6位まで数値として読み取っているためです。
 私の解析結果では小数第1位〜第6位までは構文分析にかかる時間は1桁につき12〜14μフレーム(1μフレーム=1/60000000秒)となっているのに対して小数第7位以降は4〜5μフレームとなっているからです。この値はただのスペースの構文分析にかかる時間と同程度の時間であり小数第7位以降を記述しても単なる空白文字の扱いになっていると推測されます。その小数第6位まで数値と読み取ったあとは6桁目が四捨五入されます(もっとも、この時点では「数値扱い」であり数値ではないためアスキーコードが&H35以上かそうでないかで判断していると思われる)。その際に0.999995という値は6桁目が四捨五入されて1になるのかというとそうではなくキャリーオーバーは無視されるみたいなので0になってしまいます。(これは言い換えると小数部は内部では整数部と別々に処理しており、構文解析の段階で四捨五入によって小数部から整数部への繰り上がりを行うという例外処理が行われていないためと言える)
 その後に1/4096単位に丸めたものが定数用の領域に2進数で入ります。例えば0.999994は0.99999と見なされ1/4096単位に丸められた時点では小数点以下は2進数表記で111111111111(=4095/4096)になり、これは表示の段階では10進数表記で小数第4位が四捨五入されるため画面では「1」と表示されます。

 この1/4096単位の丸め処理にも注意が必要となります。これはどういうことかというとn/4096で表現できる数(ただし、nは整数)以外は誤差が発生するということです。

表示される数
プチコンの内部で扱われる数
誤差
0.1
409/4096
0.15%
0.01
40/4096
2.3%
0.001
4/4096
2.3%

 誤差が発生するのは固定小数点だけの問題ではなく浮動小数点でも起こりうることです。浮動小数点では「桁落ち」によって大きな誤差が発生しますが、その頻度はそれほど高くはなくあくまで加減算を行わなければ浮動小数点では大きな誤差は発生しません。それに対して固定小数点では加減乗除を行わず定数として表現するだけで大きな誤差が発生する場合があります。そして、上記の表を見ても扱う数字が小さくなればなるほど相対的な誤差が大きくなります。(扱える数の最小単位が1/4096であるため小さい数字だと正確に表現するのが難しくなるため)
 誤差はワーストケースだとこのようになります。

 1/4097 → 0
 1/2049*2049 → 0.5

 このような事態を避けるためには「なるべく小数を扱わない」「できるだけ計算途中で1未満の小さな数にならないようにする」「どうしても小さな数を扱う必要がある場合は極力2の累乗分の1にする」などの工夫が必要になる場合もあります

 では具体的にどうするのかというとT=T+0.02という処理が必要な場合は1ずつ加えておいて表示段階で50で割るというのは1つの手です。厄介なのは乗算、除算でしょう。計算途中で小さな数になる可能性が高いからです。
 A/B*Cという計算を行う場合はA、B、Cいずれも1より大きいならばA/B*CよりもA*C/Bの方が演算誤差が小さくなる場合が多いです。(ただし数字が大きい場合は逆にOverflowの可能性もあるけど)
 具体的な数字を出すと例えば250*0.03をプチコンで計算すれば7.446になります。これは0.03はプチコン内部では122/4096となってしまうため250*122/4096≒7.446となるわけです。実は、250*0.03は250*(3/100)と同じなのです。「何当たり前のことを言ってるんだ?」と思うかもしれませんが、上記の「計算途中で小さな数になる」という状態が発生していると言えます。しかし、このカッコをはずして計算すると250*3/100=7.5となります。これは上記のような1/4096単位で記録された内部記録と小数第3位までしか表示されない表示との誤差が全くない純粋な7.5です。

 この表示の段階での丸め処理を見るには次のようにすればよく分かります。
 A=7.5-1/4096としてAの値を表示してみてください。表示上は7.5になっていることが分かります。では次にA-7.5の値を表示してみてください。画面には「-0」というおかしな数字が表示されているでしょう。実はこれは-1/4096なのです(ちなみに&B10000000000000000000とすれば通常はありえないぴったりマイナス0の値となる)
 Aには最初に7.5から1/4096を引いた値が入っているのでそこから7.5を引けば-1/4096になるのは当たり前のことですが、こうして見ると表示された数字というのは実際は丸められた数字であるということがよく分かるでしょう。(この丸めた数字であればプチコンでは見られないはずの524288という計算結果を得ることも可能になる)

 プチコンの固定小数点は計算誤差という面においては不利になる場面は多くありますが、誤差の発生しやすい状況を把握してそうならないような事前対応策をとればそれほど問題はありません。また、誤差を軽減する方法も定数であれば冒頭に書いたようにn/4096(nは整数)とすることで可能になります。誤差をできるだけ少なくしたいという場合は工夫次第で何とでもなります。4096で割るとなると処理時間を考慮すると避けたいかもしれませんが、0.01をより正確に記述する場合には0.01に最も近い数字は41/4096であり、これは0.010009765625であるため上記のようにプチコンでは小数第6位まではちゃんと認識されているため0.010009とすれば問題ありません。(もっとも、0.0101でも41/4096に丸められるため小数第6位まで記述しなくても切り上げ処理で小数第4位まで記述すれば十分だけど)

 上記のサンプル(4)のような疑似3Dにおける不自然にならない拡大方法を使ってゲームを作ってみることにします。(この拡大方法は感覚的に分かりやすくシンプルにするためカメラ位置を自キャラの位置に設定にしており、上記のように少し誤差があり、これを正確に計算したい人は第6回のコラムを参照)
 今回作るのはスキーゲームです。

 スキーゲームといえば8ビットパソコンでは昔から作られているBASICのゲームの中では定番ともいえるものですが、それらはPRINT文でスクロールさせることによって実現しています。
 まずはそれをプチコンでも作ってみましょう。

 コンソール画面でのゲーム作りに関しては第1回で書きましたし、今回は疑似3Dがテーマであるため端折りますが私が作ったものはこんな「ワンキースキーゲーム」です。



 ゲームを開始したら「何かボタンを押している間は右」「何も押してない間は左」に動作することで次々にやってくる旗の間を通過するというシンプルなゲームです。こんなゲームでも左右交互に旋回しながら滑走しているスキーがちゃんと表現されていると思います。

 わざわざこんなことをするのはスキーゲームをどのようなものにすべきかという方向性の確認のためです。
 やはり、スキーといえば坂道をすべっているため何もしなくてもどんどん加速するし、まっすぐ滑降する場合でも左右旋回を使用します。これを考えるとスキーゲームとレースゲームは根本的に異なるものになるはずです。では、それを今度は疑似3Dで表現します。
 スキーゲームの場合はレースゲームとは異なり、周囲が真っ白な雪景色でも旗が表示できればそれっぽくなるというのは上記のコンソール版のスキーゲームでも分かると思います。要するに遠くから近づいてきて徐々に拡大してくる旗を疑似3Dで表現できれば疑似3Dスキーゲームを作ることが可能になります。

 というわけで、疑似3Dスキーゲーム「プチコン スキー」を作ってみました。



 シンプルなゲームながら一応スキーっぽくはなってると思います。
 しかし、上記でスプライトで疑似3Dを行う方法について書きましたが実際に旗の表示はスプライトを使っていません。それは人物キャラに関してはプリセットデータで剣を両手に持たせたらストックっぽく見えるからそれで代用したのですが、旗の代わりになるプリセットデータが無かったからです。1画面で作るためあまり余裕がなかったのですが、GLINEとGFILLで旗を表現しています。
 スプライトであろうとグラフィックであろうと疑似3Dに対する考え方そのものは何も変わりません。

 私は以前にポケコン用の疑似3Dスキーゲームとなる「シュプール」を作りました。その「シュプール」は私の高速化テクニックを駆使しているためオールBASICで8fpsという速度(疑似3DをポケコンBASICで実現したことを考えれば非常に高速)を実現しているのですが、「プチコン スキー」は表示しているのが旗だけであるため何の工夫も無くても楽勝で60fps動作しています。(16行にあるVSYNC 1を取り除けば300fpsくらいで動作する)
 この60fpsという速度によって「シュプール」では出来なかった左右旋回を交互に行って減速するというゲーム性を取り入れることができました。(「シュプール」は一般的なレースゲームのようにできるだけ減速しないように進むのが基本となっている代わりにスキーっぽい操作方法でプレイするゲームになっている)

 違和感のない疑似3D表示ができても問題はゲーム内容です。スキーゲームを作るのに必要になるものは「スピード感」「スキーの操作感」「適切な当たり判定」でしょう。
 1画面にプログラムサイズを抑えているということもあり、複雑な操作性や加速、減速処理を行うのは難しいですが、操作としては真っ先に考えたのが[L][R]ボタンでの左右旋回です。一般的には十字ボタンで行う場合が多いのですが[L][R]で行うのは気分の問題に加えてそちらの方がこのゲームの場合は操作しやすく感じたからです。
 しかし、両方のボタンを使用するにはリストが長くなってしまうため(K+1)%3-1という式で強引に両方とも使えるようにしました。

 スキーの操作感を得るためには左右旋回で操作するということが必要でしょう。左右交互に旋回しながら旗の間を通過するというゲームシステムは簡単ですが、それをユーザーにやってもらうには上記のコンソール版ワンキースキーゲームのように強制的に左右交互にしか動けないようにするしかありません。そもそもスキーにおいては交互に左右旋回を行っているのは速度調整のためです。したがって、何もしなければ制御できないくらい加速、左右旋回中は減速してそれが制御可能になるというシステムにしました。
 ところが、「スピード感を重視すれば速すぎてプレイできない」「左右旋回で減速しすぎたらスピード感が失われてしまう」となかなかうまくいきません。そのため実際に何度もテストプレイをして数値の微調整をする必要がありました。

 ゲームバランスをとるためにはテストプレイが重要ということを何度も書いてきたのですが、テストプレイをやりすぎるとゲーム制作者本人が上手くなりすぎるという問題が発生する場合があります。そうすると難易度が客観的に分からないという状況に陥ることも多く自分では普通の難易度と思っていても実はめちゃくちゃ難しいということが十分に起こってしまいます。したがって、テストプレイはゲーム内容を知らない第三者が行うのが一番といえます。(バグ取り目的ならばなおさら第三者の方がいい)
 とはいえ、ある程度ゲーム制作経験があればテストプレイを第三者に行ってもらわなくてもそれを論理的に考えることもできます。

「プチコン スキー」の難易度がやたら高くなっていると考える理由

(1)自分の絶対的な位置が分からない

 プチコン スキーの場合は辺りが一面真っ白であり、自分の位置は旗との相対的な位置しか分からない。

(2)慣性がある

 旋回ボタンを押している間はどんどん進行方向が変わっている。
 背景のスクロールもないためその進行方向は旗を基準にしなければ分からないし、旗は随時速度が変わっているし、進行方向を把握するためには慣れでカバーするしかない。

(3)旋回による視点移動がある

 左右旋回をしているという感覚を味わってもらうためにこのゲームはプレイヤー視点となっているため純粋な後方視点とは異なり旋回をするたびに視点を変えている。
 そうしないと左右旋回しているというのが視覚的に分からないためだけどそれによって(1)がさらに顕著になった。

 これらを少しでも改善するために用意したのが当初は予定していなかった進行方向のガイドライン表示です。
 難易度バランスもゲームバランスの1つだと思います、難しさをウリにしているゲームでなければ難易度はほどほどになるようにした方がいいでしょう。

 当たり判定に関してですが、疑似3Dとはいえ、奥行きがあるためそれを考慮した当たり判定(旗の間を通過したかどうかの判定)にする必要があります。当たり判定は旗のZ座標が人物のZ座標と等しい、かつ、見た目で旗の間を通過できているように見えるという感じになります。しかし、今回のスキーゲームのような場合では旗のZ座標が人物と等しいというのは、旗が最も手前に来たときなので常時Z軸方向の判定とX軸方向の両方において当たり判定を行う必要はありません。X軸方向の当たり判定(旗との当たり判定)は旗が最も手前に来たときのみに行えば良いからです。
 しかし、拡大比率は速度によって決まっているのですが、それが随時変わっている関係上最も手前に来たときに全く同じZ座標になるとは限らず多少のずれがあります。Z座標のずれがあるということはサイズのずれがあるということであり、そのサイズのずれ(つまり拡大率)を考慮した当たり判定にしなくてはなりません。

 この「プチコン スキー」の当たり判定はZ軸方向に関しては問題ないのですが、X軸方向(つまり見た目の判定)はあまり良いとは言えません。
 拡大率は考慮しているものの1画面のプログラムサイズに抑えるために当たり判定を置く場所が悪いからです。そのためかなり甘めに設定しています。見た目通りの判定は表示を行い次に表示する座標の計算が行われる前に行う必要があります。座標計算後に当たり判定を行うならばその移動量の分だけマイナスした値で判定しなくてはなりません。
 この「プチコン スキー」では表示してプレイヤーキャラのX座標を加算処理した後に当たり判定を行っているために移動量であるM/4の分だけマイナスしないといけないのです。したがって、正しい当たり判定を行うためには21行を次のように変更が必要となります。

 Z=ABS(X+M)>V-6  →  Z=ABS(X+M*0.75)>V-16

 この問題を改善するために「プチコン スキー」では当たり判定を甘めに設定しています。Mの値は最大40であるため移動量(M/4)は最大で10ドットとなっており、その分だけ判定が甘くなっています。
 とはいえ、限界まで左右旋回をしている状態であっても最大で10ドットのずれしかないために身体が過半数通過していれば通過と見なされる程度です。完全にずれているのに通過になるわけではないです。要するに1フレーム遅れで通過できるはずのものが通過したことになるだけなので恐らくプレイしていて「ギリギリ通過できた」と感じるだけで違和感を感じることはあまりないのではないかと思います。

 →第6回へ進む


RETURN

inserted by FC2 system