プチコン用 GRP 2軸回転プログラム

 これはプチコンのGRP画像をドット単位で読み取りそれを2軸回転することで疑似3D表示を行うプログラムです。2軸回転というのが何かというと分からない人もいると思うので簡単に書くとスーパーファミコンの持っている機能(F-ZEROなどで使われているやつ)をプチコンのプログラムで実現したものです。
 2軸回転ルーチンだけだと使い方が良く分からないと思うので2軸回転ルーチンだけではなくキャラの動きによってどう画面変化するのかが分かる簡単なサンプルプログラムも合わせて用意しています。拡大、縮小、カメラアングルもタッチによってリアルタイムで自由に変更することができます。
 デフォの設定だと10fps、最適化を図れば20fpsで表示が可能です。


 《 メニュー 》


《 プログラムリスト 》

このプログラムリストは2軸回転ルーチンだけではなく実際にそれを使うためのサンプルプログラムも含んでいます。

ACLS:GCLS 255:GPAGE 0,2,0:GCLS 255
CLEAR
BGMPLAY 23
PNLTYPE "OFF"
LOAD "GRP1:OCHAMRCT",0
LOAD "COL2:OCHAMRCT",0
LOAD "COL2L:OCHAMRCT",0

KX=8:PX=32:PY=12
CL=2:CA=60:RS=0.2
X=128:Y=96:PA=0
SP=0:SC=KX*PX
IF SC>200THEN SC=200

SPSET 0,76,0,0,0,0
SPHOME 0,8,8
SPSCALE 0,SC
SPANIM 0,4,6
SPOFS 0,KX*PX/2,192-SC/12.5

SPPAGE 1
SPSET 0,110,0,0,0,0
SPHOME 0,1,1

@LOOP
B=BUTTON():T=TCHST:TX=TCHX:TY=TCHY
IF B AND 4 THEN PA=(PA+355)%360
IF B AND 8 THEN PA=(PA+5)%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 T THEN CA=0OR 90-(TCHY/3)+0.9:RS=POW(0.988,TCHX):CL=RS*10
C=2-C

GOSUB @3DROTATE
VX=SN*SP/16:VY=-CS*SP/16
SPANGLE 0,PA+45
SPOFS 0,X,Y
GPAGE 1
IF GSPOIT(X+VX,Y+VY)<0THEN VX=0:VY=0:BEEP 1
X=X+VX:Y=Y+VY
LOCATE 0,0:?"("0OR X","0OR Y") ソクド"SP" カクド"CA" サイズ"(0OR KX/RS+0.5),,
FPS=FPS+1IF MAINCNTL-CNT>59THEN LOCATE 27,1?"FPS"FPS,:FPS=0CNT=MAINCNTL
GOTO @LOOP

@3DROTATE
SN=SIN(RAD(PA))
CS=COS(RAD(PA))
SI=SIN(RAD(CA))
CO=COS(RAD(CA))
TA=TAN(RAD(90-CA))
MY=191
FOR I=1 TO PY
 DW=CL/I/RS-TA
 DX=CL/(DW+!DW)
 DY=DX*SQR(1+TA*TA)
 IF DY<0OR DY>512THEN DY=512
 XR=(CL*SI+DX*TA)/CL*RS
 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 1: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
QRコード(ファイル名:OCHA2ROT)

《 使用方法 》

 このサンプルプログラムの使用方法について書いていきます。
 実行するには適当なGRP画像が必要になります。このプログラムではこちらにある世界地図を使用しています。このGRPではなく自分で用意したものを使いたいという人はプログラムリスト5〜7行目を変更してください。

 [A]ボタンで前進、[B]ボタンがバックです。押し続ければ最大±20まで加速します。[X]ボタンを押せばその場に停止できます。
 タッチパネルでは拡大、縮小やカメラアングルの変更ができます。タッチのX座標が拡大率(画面上では「サイズ」の数字)、Y座標がカメラアングル(画面上では「カクド」の数字)になります。デフォの状態では拡大率は8〜183、カメラアングルは27度〜90度まで設定できます。この拡大率というのは元画像を真上から見下ろしたとき(カメラアングル90度)に元画像の1ドットが何ドット分に拡大されているかを示した数字です。
 ちなみに拡大、縮小はカメラの距離を変えることで行っています。レンズの画角調整は後述の画面表示ドット数を変えることで調整できます。(レンズの画角でサイズを変えなかったのはリアルタイムでカメラ距離も変更できるようにする必要があり、プログラムが長くなったり、操作が複雑化するのを避けるため)
 下画面に表示されている矢印はプレイヤーキャラの居る場所と向いている方向を示しています。

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

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

 変数KXの値を変えることで画面に表示している1ドットの大きさが変わります。これを小さくすればより多くのドットが表示可能になります。
 変数PXPYの値を変えることで画面に表示している縦横のドット数を変えることができます。プチコンは横256ピクセルの表示が可能なので1つのドットの大きさが4ピクセル(KX=4)ならば横に64ドット分表示できます。  概ね画面表示のドット数に比例して遅くなっていく(縦横2倍のドット数ならば4倍くらい遅くなる)ためむやみに表示ドット数を増やすと遅くてまともに操作できなくなるため注意が必要です。あと表示ドット数を増やすと見える範囲が広がるため画角が広がります。
 変数CAのカメラアングルはカメラを地面に水平に向けた状態を0度としています。0度だとカメラの地面からの高さがゼロになるため表示できません。変数PAのプレイヤーの方向は時計の12時の方向を0度としています。
 変数RSは真上から見下ろした時の画面上の1ドットが元画像の何ドット分に相当するのかを示したものです。デフォではRS=0.2となっていますが、元画像の1ドットが画面上ではその逆数の5ドット分になります。デフォでは1つあたりのドットは8倍拡大された状態になっているため40ピクセルのサイズで表示されています。

《 プログラムの仕組み 》

 このプログラムではプレイヤーの居る座標を原点としてX軸、Z軸の2軸回転処理を行っているわけですが、まずはZ軸回転から見ていきます。

 Z軸回転というのは普通の平面上における回転と考えてもらえたら良いでしょう。回転は行列式で簡単に求めることが可能で一般的なxy平面において座標(x,y)の点を原点を中心にθラジアンほど反時計回りに回転させた場合の座標(x',y')は次のようになります。

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


 回転の中心が(0,0)でないときは平行移動させれば良いだけなのですが、1ドット単位に行列演算をする場合には回転をさせる処理が非常に重くなってしまいます。また、回転前と回転後ではピクセル数が変わってしまう(元は異なるドットなのに回転後は同じ座標になる場合がある)ため回転後の画像は隙間だらけになってしまいます。
 そのため元の画像を基準にして回転後の座標を求めて回転させるのではなく回転後の座標から元画像のどの部分を読み取ったら良いのかを考えていきます。

 《図1》


 このように回転後の画面を基準にすること回転後の画像に隙間が無くなります。また、スキャンラインは角度がすべて同じであるためスキャンラインのX、Y方向の増分量をあらかじめ計算して変数に入れておけばループ内では単純な加算のみで済むので高速になります。

 2軸回転はこれにX軸回転を加えたら良いです。ただし、地面に対するカメラ角度が0度(つまり、地面に対して水平)の場合はビュー変換は不要でスクリーン座標変換は単純な1次式で表すことができるので簡単です。つまり、画面に対応しているドットが実際にカメラからどれくらい離れている地面を表示しているかというのは簡単に除算で計算できるわけです。

 《図2》


 2軸回転はこの距離情報(パースの影響)を加味した上でZ軸回転を行えば良いのです。
 この場合に図2においてプレイヤーの位置を基準にして回転させれば三人称視点のゲーム、カメラの位置を基準にして回転させれば一人称視点(プレイヤー視点)のゲームとなります。図2は三人称視点の場合なので、一人称視点のゲームでは図2のカメラの位置がプレイヤーの位置になるためです。

 カメラアングルが0度固定(水平)ならばこのように距離は簡単に求まりますが、それ以外の場合には単純な四則演算では求まりません。これはビュー変換をしてしまえば簡単です。要するにカメラ角度がxz平面に対して0度になるように回転移動をしてしまうわけです。
 問題となるのは単純に行列演算で求める場合には非常に粗いデフォ設定でも32x12で384個の座標について計算する必要があるということです。この処理はポリゴン表示プログラムの「アンドアジェネシス(のようなもの)」の98頂点のジオメトリ演算処理よりも重いものになります。

 そこで、無駄が多い行列演算で求めるのではなくビュー変換を自前で計算することにしました。地面に対してのカメラ角度が0度というのは透視図法でいえば一点透視や二点透視の状態で、それ以外の角度というと三点透視の状態になります。カメラに対して垂直な投影面を設定して、その投影面上に等間隔に並ぶような地面の座標を計算すれば良いだけです。これは三次元空間における直線と平面の交点を求めることができれば可能です。
 これは普通に方程式を使って求めました。それを処理速度を稼ぐため簡略化したのがこのプログラムです。(画面のドットに対応する地面の3次元空間上での座標さえ分かればカメラと地面の距離は相似を使って求めることが可能になる)

 《図3》


 実を言うとそんな方程式を解かなくてもまずは垂直方向の画角を求めそして表示するドットを全体の画角に対するtan θの割合からそのドットに対応する地面の場所も計算が可能になります。ただし、この方法は人間にとっては分かりやすいけどプチコンの演算処理負担は大して軽くない上に細かい角度のtan θの値を求める必要があり、固定小数点のプチコンでは使い物にならないくらい誤差が出るためやめました。つまり、このプログラムはそれよりも精度や速度の面で勝っているということです。
 私のプログラムでは三角関数は最初の1回(本来はカメラアングルを変えたときのみ実行すればいいので@3DROTATEルーチンから出してやればほんのわずかだけ速くなる)だけ計算すれば求めることが可能になっています。いくらプチコンの三角関数の演算速度が速いといっても四則演算と比べたらかなり遅いのでメインループ中に何度も使うのは速度低下の原因となります。(縦12ドット表示でもサブルーチン内で最低12回の三角関数演算が必要)

 図3のようにビュー変換後のカメラからの距離が求まれば画面横方向の大きさは基準サイズを元に比例計算するだけで済みます。これによってビュー変換処理は桁違いに高速化できました。
>

《 最後に 》

 実をいうとこの2軸回転プログラムは当初は「GRP画面を拡大、縮小、回転をしよう」というテーマのプチコン講座に書く予定だったものです。Z軸回転、X軸回転を書いて最後は両方を使った2軸回転というものにする予定でした。しかし、講座として成立させる(完成したプログラムの仕組みを完全に理解してもらう)ためにはプチコンのメインターゲットが小中学生であるということを考えると最低でも三角関数や行列などに関するする数学講座も書かなくてはなりません。2軸回転については上記のように「公式に入れるだけ」では済まないため高校数学をすべて終わらせておかないと理解するのが難しいです。(そういう面では行列式に入れて一発で求まるワイヤーフレーム表示の方が格段に簡単)

 また、「2軸回転」は講座内で最終的に作るゲームに必要な技術としておかないと意味がない(技術というのは目的ではなく目標となるものを作るための手段でしかない)ため2軸回転を上手く使ったゲームが必要でした。対応ゲームは今年の4月に締め切られた第2回プチコン大喜利に向けて作っていたのですが、とりあえず実行できるレベルのサンプルを作ってみたところあまりに面白くないということが分かりました。これはゲームバランスを調整したら何とかなるという問題ではなく速度を重視した場合にはドットが粗すぎてスピード感が全く出ないという問題であったため速度面からレースゲームに使うのに厳しいということです。
 しかし、この2軸回転を有効利用できるシンプルなゲームを思いつかなかったため結局その講座は中止となりました。(講座で書く予定だった内容の大まかなあらすじについては第6回プチコン講座のコラムに追記した)

 普通ならばこのままお蔵入りしてしまう場合が多いのですが、2軸回転について興味を持っている人が結構多かったためゲームという完成品ではなく「2軸回転ルーチン」という技術公開という形で今回発表することにしました。とりあえず、2軸回転がどんなものか分かるサンプルプログラムを用意したのでそれを実行すればどのようなものかは分かると思うし、実用になるか否かということも各自で判断してもらえるのではないかと思います。
 デフォでは32x12ドット8倍拡大で10fps程度なのですが、8倍拡大固定ならばGFILLを使わずBGPUTを使うことで高速化が可能になります。ただし、単純にBGPUTに置き換えただけではあまり高速化はできないためBGPUTで最も高速に表示できるようにパレット変更処理を無くすため全体での使用色数を16色のデータに限定するなどの最適化を図ることで最大でデフォの約2倍の20fps程度の速度にすることが可能になります。興味がある人はチャレンジしてみてください。


RETURN/RETURN *MAIN

inserted by FC2 system