プチコン講座

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

【プチコン/mkII 両対応】

 前回は疑似3Dゲームとしてスキーゲームを作ってみましたが今回は疑似3Dのレースゲームを作ってみることにします。

 疑似3Dのレースゲームを作る上で恐らく最初のハードルになるのがカーブの表現でしょう。確かに疑似3Dはそれっぽく表示できれば問題ありません。しかし、直線ならば消失点に向かっている2本の直線を描けばそれっぽくなりますが、カーブをそれっぽく描くのは原理を知らなければなかなか難しいです。

 一見難しそうですが、実はそんなに難しくありません。例えば、コンソール画面用(テキストキャラを使ったもの)だとこれで疑似3Dの道路が表現できます。

道路表示サンプル(コンソール)
CLS
 A$="■■■■■■■■■■■■■■■■■■■■" 
'■を20個
 R=0
 B=0
 C=0
 FOR I=0 TO 9
  D$=MID$ (A$,0,20-I*2)
  LOCATE 6+I+A,23-I
  PRINT C$;
  C=C+R/12
  B=B+C
 NEXT
@LOOP
GOTO @LOOP
※最後を無限ループにしているのは「OK」の表示で道路が消えるのを防ぐため
 mkIIはA$="■■■■■■■■■■■■■■■■■■■■" A$="■"*20 とすることができます。

 簡単に説明すると奥に行くにしたがって狭くなる道を用意して、カーブの場合はずらす量を積算することで奥に行くにしたがってずらす量を大きくしているというだけの非常にシンプルなものです。Rの値が0ならば直線だけど-4〜+4の間(0以外)で変化させるとカーブになり数値によって曲がり方が変わってきます。
 R/12の12という値はRが-4〜+4の範囲内で画面内に収まるようにしただけのものです。これは適当な数字に変えても構いません。(なぜ-4〜+4の範囲にしたかというとコースデータを作る際に値にプラス5すれば1〜9になりコースデータを作りやすくなるため)

 ここで「カーブとはいえ消失点が画面中央にないのはおかしい」と思う人も中にはいるかもしれません。確かに普通は単純な一点透視を行う場合に「消失点」といったら画面の中央になることが多いですが、カーブにもそれを適用すると問題が出てくるのです。
 実際にどのような問題が出るのかを図示しました。

《 図1 》
カーブの表示

 カーブの消失点を常に画面中央に表示した場合には進行方向と異なるものになってしまい明らかに間違っているのが分かると思います。それと同時にカーブの消失点は道路に沿って走っている場合には上記サンプルのように単純に左右に積算すれば特に問題がないことも分かります。
 ただし、上記サンプルはキャラクタ単位の表示ということもあり、カーブはガタガタで表示されているのでなめらかさが全くありません。それにさらなる急カーブを表現したい場合には表示が崩れてしまうため画面外へのはみ出し防止処理(MID$を用いてはみ出さないように表示量を減らす)を行う必要があります。したがって、実際はグラフィックを使った方が(画面外への座標もちゃんと受け付けてそれに対応した描画をしてくれるために)簡単かつキレイなカーブを描くことが可能になります。

道路表示サンプル(GLINE)
 VISIBLE 1,,,,,1
 GPAGE 0
 GCLS
 R=4
 A=0
 B=0
 FOR I=1 TO 70
  W=150-I*2
  GLINE 128+A-W/2,192-I,128+W/2+A,192-I,15
  B=B+R/80
  A=A+B
 NEXT

 基本的にコンソール用のサンプルと同じであるため内容を説明するまでもないものですが、Wの値が道幅になっておりGLINEを使ってセンターラインの座標(128+A)を基準に描画しています。センターラインを基準にしておけば道幅の半分の量を左右に足していけば済むために計算が楽になりますし、道幅を変更する場合も簡単にできます。
 アーケードゲームにおいてもかつてはこういった疑似3Dのレースゲームはたくさんありましたが、「ポールポジション」など多くのゲームはラスタースクロール機能を使って実現しています。このラスタースクロールを行えば疑似3D表示は簡単にできるのですが、それをソフトウェアによって実装する必要がある場合にはラスタースクロールに拘る必要はありません。むしろ、ラスタースクロールよりもこういった簡単な疑似3D表示ルーチンの方が簡単で速いくらいです。(プチコンmkIIでラスタースクロールを行う方法はこちらを参照)
 ただし、メインルーチン1回あたり上記のようにGLINEを70回実行しているのはさすがに高速なプチコンとはいえかなり負担が大きいため多少荒くはなるけどGFILLを使ってループ回数を減らした方が良いかも知れません。

 前回書いたような疑似3D表示を行う場合の2つの注意点となった「拡大率の変化」「表示座標」はレースゲームにも当てはまるため別の観点からカーブを描くことができます。
 まず、道を上から見た場合に長方形の集合体と仮定します。イメージしにくければ同じサイズの本や紙を長い廊下や広い部屋に線路のように繋いで敷き詰めていくといいかもしれません。
 その場合は真上から見たら1つ1つの形は長方形だけど床の付近に顔を寄せて低いアングル から斜めで見ると台形の集合体で構成されていることが分かるでしょう。これは長方形にパースがかかることで1つの長方形(1つの本)だけを見てみると長方形の奥の辺の方が手前の辺よりも短くなるからです。
 そして、遠くなるにしたがってその長方形(見た目は台形)がどんどん小さくなります。その縮小率が前回書いたような指数関数的なものになっています。

 これを画面上で表現すれば良いのですが、台形を描画するというのはかなり大きな負担になってしまうために1つ1つの四角形は長方形で補うことにします。

道路表示サンプル(GFILL)
 VISIBLE 1,,,,,1
 GPAGE 0
 GCLS
 R=4
 A=0
 B=6
 W=150
 Y=191
 FOR I=0 TO 49
  GFILL 128+A-W/2,Y,128+A+W/2,Y,15
  A=A+R/1.5
  B=B*0.94
  W=W*0.94
 NEXT

 上記の道の大きさを奥に行くにしたがって「一定比率で縦横ともに縮小したもの」と前述のような「縦はそのままで横のみを狭めたもの」は一見すると両方ともカーブらしさが表現出来ているため大差ないように見えますが、そこには根本的な落とし穴があるのです。

 それを図示したのがこれです。

《 図2 》
疑似3Dの道路表示

 この図2の(A)を見てのように一定比率で縮小したものはあえて言うまでもなくすべての長方形が相似になっています。同じものを遠くからみたら形は同じでサイズのみ変わるというのはすごく当然のことですね。(ただし、実際の道路はカメラに対して正面向きになっているのではないため道路を構成している長方形は距離に比例して同じ形で小さくなるというわけではなくあくまでこの方法は感覚的にイメージしやすく計算しやすいものであり正確な計算をしたい人は下記のコラム参照)
 それに対して図の(B)のように横幅のみを縮小していった場合には疑似3D視点では違和感がないように見えますが、1つ1つの長方形の縦横比が異なっているためこれを仮に上から見た場合には手前にくるにしたがって奥行きの長さが短い長方形になってしまっているのす。つまり、近くのものと遠くのものでは形が変わっています。
 これが意味するものは何かというと前回書いたような単純な拡大をした場合には近づくにつれて遅くなって見えるということです。確かにこれだと1ラインごと(長方形1つごと)の移動を行った場合には実際の移動距離は短くなっているのがこの(B)を上から見た図で考えれば一目瞭然です。

 この(B)の方法で疑似3Dを行うならば一定の速度で近づいているように見せるためには前回書いたように近づくにつれて画面上の移動速度を早める必要があります。つまり、上から見た場合に同じ時間での移動距離が同じになるようにすれば良いのです。これは遠くにいるときには1ライン分の移動だったら近づくにつれて2ライン分、3ライン分の移動量に変えてやればよいということです。しかし、自然な移動量に見せるためには指数関数的に増やす必要があるため画面上でのキャラが増えれば増えるほど処理が大変になってきます。
 一定比率で縮小している(A)の方法で描画する場合には遠くにいても近くにいても1ライン分だけ移動させればちゃんと等速で近づいているように見えます。しかもその障害物のサイズは道路の縮小率を元にすれば違和感のないサイズにすることができるためサイズの計算も簡単です。

 つまり、一見面倒くさそうに感じる(A)の方法ですが、違和感の無い表示を行うためにはかえって楽ができるということです。

 さらに(A)の方法は1つ1つの四角形のサイズ(画面から見て奥行き側のサイズ)がちゃんと 計算されており、それだけで距離情報を持っている(例えば大きさが半分ならば距離が2倍離れている)ので視点移動をすることも可能になります。そして、視点移動を行った場合でも上記のように道路上を動作する障害物の速度やサイズの計算は簡単にできます。

レースゲームにおける道路のカーブおよび視点変更を行うサンプルプログラムの動画
 QRコード(ファイル名:OCHAVIEW)

 あくまで疑似3Dであり奥行き方向への距離情報しかないため画面の左右に視点を変えたら表示の誤差が出てしまう(この疑似3Dの道路表示は厳密なものではなく画面内において一番近いところを基準点としているためカーブが簡単に表現できる反面で左右方向への視点移動をする場合には本来ならば透視図法で言えば2点透視にすべきようなものを1点透視で表現しているためあまり正確なものではない)けど上下移動ならばほとんど問題ありません。
 視点は固定で速度やサイズの違和感が無くすための補正処理をちゃんと行うという人やそこまで気にしないという人ならば(A)と(B)どちらの方法を用いても問題ありませんが、違和感があるのは嫌とか少しでも楽をしたいというのであれば(A)の方法がお勧めです。

コラム レースゲームにおける道路の3D表現

 3D(疑似3Dも含む)のレースゲームを作る場合には道路をいかに表現するかが最初の難関になります。これがまっすぐ1本の直線の道路ならばあらかじめ道路両脇を直線で結んでおけばそれで済む問題(ただし、これでも前回の講座のように拡大率の変え方は考慮しなくてはならない)ですが、カーブを表現する場合にはそうもいきません。
 この講座ではこの難しいカーブの表現をいかにして簡単に行うかを重視したものになっていてその中で違和感が少なく簡単な式で表すことができるものを選択しました。しかし、この方法は疑似3Dなら問題ないのですが、「正確な3D表示」とは言えないのです。

 正しく表現するための方法としてはポリゴンやワイヤーフレームといった3DCGで道路を表現するというものがあります。これはプチコン用ポリゴン表示プログラムを見ての通り非常に重い処理となっていてプチコンで行うにはかなり無理があるでしょう。頂点数を減らしたかなり簡略化されたコースをワイヤーフレームによって表現すれば何とか実用に耐えうる速度(15fps程度)を実現可能ですが厳しいのは確かです。
 ポリゴンやワイヤーフレームがなぜ重いかというとどの角度から見ても(計算的上)正しい表現ができるようにするための回転処理(ジオメトリ処理)が重いのが1つの理由です。それに加えてポリゴンでは面の塗りつぶし処理も自前で用意しなくてはならず、それが重くしている理由にもなっています。ワイヤーフレームはポリゴンと比べると描画処理は桁違いに軽くなるのですが、手前の面で隠れている線を非表示にする処理(カリング)を加えると法線ベクトルの演算が必要になってくるため非常に重くなります。

 では、上記の図2の(A)のように道路は細長い長方形によって構成されていると考えたらどうでしょうか。これを斜め後ろから見下ろすだけならばを正しく表示するのは簡単そうに思えるかもしれません。
 実はカメラの向きが道路(地面)に対して平行なら下記のようなごく簡単な一次式で表すことが可能なのです。

  SX=128+X/Z*10
  SY=96-Y/Z*10

 この式の具体的な意味はこちらに書いているのですが、簡単に説明すると変数Zがカメラとの距離であり距離に比例して小さく見えるというパースの大原則を数式化しただけのものです。スプライトを使った疑似3Dゲームを作る場合にはSPHOMEで設定したスプライトの中心座標をこの式によって求めることができます(SPSCALEの値はZの値に反比例する)。また、立体の各頂点に適用してこの式によって得られた座標を線で結べばワイヤーフレームも可能です。レースゲームに使用する場合には後方視点の場合は自車のZ座標を0に設定した方が判定がしやすいですが、その場合にカメラと自車の距離が20ならばX/ZX/(Z+20)とすれば良いです。
 ただし、カメラが地面に対して平行というのは一点透視の状態であり、消失点(直線道路を表示して道路両脇の線を延長した線が交差する座標)がアイレベル(カメラのある視点の高さ)になります。言うまでもなく車は空中ではなく道路の上を走っているのですが、透視図の性質上消失点から離れれば離れるほど誤差が出てくるためこのようにカメラを地面と平行にした状態ではアイレベルが高くなればなるほど大きな誤差が出てきます。(誤差を少なくするためにはカメラのレンズを標準〜望遠にする必要があるけど道路の遠くを良く見せるためにアイレベルを高くしてなおかつ自車を画面内に入れようとするとどうしても広角気味になってしまう)
 
 消失点からの視角のずれによる表示誤差を減らすには自車を消失点(この場合の正しい表現でいえば「視心」となり、一般的な3DCGや透視図だとこの視心に近いほど正確に表現できるの上に置けばいいです。簡単に言えば、自車に向けて見下ろすようにカメラの向ける角度を変えてしまえばいいだけです。そのようにして、出来たのがこの「2D→3D レース」(プレイ動画はこちら)です。このゲームは道路が長方形で構成されていてその長方形を見下ろした場合もGFILLで簡単に描画できるように長方形にしているという点を除いては非常に正確な計算を高速に行っています。
 これは簡単に言えば「縦回転」となっていてX軸回転レースゲームと言えます。ちゃんと正確に計算を行っているため0度〜90度までどのようなカメラアングルであっても正しく表示することが可能になっています。(一見正確な表示ができそうなポリゴンやワイヤーフレームであっても上記の視心からのずれによる誤差などが原因でカメラの位置やレンズの設定によっては表示に不自然さが出てしまうため決して万能なものではないことに注意して欲しい)

 ただし、このゲームをプレイするかプレイ動画を見たら分かるように単純な縦スクロールのレースゲームを3Dで表示しているだけなのです。それはこのゲームはジグザグコースを表現することはできるのですが、カーブを表現することはできないためです。
 このゲームでカーブを表現するためにはX軸回転だけではなくZ軸回転(真上から見た場合に普通に時計回りや反時計回りに回転しているように見えるもの)を加えた2軸回転が必要になってきます。2軸回転はスーファミの「F-ZERO」や「マリオカート」などではごく普通に使われていますが、これはスーファミがBG画面の2軸回転をハードウェアでサポートしており非常に高速に行えるためです。プチコンではそういう機能がないためプログラムで実現する必要があります。

 プチコンで2軸回転を行うプログラムはこちらに用意しました。これを使って簡単なレースゲームを作ってみましたがこのサンプル動画のようにプチコンで実用レベルの速度(15fps程度)を得るためにはどうしても解像度が低くなってしまいそれが原因でスピード感も感じさせなくなってしまいます。
 もしも、プチコンで2軸回転を使ったレースゲームを作るならば回転はZ軸回転のみにしてカメラの向きは地面に水平に固定としてX軸をリアルタイムで回転させるのをやめないと厳しいでしょう。カメラアングルを変える表現をしたいとか画角が極端に広角にならない限りはこれで問題はないと思います。(カメラの向きが地面に水平で固定ならば速度面で有利になるためそれで2軸回転風のレースゲームを上手く作っている人もいる)

 つまり、正確に計算を行えば行うほど誤魔化しが効かなくなるためにカーブの表現が難しくなるということです。レースゲームにおいてカーブの存在は見た目の変化だけではなくカーブの曲率によって速度を調整するという戦略的要素も含んでいるため非常に重要だと思われます。(タイムアタック重視のゲームだと速度調整の戦略性がないとゲームとして全く楽しめなくなる)
 したがって、今回の講座のように「カーブの表示をいかに簡単に行うか」というところから出発して違和感が少しでも無くなるような簡単な方法(1画面に収まるようなレベル)で作るというのも正しいアプローチの1つだと思います。そして、正確さよりもそれっぽさを重視できるのが疑似3Dのメリットだと思います。

 今回のレースゲームも前回のスキーゲームと同じく違和感をより少なくするために一定比率で縮小する(A)の方を選択します。
 そして、出来たのがこのゲーム「PETIT RUN」です。(これを作って10ヶ月後にmkII専用にさらに改良した「PETIT RUN mkII」も作りましたが基本的な処理はほとんど変わってないので今回の講座も十分に参考になるでしょう)



 このゲームでは車ではなく人間がレースをします。というのもプリセットのキャラデータの中に車のデータが無かったからです。1画面というプログラムサイズの関係上そのキャラを定義するのは無理ですし、スキーゲームの旗のようには車はGLINEで簡単に描画できないため消去法的に人間キャラになりました。もっとも、人間キャラのレースゲームといえばPS用ソフトの「ランニングハイ」とか実際にあるわけなので人間キャラのレースゲームというのも別に問題はないでしょう。
 レースゲームで重要になるのは「道路の描画」「道路上や背景のオブジェクトの描画」「自車の挙動」「当たり判定」でしょう。

 道路の描画に関しての基本的なことはすでに書いていますが、ここではコースデータを元にどのように表示に反映させるかについて書いていきます。
 このゲームではコースデータは「1」〜「9」で構成されています。これは「5」が直線となっており、それよりも小さいものが左カーブ、大きいものが右カーブとなっているため「1」が左急カーブ「9」が右急カーブとなっています。ここではその数字から5を引いたものを曲率として示すことにします。(つまり、データ5である直線は「曲率0」、データ1である左急カーブは「曲率ー4」となる)

 例えばコースデータにおいて曲率-2と曲率+2のカーブが連続する場合は本来ならば画面上にはS字カーブが描かれるはずですがこのゲームではリストを少しでも短縮するため1歩先のデータ分しか表示していません。つまり、曲率-2のカーブのみが描かれるということです。それでは問題がありそうですが、このゲームのコースデータは1つ当たり40ライン分の距離となっており、画面上には21ライン分しか表示されていないため問題はないのです。
 それでも、データとデータの分かれ目部分では「曲率-2」のカーブと「曲率+2」のカーブが描かれないと不自然になると考えるかもしれません。それをうまく繋ぎ合わせるためこのゲームでは曲率をなめらかに変化させています。「曲率-2」のカーブと「曲率+2」のカーブのちょうどつなぎ目においては瞬間的には曲率0となっているためそのように表示するようにしているのです。
 具体的に書くと曲率-2の状態から(コースデータの1/4の距離に相当する)10ライン進むごとに「曲率-2 → 曲率ー1 → 曲率0 → 曲率+1 → 曲率+2」と変化するようになっています。左右交互になるカーブや曲率が変わっているカーブがつなぎ合わさった状態でも表示に不自然さが無くなるようになっています。(この処理は8行〜10行で行われているので見れば分かりますが、実際は10ラインごとではなく自キャラが移動するごとに随時調整を行っている)

 ちゃんとS字カーブを表示したいというのならば現状では各ラインで統一している(つまり、1回の演算で済んでいる)曲率計算を1ラインごとに計算する(つまり曲率計算は現状の21倍の時間がかかってしまう)必要があり、さらに現在ではコースデータ1つ先まで読み出しているデータを2つ先まで読み出す必要があります。今は見た目で決めているカーブでずらす量もきちんと計算して違和感のない量に設定しなくてはならなくなってしまいます。
 そのため処理そのものが重くなり、ループ回数を減らさない限り(画面表示を粗くしない限り)60fpsを維持するのが難しくなります。もちろんプログラムリストを1画面サイズに収めることも無理になります。(なめらかなS字カーブではなく直線的なZ字カーブならば計算は簡易化できるけど今度は下記のような表示や自キャラの挙動の不自然さが出てしまう)
 「不自然さをなるべく無くす」「処理をシンプルにして(リストを短くして)、なおかつ、分かりやすくする」「処理速度向上を図る」ということが可能になるためこのような方法をとっているわけです。


《 このゲームにおいてZ字カーブが不自然な理由 》

 Z字カーブは直線で構成されたカーブとなっています。つまり、常に曲線で構成されているS字カーブとは異なり、コースデータのつなぎ目部分以外は直線と同じ扱いになるわけです。その結果下記の2つの問題が出てきます。

 (1) プレイヤーの視点
 (2) 遠心力


 (1)のプレイヤーの視点についてですが、このPETIT RUNでは常に道路において平行となっています。カーブにおいては接線の方向に視点があるということです。これは言い換えると進行方向に視点があるともいえます。そうすることで回転処理をしなくても済むため処理が単純化でき高速化かつリスト短縮が可能になっています。
 これがカーブが曲線ではなく直線となるZ字の場合だと例えば曲率ー2→曲率-2のように同じ曲率(同じコースデータ)が続いているという時には単なる直線(画面中央を消失点とした道路)として表示されることになってしまいます。また、そのように表示をしたら曲率ー2→曲率ー1にコースが変わる場合には本来ならば曲率ー1は左カーブですが表示上は右カーブになってしまうのです。(曲率ー2が直線扱いであるため)
 したがって、現状の「常に道路に平行な視点」では不自然さが出てしまい、視点は道路と無関係に常に特定方向に統一することになります。これは昔ながらの縦スクロールのレースゲームと同じと考えれば分かりやすいでしょう。

 (2)の遠心力は後述のように自キャラの挙動において大きな役割を担っています。速度を上げれば上げるほどカーブが曲がりにくくなるため遠心力があることで減速を活用する必要性が出てくるからです。
 Z字カーブは自キャラの挙動は直線と同じになるため遠心力は働きません。したがって、遠心力のパラメータは導入できません。
 実際はカーブかどうかではなくハンドルを切った時点で円運動となるために遠心力が働くので直線だけで構成されているから遠心力のパラメータが導入できないということはないのですが、仮にZ字カーブでハンドルを切ったときに遠心力が働くようにした場合には視点と進行方向が異なっているためこのPETIT RUNの表示アルゴリズムで自キャラを円運動させるような挙動を行えば不自然さが単に拡大されるだけとなってしまいます。
 その点、このゲームでは常に進行方向(視点)は接線方向となっており、カーブの際は基本的にカーブにそってハンドルを切るため遠心力が自然に表現されています。(つまり、急カーブになるほど急ハンドルを切っているのだけどこれが処理の単純化にも繋がっている)

 つまり、このゲームにおいてS字ではなくZ字カーブを導入すると表示や自キャラの挙動が不自然になってしまうというわけです。したがって、根本的な面からゲームシステムを変える必要性が出てきます。もしも、Z字カーブを導入したらどうなるかは上記コラムで紹介した「2D→3D レース」をプレイしてみたら分かると思います。(このゲームは縦スクロールゲームだけどリアルタイムで縦方向に画面回転させることで疑似3D表示をしているだけなので)


 この「PETIT RUN」はタイムアタック専用のゲームであるため敵車は登場しません。そして、1画面というプログラムサイズであるため背景や道路上のオブジェクトもありません。唯一動きのあるものは道路上にある白線だけです。その代わり60fpsで動作するためタイムアタックには最適です。いくら見た目が良くても10fpsとか15fpsではタイムアタックには難があります。(ポケコン用に作ったオールBASICの疑似3Dレースゲーム「3D DRIVING」は8fpsでしたが)
 そのため白線の動きについて書くことにしましょう。
 白線は一定間隔ずつ表示されているのですが、これはトータルの移動距離を示すHを一定倍数し剰余を演算しているだけです。15行目のC=13+((I+H*40)%8>=6)*2がその部分です。
 これは、道路を描いている四角形(ライン)のうち8ライン中6ラインが道路の地の色を示すというものになっています。(差し引き8-6=2で白線は2ライン分の太さとなる)
 この「8」「6」の数字を変えることで白線の間隔や太さを変えることが可能になります。

 一般的にゲームのスピード感を増加させるためには道路上や道路脇のオブジェクトの移動速度を上げれば良いのですがこれはこの「PETIT RUN」においては白線の移動速度に相当します。
 このゲームでは最高速度(Sの値)は約31.8となっており、移動距離Hを40倍している(言い変えるとコースの基本単位の1/40移動すれば1ライン分移動となる)ために最高では1フレームあたり31.8×40=1272増加することになります。1200でHの値は1つ増えるように設定されてあるために最高速度まで上げても1フレームあたりでは1ライン程度の移動量です。
 この移動量を増やせば(例えばH*40の40という数字を80に増やしたりすれば)さらにスピード感は増すと考えがちですが、その上げ方には注意しておく必要があります。実は移動量が増えても速くなったと必ずしも感じるわけではないからです。

 白線が一定間隔で出ているためその間隔と同じ速度(このゲームでは1フレームあたり8ライン)で移動させた場合は元の位置と同じ位置に白線が表示されることになります。そのため白線が止まっているように見えてしまいます。
 では、それよりも遅ければ良いのかというとそうでもありません。
 白線が8ラインおきにある場合には1フレーム当たり白線を7ラインの移動移動した場合には逆に1フレーム当たり1ライン後退しているように見えてしまいます。それならば8ラインの半分の速度である4ラインくらいならば十分高速に進んでいるように見えると思うかもしれないですが、今まで無かった白線と白線の真ん中に新たに白線が表示されているように見えてしまうためスピード感は感じられません。(60fpsより遅い場合は本来の白線とその間の白線が点滅して見えてしまう)
 つまり、スピード感を上げるために白線の移動量を増やすことは効果的ですがその量は白線間隔の半分を大きく下回るように必要があるというわけです。(移動量を大きくすればするほどスムーズさを失う)

 これは白線だけに止まらずゲーム内に現れるオブジェクトすべてに言えることです。
 言葉で説明されてもよく分からないという場合は実際にH*40の数字をH*200とかH*300にして 実際に自分の目で確かめるのが一番でしょう。

 次に自車の挙動について書くことにします。
 レースゲームにおいて基本となるのがアクセルとブレーキでしょう。これはレースゲームを作った経験が無い人だと悩む場面ですが私の場合は過去に自分が作ったポケコンゲームで使用したものを元にしたシンプルなものを採用しています。
それが、12行目のS=(S+(16AND K)/57)*0.99という式です。
アクセルボタンを押したらアクセル、離したらブレーキという場合にはその2通りの条件分岐があるし、アクセルを押した場合には上限速度を超えないようにしないといけないわけだしブレーキを使用した場合には速度がゼロ未満にならないようにしないといけないために論理式を使っても下記のように長くなってしまいます。(コード短縮を駆使すればもう少し短くできるけど)

 S=S+((K AND 16)==16)*(S<30)/2-((K AND 16)==0)*(S>0)/2
 ※最高速度30でアクセルを押したら0.5速くなり、ブレーキをかけたら0.5遅くなる場合

 私が考案した式はこれと比べたら短くて速いというメリットがあるし、最初の加速が良く最高速に達するには時間がかかる(速度0の状態からアクセルボタンを押した時間と速度は比例しない)という点も個人的には好みです。(0と特定の最高速度に収束する式であるため上限、下限に達した場合の判定が不要になっている)

 あと、操作キャラが人間であることを考慮して左右のハンドルを切っていないまっすぐに進んだ状態では最高速と加速がアップするというターボ的な要素も加えました。これによって戦略性が増しています。(ハンドルを切っている時に速度や加速度が落ちるのは当たり前のことなので市販の疑似3Dレースゲームの多くがそうなるような処理を行っているのでPETIT RUNが特別変わったことをしているわけでもない)
 デフォのコースではこれを生かしたターボスタートによって通常走行ラインよりも最大で0.08秒タイムを縮めることが可能になっています。(19秒8台になると0.01秒を縮めるのが難しくなってくるためこの0.08秒は非常に大きい)
 何しろこのゲームはシンプルであり急カーブがたくさん出てくるコースでない限りは慣れるとタイムに差が出にくくなってしまいがちです。アクセル全開で走れるようなコースではタイム差が全く無くなってしまいます。
 しかし、この要素を取り入れることでハンドルを切るたびにタイム差が発生するため0.01秒単位のタイムアタックが熱くなるのです。いかにアクセルを緩めず、かつハンドルを切っている時間を短くするかがタイム短縮のポイントになります。

 それと重要になるのはカーブの時の挙動でしょう。
 シミュレータではないので厳密な計算は不要ですが、急カーブが簡単に曲がれすぎても難しすぎても面白くないので疑似3Dではいかにそのバランスをとるかが重要になってきます。そのバランスを取る上で考慮すべきことは「カーブを曲がる際には遠心力が働く」ということくらいです。
 あえて説明するまでもないことですが、遠心力は速度の2乗に比例し、カーブの半径に反比例します。もちろん速度の2乗に拘る必要もないため自分のゲームに合ったもの(自分がベストと考えるバランス)で構いません。違和感なく快適にプレイできなおかつ戦略性のあるものであればいいのですが、それは排他的要素もありすべてを満たすことは無理です。したがって、どのようなゲームにしたいのかというのを頭に入れておく必要があります。
 タイムアタック重視のレースゲームといえばアクセルワークとコーナーのライン取りが重要だと私は考えているためPETIT RUNではそれを重視してバランス調整をしています。これが13行目のX=X+R*S*S/340というものです。この式はカーブはどのくらいの速度で曲がれるのがベストかということをテストプレイをしながら逆算して求めたものです。(完璧なライン取りができたときにはデフォのコースは最終コーナー以外はアクセル全開で曲がることができ、こうすることで爽快感が出るようにしている)

 コースアウト時の自車の挙動も重要です。
 このゲームでは常にコースに対して平行な方向を向いているので問題は速度だけです。コースアウトになったら一気に速度を0にするという方法もありますが、それだと難易度が高くなるだけではなく爽快感も低くなりがちです。したがって、コースアウト時には一気に0にするのではなく少し減速するというのがベターでしょう。ただし、この「少し」というのもバランスを考える必要があります。
 このゲームではS=S-S*M/20という式によって減速処理を行っています。変数Mがミス(コースアウト)をしたかどうかのフラグになるためコースアウト時には1フレームあたり速度が1-1/20倍、つまり19/20倍になるようになっています。こうすることで速度が負数になることがないためその判定が不要になりリスト短縮にも繋がっています。(急カーブですでに速度を落としている状態の時にはこれではコースアウト時のリスクが小さすぎるし、高速時にはリスクがやや大きめと感じたためこれを改良した
PETIT RUN mkIIではS/20だった減速する量をS*3/(S+4)に変更してさらに爽快感と攻略性を増している)
 コースアウト時にどの程度減速するかはリスクとリターンのバランスで考える必要があります。速度を上げることによってコースアウトのリスクが高まるけど速度を上げれば好タイムが出やすくなります。コースアウト時のリスクを小さくすれば「(速度が落ちにくいため)好タイムが出やすくなる」というリターンの方が大きくなるためリスクとリターンのバランスが変わります。その反面でコースアウトしないように急カーブに入る前に減速するよりもアクセル全開でコースアウトしながら曲がった方が好タイムが出るということにもなる可能性もあります。この辺はそのゲームのゲーム性や攻略性に関わる部分であり、どちらがベターとも言えませんがPETIT RUNではベストなライン取りを見つけるということが攻略するための基本と考えているためコースアウト時のリスクは小さめにしつつコースアウトしながら曲がるよりもコースアウトをしないで曲がった方が好タイムがでるようなバランスにしています。それが先ほどの式になります。

 あと難しいのは当たり判定です。といってもこのゲームだとコース上にオブジェクトがないために行うものはコースアウト判定だけでありそれほど難しくはありません。
 しかし、純粋な2Dゲームではないため見た目通りに判定してしまう(足元が1ドットでもコース外にかかっているとコースアウト扱いになる)場合には快適にプレイできるとは言い難いということに加えてリスト短縮のためにこのゲームでは多少コースアウト判定が甘くなっている部分があります。全般的にコースアウト判定はインに厳しくアウトに優しくなっています。
 そのため数ドットはみ出ることを覚悟で曲がれるため超スピードでぎりぎり曲がりきるという感覚を味わいやすくなっていると思います。

 一応、正しい当たり判定も書いておくことにしましょう。
 このゲームの道路の横幅は200ドットで人物キャラのスプライトサイズは32x32ドットですが、見た目の足元のサイズは横幅16ドットになっています。中央を基準に考えると道幅は左右100ドットでキャラは左右8ドットであるためX座標の許容量はコースの中心座標を0とした場合に-92≦X≦92となります。
 カーブの際はこれにR+Rが加算されるため19行の当たり判定を正しく行う場合は次のように変更が必要になります。

 M=ABS (X+R)>95 → M=ABS (X+R+R)>92

 この変更によって道路の左右ともに1ドットでもはみ出たらコースアウト扱いになります。
 変更前だと直線時で左右3ドット、急カーブ時にはアウト方向に7ドットの余裕があります。このため急カーブが少しだけ簡単に曲がれるようになっています。
 第4回の講座で書いたピョン子において1ドットの足場に簡単に立てるのと同じように一見難しそうなことが簡単にできるということがゲームの爽快感に繋がると私は考えているためPETIT RUNではあえてこのように当たり判定を正しく行っていません。
 とはいえ、あまり当たり判定に余裕があると「うまくギリギリ曲がることができた」と感じられなくなるので難しいところです。


 さて、せっかく視点変更のアルゴリズムを考えたのにそれを「PETIT RUN」では使用していません。それは「1画面」というプログラムサイズに収めるのが難しいというのもありますが、このレベルのゲームで後方視点とドライバー視点との視点切り替えの必要性をあまり感じてなかったからです。とはいえ単純に左右に動かした場合に消失点が動いてしまうというのも問題だし、せっかくシンプルに実現できるアルゴリズムを考えたので視点移動対応バージョンとなる「PETIT RUN D」も作ってしまいました。(PETIT RUN mkIIでは左右移動の際に発生する視点移動のみサポートしていてPETIT RUNのように左右にハンドルを切って単純に道路を左右にスクロールさせるのではなく消失点を固定で道路の表示そのものを変えている)



 表示は幾分簡素化しているものの「60fps」という動作速度と「1画面」というプログラムサイズは維持したままリアルタイム視点切り替えが可能になっています。
 後方視点とドライバー視点との視点切り替えは1画面プログラムでは画期的ですが、ドライバー視点は情報量が少ないのとコースデータを表示に反映させるアルゴリズムがドライバー視点向きではないためにこの視点切り替え対応バージョンとなる「PETIT RUN D」よりも標準の「PETIT RUN」の方が遊びやすいと思います。

 後方視点とドライバー視点の変更アルゴリズムを書いておくとドライバー視点は道路を近くで見ているということで200ピクセルだった道路の横幅を712ピクセルにして(中途半端な数字なのは道幅の半分が256+100ピクセルにしたため)、縮小率を高くすることで遠近感を強調しています。それを行うと水平線の位置も変化してしまうのですが、値を調整することで視点変更をしても変わらないようにしているというだけです。(水平線の位置が変わらずに道路の幅が広がったということはアイレベルが低くなったことを意味する)
 ただの表示サイズの変更となっているため前述のように視点変更をしても道路上のオブジェクトのサイズや移動速度や当たり判定を変えたりする必要はありません。これが(B)の方法で強引に視点変更を行えば移動量計算や当たり判定の式を別途用意しなくてはなりません。

ちなみにPETIT RUNを図2の(B)の方法を用いて道路を標示した場合の変更点を書いておきます。
 11行目 E=7E=3 に変更
 14行目 FORの前に D=0 を追加
 14行目 V=V+R+RD=D+R/5:V=V+D に変更
 18行目 E=E*P:F=F*PF=F-4.5 に変更

 これを実行すれば(B)の方法を補正無しで使用するというのがどのくらいの違和感があるかということが分かると思います。違和感があるかないかというのは違和感の少ないゲームだとなかなか判別が付かないため違和感があるものを実際に見ておくのも重要と言うことでこのような変更点を用意しました。

 前回のスキーゲーム、今回のレースゲームと2回に分けて疑似3Dゲームについて書いてきましたが疑似3Dゲームで重要なのは「表示に違和感がないこと」と「操作に違和感がないこと」です。3Dポリゴンやワイヤーフレームではなく所詮「疑似3D」であるため厳密に計算するということにそれほど大きな意味はないですが、プレイをしていて違和感を感じるようなレベルだと良いとはいえません
 ただ、その違和感には大抵は理由があり、それらは前回、今回書いたようなものばかりです。
 多少違和感を感じてもそれを吹き飛ばすような面白さがあれば問題ないのですが、プレイヤーに違和感を感じさせることによって面白さを損ねているという場合があるためそういう場合は上記のように違和感を無くすことが必要になってきます。
 違和感を無くすゲームを作ることは「プチコン スキー」「PETIT RUN」を見てのように1画面のプログラムサイズでも可能であるということは分かってもらえたのではないでしょうか。

 →第7回へ進む


RETURN

inserted by FC2 system