プチコン3号用 GRP 2軸回転プログラム(プチコンBIG対応)

初出公開日 2015年2月5日
【New3DS推奨】【BIG推奨】 プチコンBIGでも特に問題なく動作します

 これはプチコン3号のGRP画像をドット単位で読み取りそれを2軸回転することで疑似3D表示を行うプログラムです。2軸回転というのが何かというと分からない人もいると思うので簡単に書くとスーパーファミコンの持っている機能(F-ZERO、マリオカートやFFの飛空挺などで使われているもの)をプチコン3号のプログラムで実現したものです。

 このプログラムはプチコンmkIIで作った「プチコン用GRP2軸回転プログラム」をプチコン3号用にベタ移植しただけです。最適化していないmkII用プログラムをプチコン3号用にベタ移植(表示面の細かい違いはあるけど擬似3D変換部分のアルゴリズムは全く同一)しただけであり、処理速度の高速化の余地はたくさんあるので速度面に不満を感じる人は自分が使い方に合わせて最適化をしても良いでしょう。また、DEFを使って自作関数化するのも良いでしょう。

 ちなみにデフォの設定だと(ver.3.2.1のNew3DSで動作させた場合)18〜19fps程度(プチコンBIGならば64fps程度)ですが、最適化を図れば全く同じ機能のまま30fps以上(プチコンBIGならば100fps程度)で表示が可能です。(同一条件下で比較すればプチコンmkII版と比べて30倍近く高速でBIGならば100倍くらい高速!)
 現時点では(このプログラムを発表した時点では)プチコン3号で自由に回転できる2軸回転プログラムはこのプログラムだけです。カメラアングル固定の2軸回転風プログラムならばたくさん発表されていて、それを活用したレースゲーム(例えば、れいさんがつくった「F-ねこ」)もあります。(※F-ねこはインターレース表示によってこのプログラムより高精細な表示で30fpsを実現!)

 なお、高度サウンドユニットに収録されているARYOP命令を使えばこのプログラムと比べて格段に高速化が可能だと思います。(このプログラムと同じくカメラアングルを自在に変えられるものはARYOPでは困難ですが、カメラアングル固定であれば四則演算のみで済むためARYOPを使えば桁違いの高速化ができるかも)

《 実行画面スクリーンショット 》


《 実行中の動画 》

※この動画はver.3.1.0で使用した時のものでver.3.2.1ならばそれより少し高速に動きます。(ただし、これより高精細な描画をすれば遅くなる)

 《 メニュー 》





プログラムリスト、公開キー



《 プログラムリスト 》
ACLS:XSCREEN 2
KX=4:PX=100:PY=50
CL=5:CA=80:RS=0.5
X=256:Y=256:PA=0

SPSET 0,2416
SPHOME 0,8,16
SPSET 0,3232
SPHOME 0,8,8
SPCOLOR 1,RGB(200,0,0,0)
LOAD "GRP3:SYS/DEFSP.GRP",0
BGMPLAY 30

WHILE 1
 TOUCH OUT TM,TX,TY
 B=BUTTON()
 IF B AND 4 THEN PA=(PA+355)MOD 360
 IF B AND 8 THEN PA=(PA+5)MOD 360
 IF B AND 16 THEN SP=SP+(SP<20)
 IF B AND 32 THEN SP=SP-(SP>-20)
 IF B AND 64 THEN SP=0
 IF TM THEN CA=0OR 91-ROUND(TY/4):RS=POW(0.99,TX):CL=RS*10
 C=2-C
 GOSUB @GRP2ROT
 X=SN*SP/8:Y=-CS*SP/8
 INC FPS:IF MAINCNT-CNT>59THEN LOCATE 0,0?"FPS"FPS,:FPS=0CNT=MAINCNT
 SPSCALE 0,KX*PX/2,240-SC*16
 SPOFS 1,KX*PX/2,240
WEND

@GRP2ROT
SN=SIN(RAD(PA))
CS=COS(RAD(PA))
SI=SIN(RAD(CA))
CO=COS(RAD(CA))
TA=TAN(RAD(90-CA))
MY=239
FOR I=1 TO PY
 DW=CL/I/RS-TA
 DX=CL/(DW+!DW)
 DY=DX*SQR(1+TA*TA)
 IF DY<0 OR DY>999THEN DY=999
 XR=(CL*SI+DX*TA)/CL*RS
 IF XR>999 THEN XR=999
 AX=X+SN*DY:AY=Y-CS*DY
 BX=CS*XR:BY=SN*XR
 CX=AX-BX*PX/2:CY=AY-BY*PX/2
 FOR MX=0 TO (PX-1)*KX STEP KX
  GPAGE 0,3:G=GSPOIT(CX,CY)
  GPAGE 0,C,2-C
  GFILL MX,MY,MX+KX,MY-KX,G*(G<0)
  CX=CX+BX:CY=CY+BY
 NEXT
 MY=MY-KX
NEXT
RETURN

公開キー【 QDADA3JD 】、ファイル名「GRP2ROT

 このプログラムリストは2軸回転ルーチンだけではなく実際にそれを使うためのサンプルプログラムも含んでいます。
 「GRP2軸回転プログラム」本体はサブルーチン@GRP2ROTであり、それ以外の部分はそれを使うためのサンプルプログラムです。


使用方法



 このサンプルプログラムの使用方法について書いていきます。
 実行するには適当なGRP画像が必要になります。このプログラムではプチコン3号のSYSフォルダの中にあらかじめ用意されているDEFSP.GRP(デフォルトのスプライトキャラ用のGRP)を読み込んで使用しています。このGRPではなく自分で用意したものを使いたいという人はプログラムリスト10行目を使いたいGRPのファイル名に変更してください。
 このプログラムではGRP3にあるデータを読み取りそれを2軸回転しています。GRP2はダブルバッファリング用として使用していてこれによって60fpsを大きく下回る速度であっても画面のちらつきが発生しないようになっています。

 十字ボタンの左右で左右回転を行い[A]ボタンで前進、[B]ボタンがバックです。押し続ければ一定速度まで加速します。[X]ボタンを押せばその場に停止(つまり速度0になる)できます。
 タッチパネルでは拡大、縮小やカメラアングルの変更ができます。タッチのX座標が拡大率、Y座標がカメラアングルになります。画面上端をタッチすればカメラアングル90度となり真上から見た状態となります。

  《 主な使用変数 》
AX、AY
スキャンラインの基準座標
BX、BY
スキャン座標のX、Y方向の増分量
CA
カメラアングル(X軸回転)
CL
(プレイヤーからの)カメラ距離
CX、CY
元画像におけるスキャン座標
DY
(プレイヤーを基準にした)画面上のY座標に対応する地面までの距離
KX
ドット拡大率
PA
プレイヤーの向き(Z軸回転)
PX、PY
縦横の表示ドット数
RS
スキャンの基準量
X、Y
元画像におけるプレイヤーのX、Y座標

 リスト中のサブルーチン@GRP2ROTがGRP2軸回転ルーチンとなっています。自分のプログラムでこのルーチンを使いたい人はこの部分だけを組み込むと良いでしょう。(このルーチンは自由に使用してもらって構いません)

 変数KXの値を変えることで画面に表示している1ドットの大きさが変わります。これを小さくすればより多くのドットが表示可能になります。
 変数PXPYの値を変えることで画面に表示している縦横のドット数を変えることができます。プチコン3号の上画面は横400ピクセルの表示が可能なので1つのドットの大きさが4ピクセル(KX=4)ならば横に100ドット分表示できます。  概ね画面表示のドット数に比例して遅くなっていく(縦横2倍のドット数ならば4倍くらい遅くなる)ためむやみに増やすと速度が極端に遅くなるため注意が必要です。あと表示ドット数を増やすと見える範囲が広がるため画角が広がります。
 変数CAのカメラアングルはカメラを地面に水平に向けた状態を0度としています。0度だとカメラの地面からの高さがゼロになるため表示できません。変数PAのプレイヤーの方向は時計の12時の方向を0度としています。
 変数RSは真上から見下ろした時の画面上の1ドットが元画像の何ドット分に相当するのかを示したものです。デフォではRS=0.5となっていますが、元画像の1ドットが画面上(擬似3Dで一番近くに見えるドット)ではその逆数の2ドット分になります。デフォでは1つあたりのドットは4倍拡大された状態になっているため8ピクセルのサイズで表示されています。サンプルではデフォルトのスプライトキャラのGRPを表示しているわけですが、横16ピクセルのスプライトキャラは画面の一番手前(一番下に表示)のもの(もしくは画面上端をタッチしてカメラアングル90度となる真上から見た場合)は横128ピクセルに拡大表示されているというわけです。
 画角は表示しているドット数を増やせば広くなり、減らせば狭くなります。また、拡大率の逆数となる変数RSの値を大きくすれば画角は広くなり、小さくすれば狭くなります。このプログラムでは初心者でも扱えるように画角をパラメータとして設定できるようにはしていません。画角○○度という設定をするよりも元画像1ドットが画面上では何ドットで表示されるかという設定ができる方が扱いやすいと判断しました。これによってスプライトやGRPやBGと組み合わせて表示する際にそのサイズを三角関数を使って計算せずに済むようになります。
 カメラからの距離も自動的に調整されるため難しいことを考える必要は全く無くて初心者でも簡単に扱えるというメリットがあります。カメラからの距離を変えたいという場合は初期設定の変数CLの値を変えれば良いですが、変数RSの値もそれと連動して変わるため注意が必要です。(カメラからの距離を遠くにすれば背景は小さく表示されることになるけどこのサンプルプログラムのようにプレイヤーキャラの表示サイズを2軸回転ルーチン外にてカメラの距離と連動して変わるようにしなくてはならなくなるのでRSの値を調整する方が扱いやすいと思う)


プログラムの仕組み



 この「GRP2軸回転プログラム」ではZ軸回転とX軸回転を行っています。
 Z軸回転というのは「スプライトの回転」などで良く使われているもので画面を真上から見た状態の画像を回転します。
 回転は行列を使えば簡単に計算することが可能です。一般的なxy平面において座標 (x,y) の点を原点を中心にθラジアンほど反時計回りに回転させた場合の座標 (x',y') は次のようになります。

 x'=x cos θ - y sin θ
 y'=x sin θ + y cos θ


 しかし、このやり方では回転後に画像のドット数が変わるため隙間が出来てしまうという問題に加えて処理が非常に重いという問題があります。例えば256×256ドットの画像を回転させるためには65536ドット分の行列演算をする必要があります。これは、ポリゴン表示やワイヤーフレーム表示プログラムで単純に考えると約22000頂点の3軸回転(22000x3=66000≒65536)を行うジオメトリ演算に匹敵するすさまじいほどの処理量になり高速なプチコン3号であっても1fpsを下回るくらいの遅さになります。。
 そこで表示後の座標を元に斜め方向に元画像をスキャンしていく方法を採っています。

 《 図1 》


 この方法はポピュラーな方法ですが、回転前と回転後のドット数が変わっても画像に隙間ができることがなく、また、増分量は一定でありメインループは加減算だけで済むため計算が極めて単純化でき高速化ができます。


 では、2軸回転のもう1軸であるX軸回転を見てみます。

 これは単純化ができそうにないように思えますが、実はカメラアングルが地面に対して0度(つまり、地面に対して平行)、言い換えると一点透視の状態だと計算は非常に簡単になります。


 《 図2 》


 このように四則演算のみで画面のドットに対応したカメラからの距離が求めることができるのです。これはビュー変換が不要であるためスクリーン座標変換(透視変換)のみで実現できるからです。
 これならば、その計算によって求まった地面の座標(元画面の座標)のドットを読み取り、それを図1のやり方のように一定の方向にスキャンすれば良いだけです。

 しかし、カメラアングルが0度で固定ならば「2軸回転プログラム」としては不十分です。しかし、自由な回転を行うためには行列を使って計算する必要があります。この方法は先ほども書いたように極めて重い処理であり、フラットシェーディングのポリゴンプログラムの方が格段に軽いくらいです。実際、私がmkIIで作ったプログラムでいえば、98頂点、152ポリゴンで構成されたアンドアジェネシス(もどき)よりもGRP2軸回転で128x64ドットの表示をした方が格段に重かったです。

 カメラアングルが0度で固定の場合は一点透視(実際はZ軸回転が加わるため二点透視となる)ですが、カメラアングル可変の場合は三点透視となります。
 これは行列を使わなくても様々な方法によって計算が可能であり、このプログラムでは直線と平面の方程式によって求めています。

 《 図3 》


 このプログラムではこの方法によって行列を使ったビュー変換と同等のものを桁違いの高速化を実現しています。

 しかし、実を言うとそんな方程式を解かなくてもまずは垂直方向の画角を求めそして表示するドットを全体の画角に対するtan θの割合からそのドットに対応する地面の場所も計算が可能になります。32bit固定小数点であり、小数点以下が12bitしかないプチコンmkIIではこの方法は精度の問題で実用的ではありませんでしたが、プチコン3号の実数型は仮数部が52bitであるため精度の面では問題はないと思われます。今回はmkII版のベタ移植ということでその方法とどちらが有利かの比較はしていませんが、興味がある人は自分で試してみてください。


最後に



 このプログラムは自作ゲームで使いやすいように回転の中心はプレイヤーのいる座標で固定して カメラが動いても常にプレイヤーが中心にくるようにしています。とはいえ、実際にゲームで2軸回転を行う場合はプレイヤーの表示をどうするのかが問題になってきます。

 それはどういうものかというと、このサンプルでは飛空挺で飛んでいるものをイメージしているため船のスプライトキャラが空中に浮いたような状態にしているのですが、船のキャラを自由な角度から表示することはできないため真上から見たカメラアングルにしても船そのものは後方で見た感じになっています。
 これを改善するにはカメラアングルが固定化されているならばその固定化されたアングルで見たスプライトキャラを描けば良いですが、このプログラムのウリである「カメラアングルを自由に変えることが可能」というのが活かせません。カメラアングルを自由に変えた場合にそれに追従するようにキャラが変化するためには自由な角度から見たスプライトキャラを用意する必要がありますがそれは非常に大変です。

 その場合は「キャラのみポリゴン表示をする」という方法があります。プチコンmkIIで作ったポリゴン表示プログラムならば
こちらにあるのでこれをプチコン3号に移植しても良いしょう。プチコン3号であればGTRI命令を使えばmkIIと比べて桁違いの高速化が実現できます。
 ちなみにキャラのみポリゴンでゲームの背景はGRP2軸回転プログラムを使った擬似3DレースゲームもプチコンmkIIで作ってみました。こちらに試作品のプレイ動画を用意していますが、非常に重い処理であるためプチコンmkIIではレースゲームとしては楽しめないというというのを理由に試作品を作った段階で制作は中断しました。(この「3Dレースゲーム試作品」で使われている自作のポリゴンカー「ブルースワロー(モチーフはもちろんツバメ)」「レッドイーグル(モチーフはもちろん鷹)」のデータは第3回プチコン大喜利でノミネートされた「プチコン用3Dポリゴン立体視プログラム」に含まれているので自作ゲームで使いたいという人は自由に使ってもらって構いません。)  しかし、mkIIより桁違いの高速化(New 3DSで動作時ならば20〜40倍程度の高速化が可能)なプチコン3号であれば実用になるかもしれません。興味ややる気のある人はぜひチャレンジしてみてください。(私は当面やりたいことが他にたくさんあるため作るとしてもしばらく先になりそう)

 というわけで、ゲーム向けとして作ったけど実際に使えるかどうかは未知数であるもののそれで良ければぜひ使ってみてください。本当は時間をかけてちゃんとゲームに使えるか検証や最適化をしたかったですが、時間的な都合もあり、このままではお蔵入りになりそうなのでベタ移植の状態で今回公開しました。(といっても、Miiverseやtwitterで公開したのは随分前だけど)
 気づいた範囲内での例外処理はしていますが、不具合等を見つけた場合は報告してください。


RETURN (プチコン3号/BIGプログラムのページにもどる) RETURN *MAIN (トップページにもどる)

inserted by FC2 system