〈2024年9月7日追記:色々全面刷新したのでこっち見てください〉
<2021年8月24日追記:クソ見づらかったので以下に簡潔に書き直しました>
文系で数学わからんちんですが、その分同じような人でも分かる書き方になっているといいなと思います。
分からなくても色々試行錯誤していれば分かってくると思います。
目次
基本的な考え方
ウディタの基本(変数呼び出し値など)
描画①(3次元座標の角度)
描画②(中心点からのブロック生成と自由変形、RGB)
隠面消去①(床、壁、天井とブロック内)
キャラ管理(ブロックから構成されるキャラ)
キャラの動かし方(ブロックの下側のみ動かすなど)
描画③(座標の回転)
隠面処理②(キャラ間のバブルソート)
おわりに(未着手:当たり判定など)
1.基本的な考え方
・3次元の座標を2次元のゲーム画面にどう投影するかという話です。
・自分の視野をゲーム画面だと考えたときに、その辺の机の角がどこ表示されるか、みたいな話です。
・机の角を中心にブロックを表示すれば、マイクラが作れます。
・上面だけに血を表示する薄いブロックを土ブロックの上に表示すれば、血のついた土を表現できます。
・ブロックを人型に配置して連動して動かすとキャラが動きます。
・床、壁、天井のブロック生成はこのHPで公開している3Dマップ↓の非暗号化コモンの中で説明しているので割愛します。
2.ウディタの基本(適宜飛ばしてください)
■変数呼び出し値
変数呼び出し値は覚えておかないとコモンイベントが作れないので書きます。
16000XXはコモンセルフ変数の呼び出し値です。
例えばピクチャのX、Yに1600000、1600001を指定すると、コモンセルフ変数0と1がそこに入ります。
ピンとこない人は、コモンセルフ変数0と1に変数操作+でマウスX座標、マウスY座標を入れて、ピクチャのX、Yに変数呼び出し値を指定し、好きなピクチャを表示してみてください。
マウスの場所に好きなピクチャが表示されます。
なお、表示位置は中心にしましょう。
左上だとマウスの位置の右下にピクチャが表示されます。
■文字列ピクチャ
ピクチャのオプションに文字列ピクチャがあり、入力した文字を表示できます。
特殊文字が使えるのでデバッグに便利です。
■ループ処理とカウンター
ほぼ全てのコモンイベントで使います。
ループを作りカウンターを0から1つずつ増やします。
カウンターはコモンセルフ変数XX=コモンセルフ変数XX+1として増やしていきます。
カウンターの数値を条件分岐で毎回チェックし、所定の数になったらループを中断します。
DBを上から順番に処理していくときなどに使います。
3.描画①3次元座標の角度
■机の角がどこに映るか
実際の視野をベースに考えてみます。
自分の前方横幅90°くらいが視野で、それ以外は見えません。
(視野角と歪みについて、10.おわりに、で触れました)
視野内のものが視野のどの場所に映るかは、自分から見た角度÷視野の横幅(90°)で決まります。
縦も同じで、縦幅は60°とします。
机の角が視野の左端から45°、上端から30°の位置にあれば、視野の真ん中に机の角が見えます。
この話ではウディタで同じことをやります。
■机の角の座標
机の角がある場所は、ウディタ上では座標(x,y,z)で表します。
横と縦の角度は、座標から求められます。
経度と緯度を思い出してみてください。あれは東西と南北を表していて、ここでいうxとyがそれにあたります。
座標は、「南に4歩、東に5歩進んで3階に上がった所にいる人」のように、空間の中で対象がいる場所を特定する数字です。この例だと(+4歩、+5歩、+2階)がその人のいる座標になります。
(※処理上はzは高くなるほど少なくなります)
横と縦だと高さが分からないので、高さをzとして加えたのがzです。
机の角が自分の真南1m、1m下にあれば、(0m、1m、1m)になります。
■机の角の横と縦の角度
角度はウディタの計算オプションで、X座標、Y座標から出せます。xとyを入れると自分から見た横の角度θ(シータ)が分かります。
縦の角度を求めるには少し準備がいります。
三角関数は意味を理解せず、ツールだと割り切って使うと便利です。
X÷cosθ=rか、Y÷sinθ=rでrが求められます。rは座標(x,y)までの直線距離です。
sinθ、cosθもウディタの計算オプションで、角度θから求められます。
(cosθが0、xが0に近いとrがおかしくなるので、条件分岐で絶対値を用いて併用してください。)
机の角が真南1mにあったら、東西南北だけで考えた距離は1mですが、南に1m、東に1mだったら、距離はもう少し長そうですよね。この距離がrです。
距離rと高さzは、xとyと同じように、ウディタの角度計算に入れて角度を出せます。そして、この角度が縦の角度になっています。
縦の角度はθではなくΦ(ファイ)と言うらしいです。
これで机の角の、自分から見た横と縦の角度が出せます。
それぞれ90°、60°で割り、ゲーム画面の横幅と縦幅をかけると、ゲーム画面のどこに机の角が表示されるか分かります。
1280×720のサイズなら、X座標640、Y座標360で中央に表示されます。
■なんで縦の角度になるの
ホールケーキの中心まで包丁を入れることをイメージをしてください。
ホールケーキを上から見ると、ホールケーキの切り口が横の角度、中心から切り口までの距離がrです。
切ったケーキの断面は絵文字(🍰)のように四角になっています。
このとき、rはケーキの断面の横幅、高さzが断面の縦幅になっていて、xとyと同じ関係になっています。
そのため、rとzの座標から、ケーキの中心の底と上の切り口の角度、縦の角度を求めることができます。
ホールケーキ。美味しそうですね。
4.描画②ブロック描画
■ブロックの頂点を考える
机の角のまま描画しても、単なる点なので面白くありません。
そこで机の角を中心にブロックを描いてみます。ブロックは8つの点からできています。
この8つの点は、小さいxと大きいx、小さいyと大きいy、小さいzと大きいzの6つの数字の組み合わせでできています。
x-、y-、z-/x+、y-、z-/・・・みたいな感じです。
(実際は中心点の座標+x-とかです、x-は負の数値です)
机の角から1m間隔でブロックを描くと、2m×2m×2mの大きなブロックが目の前に出現します。夢がありますね。
■ブロックの面を描画する/自由変形
ブロックの8つの頂点は、6つの面を作ります。上下、前後、左右です。
ここでウディタの自由変形機能の登場です。
自由変形機能は、四角形の頂点の投影xy座標を全て打ち込むことで、その頂点から描かれる四角形を描画できます。
この四角形を組み合わせるとブロックを表現できます。
中心点から頂点までの距離を変えると、ブロックのサイズや長さを自由に変えられます。
流れとしては、下記のとおりです。
①中心点の座標を求める
②中心点からブロックの頂点の座標を求める
③自由変形を使い面を描く
■RGBを使った遠近表現
遠くの物ほど暗く描くと遠近感がでます。
これは単純で、xとyからrを求めたように、rとzから距離を求めれば、これが自分から見た中心点の距離になっています。
あとは中心点の距離が大きくなるほどRGBが小さくなるような計算式にすれば(100−距離とか)オッケーです。
ウディタのRGBは0〜200で、ピクチャから変えられます。
5.隠面消去①床、壁、天井
隠面消去により、壁の手前にブロックが表示されます。ブロックも裏側は見えません。
■隠面消去とは
手前にあるピクチャを手前に表示する処理です。
ウディタはピクチャ番号が大きいピクチャを手前に表示するので、100m奥のキャラを5m奥のブロックより手前に表示したりします。
これではアカンので隠面消去する必要があります。
■床、壁、天井の優先度を落とす
床、壁、天井が他のブロックの手前に来ることはありません。
なのでブロックを生成するときにそのブロックが床、壁、天井であることを記録しておいて、描画するときの優先度を最低にしておきます。
例としてはピクチャ番号を-10000とかから表示する感じです。
■ブロック内の隠面消去
ブロックを最大3面までしか表示させません。
カイジのジゴロ賽:ブロックは一度に3面までしか見えません。
一度に見える3面は重ならないので、ブロック内で隠面消去できています。
どの面を表示しないかは、主人公とブロックの相対的な位置関係で決まります。
ブロックの左にいれば左面しか見えない、前にいれば前面しか見えない、そんな感じです。
床は上面、天井は下面、壁は隣りにブロックがある場合は描画しないようにして2面だけ表示します。
これを応用して、薄いブロックの上面だけ表示して床の上に配置すると、血のついた床を表現できたりします。
6.キャラ管理
■キャラの管理には2つのDBを使う
キャラDBとキャラブロックDBを用意します。
キャラブロックDBは、パーツを管理します。キャラを構成するブロック(手とか胴体とか)を連番で登録します。具体的には、基準となるキャラブロック(頭など)から見た相対的なXYZ座標・ブロックの寸法などを登録します。
キャラDBには基準となるキャラブロックの現在のXYZ座標と、起点のキャラブロック番号と終点のキャラブロック番号を登録します。
描画時は、各キャラブロックの相対座標とキャラの現在座標の合計で各キャラブロックの描画位置を求めます。
すると、キャラの現在座標を動かすだけで他のキャラブロックもまとめて動くようになります。
棒人間みたいな点の集まりをブロックで膨らまして、頭を掴んで動かす感じです。
なお、描画用にDBをもう2つ使いますが、それは9.隠面消去②で触れます。
■サードパーソンビュー
プレイヤーの座標移動に連動して動くキャラクターや、プレイヤーが操作できるキャラクターを作れば、サードパーソンビューの3D表現が可能です。
■複数キャラを動かす
複数キャラを動かしたいときはキャラDBを追加し、コモンイベントでキャラ番号0から順に処理していけばOKです。
■人型ではないキャラを動かす
キャラブロックの相対的な位置とブロックの大きさ、形状を変えることで、自由度の高いキャラ描写ができます。
マインクラフトの豚やクモはこの方法で作れます。
■非ブロックのキャラを描く(未着手)
自由変形は四角形に対応しているので、基本は四角形のみからなる正六面体(ブロック)しか作れません。
ただ、自由変形の頂点の一つの座標をダブらせることで三角形は作れるので、表面を無地にすれば三角形のポリゴンを使えます(模様があると歪む)。
これを使うと、equilinoxの動物のような表現はできそうです。
7.キャラの動かし方(ブロックの下側のみ動かすなど)
■旋回・探索
キャラが自分の方向を向くようにします。
主人公のxy角度とキャラの角度の関係を元に1度ずつ近づけていく計算です。逆回転だったりするので上手く調整してください。一方の角度差が180°より小さい方に近づけます。
なお、ここまでの計算式だと、回転したときの3面表示がそのまま(後ろが見えないとか)だったり、8点の座標を上手く求められず、描画が崩れてしまいます。
ここは8の座標回転で書きます。
■歩行
腕と足のブロックの下側の4点の座標だけ前後に動かすことで、歩行を表現できます。
横から見ると長方形が平行四辺形になる感じですね。
(中心点を腕足ブロックの上端に設定すると完全な平行四辺形になります。)
右腕と左足を+1、左腕と右足を-1にして頭と胴体を0にし、20から-20を往復する感じです。
ここの増加量と上限量はキャラの移動量と合わせるのがおすすめです。
前に出さない方の足の座標は固定される(後退しない)ので、マイナスの増加量がキャラの移動量と相殺することになります。
前後に動かす場合はブロックのy-、y+座標を加減することになる(私の場合)のですが、キャラの角度が変わったあとにy座標を伸ばすと、足が横に広がったりします。
そのため、■旋回で書いた座標回転の処理を行う前に計算式に加える必要があります。
■攻撃
ここまでの動きの応用でできるのは突進表現です。
私は腕を上げて銃を撃たせたかったんですが、そのあたりでPCに麦茶をぶちまけて終わりました。
腕の上げ方は次の座標の回転でやります。
8.描画③座標の回転
■横の回転
ここまで読んでいただきありがとうございます。
座標の回転は動くブロック(キャラ)を描画する際に必須ですが、少し複雑です。
行列が出てきますが、一旦はスルーして平気です。
座標の回転で調べると下画像の計算式が出てきます。左手系と右手系という言葉がありますが、左手系の計算式だとうまく行きます。
下の画像は、Z軸(高さ)で回転させたときのX、Y、Z座標がどこに移動するかを示したものです(末尾にURLあります)。
ホールケーキの中央にフォークを指して、フォークを垂直にしたまま回転させたときに、イチゴがどこに動くかみたいな話です。
z(高さ)は横回転しても変わらないのでzのままです。yとxは複雑ですが、「回転する前のx座標とy座標に、zの回転角度に応じた三角関数を乗じたもの」を足すと求められます。
そういうものだと割り切りましょう。
この計算式は、キャラがある角度を向いているときに、向いた方向を正面としてブロックを描画するときに使います。
この計算式はあらかじめX-sinθ、X+sinθみたいに組み合わせを作ってから、各ブロックの頂点を求めるときに足したり引いたりするのでかなり面倒ですが、実際に完成してみると三角関数スゲーってなります。
超注意点として、ウディタの三角関数はデフォで×1000されるので、適切なタイミングで÷1000することを忘れないでください。
■足や腕を上げる(横→縦の回転)
ここはかなり骨が折れます。
ホールケーキの真ん中にフォークを指して横回転させた後、フォークでホールケーキを持ち上げるイメージです。
Z軸(高さ)を中心に回転させたあと、X軸かY軸を中心に回転させる必要があります。
ここで行列のかけ算が登場します。ビビることはありません。行列が理解できないままでも回転はできます。
計算ルールだけ抑えましょう。
行列の見方を説明します。
行列は左側を行、右側を列で見て計算します。
(θzはz軸の角度、θyはy軸の角度という意味です)
下の画像では、左側1行目はcosθy、0、-sinθy、0です。
右側の1列目はcosθz、sinθz、0、0です。
これをかけていくと、1つめがcosθy×cosθzになり、あとはどれも0とのかけ算になり、0になります。
そのため、結果の1行1列目はcosθy×cosθzになっています。
次は左側の行だけ数を増やします。左側の2行目は0、1、0、0で、右側の1列目はcosθz、-sinθz、0、0なので、2行目1列目が-sinθzになります。
これを繰り返すと画像のとおりの結果になるはずです。
画像の下段にはもう1段階行列の計算があります。
左側の1行目がcosθy×cosθz、cosθy×sinθz、0、0で、右側1列はx、y、z、1です。
そのため、最終的なX座標=x×cosθy×cosθz+y×cosθy×sinθz、となります。
この計算だけで計算量はかなり増えますが、ウディタ3Dへの愛で乗り切ってください。重さはそこまで変わりません。
ちなみにyはx×-sinθz+cosθzと簡単な感じになっていますが、これは画像の右側の計算式と同じです。
Z軸で回転させたときにzの数値が変わらなかったように、y軸で回転させたのでyの数値が変わっていない、という感じです。
z軸回り→x軸回りをやりたい場合は、このルールを頭に叩き込んで、下のHPからx軸回りの行列を引っ張ってきて、画像の右側の行列を置き換えてください。
9.隠面消去②
■バブルソート
考え方は以下を参考にしました。バブルソートは計算速度が遅いそうですが、一番単純なので採用しました。
また、400個くらいまでなら実用レベルなのかなとも思います。
ブロックの中心点までの距離(3次元)をブロックの頂点を計算する際に保存し、バブルソートで並び替えをします。
各頂点のXY座標(16個)と距離を格納するDBを作り、ブロックの頂点を計算したら順番に格納していきます。
このDBには、最終的に描画するすべてのブロックの頂点の描画用XY座標が記録されます。
次に、このDBの距離と今の順番の2つを記録する別のDBを用意し、距離と順番を格納していきます。
このDBでバブルソートを行います。バブルソートは、データ0とデータ1の距離を比べ、近い方の距離と番号をデータ1に格納します。これをデータ1とデータ2のように、順番に上げていきます。
すると、最後のデータと比較するときには、最も近い距離にあるデータが最後のデータに入ります。
同じことを繰り返すと、2番目に近いデータが最後から2番目のデータに入り、繰り返すことで全データを大きい順に整列できます。
最後に、データに格納している元の順番を頼りに、元のDBに整列後のデータ番号を入力していきます。
このデータ番号に従いピクチャ番号を指定すれば、大きい番号が前に表示されます。
(3面表示するので、3倍したりする必要があります)
意外に軽く計算でき、驚くはずです。
10.おわりに
■視野の角度と歪み
9/13:反応を見ていて追記しました
視野角を90°としていましたが、これだと視野の両端が小さくなりすぎて、気になります。
手元にPCがないので確かめられませんが、60°以下が歪みが少なそうです。
サードパーソンビューであれば視野角を絞っても視野が狭いという感じはしないと思うので、一つの解決策かもしれません。
☆調べたHP☆
画角について
3Dオブジェクト描画のおさらい
人間の目はどれくらいの範囲が見えるか
■未着手:当たり判定
ブロックに当たり判定を持たせて通行不可ポイントを作ります。これはZ座標を無視してXY座標だけでマップを作る方法などがあると思います。
キャラクターの当たり判定は、ブロックの形状に応じて変わるためかなり手間がかかりそうです。
正直、ダブリ上等でやった方がいい気がします。
■3Dはゲーム用以外の素材が使いやすい
3Dゲームは素材の幅が広いという点でマップチップの2D見下ろしよりもノンフィールドのゲームに近い印象を受けました。
1280×720だと素材の関係でこっちの方が作りやすいんじゃないかとすら思いました。
建築パース(建築の人たちが建物のシミュレーションとかで使う床とか)のフリー素材がそのまま使えるのは偉大です。
建築パース使うとこんな感じです。
■未着手:影をつける
影とかつけみたいですよね。太陽の座標を作って、影の重なりは考えず、相対的な位置関係で明るい面とか影を描写するなら負担なくできそうです。
■重さ問題
問題は描画の重さです。
軽さを求めるなら、床を1枚のピクチャで表す、壁を表示しないなど、工夫が必要かもしれません。
あとは生成時に床などの番号に規則性を持たせ、主人公の周辺の床のみ描画判定の対象にする、なども考えられます。
キャラクターも9体表示で描画FPS30くらいには落ちるのでどうかなと思ったり。
あとはz座標を捨てるのも一つです。XYだけで画像を表示するやり方ですね。
案内人と迷い人のおまけはz座標を捨てていて、本編3Dは立体を表示せず点と棒だけで表現しています。どこから見ても点は点、棒は棒なので色々省けます。
■3Dキャラの描画センス問題
あとポリゴンにイカした絵を表示するのが難しいです。マイクラよりドット数は多いのでうまい人がやればもっといい感じにできるかなと思ったり。
マイクラ並みに1ブロックのドット数を落とせば高速化したりするんでしょうか。
■その他参考にしたHP
なるほど3Dグラフィック描画の仕組み
覚える必要はないので毎回これを見ながらやるのがおすすめです。
三角関数の公式
途中からわけ分からなくなりますが、3Dの視野のイメージがわきやすいと思います。
関心ある人がより良い方法に挑戦してくれることを夢見ています!
おしまい
Comments