俺に解るように説明する "Armory Engine" 入門+

ゲームエンジン Armory Engine (Armory3D) の使い方を手探りで学んで入門しようって感じかな。

P5.エアホッケー.02「動くパックの登場?」

f:id:ore2wakaru:20190505011743p:plain

壁の所に来たら跳ね返るような感じに動くパックを登場させる。物理でやるならApply Impulseや、Apply Forceだが今回は使わない。普通にオブジェクトを動かす場合はTranslate ObjectTimeノードの合わせ技を使う。普通は、こっちの方を先に覚えるかな。

カメラとライト

パックが動いても、カメラとライトがちゃんと設定されていないと [▶ Play] しても見えないからな。イイ感じの所にセットしておこう。

 カメラ: テンキーの 0 でゲーム画面上どう見えるか確認しながら位置を決めればいい。まー、一番いいやり方は Lock Camera to View でやるやつかなー? それから、Clip End150m くらいに設定かな。これより短いと向こうの方が欠けるでしょ、多分。(ココ参照。) ちなみに俺はこの位置と回転にセットした。

Location Rotation
X 0 64
Y -88 0
Z 36 0

 ライト: ライトはやっぱっり Sun にしてステージの下に置くよね、じゃまだし。(ココ参照) 回転は適当。

Location Rotation
X 0 37.3
Y 0 3.16
Z -10 107

で、整理整頓は心がけておこう。

f:id:ore2wakaru:20190504194754p:plain

パックとマレット

パックを置くのと、ついでにマレットも置いておこう。一緒のコレクションに置くだけ、簡単。

f:id:ore2wakaru:20190504203854p:plain

 マレット(com): コンピュータが動かすマレット。シリンダーから出来てます。直径3m、高さ1.8m。色は適当にどうぞ。

Location Scale
X 0 1.5
Y 30 1.5
Z 0.95 0.9

 マレット(player): プレーヤーが動かすマレット。大きさは com 用のと同じ。色は適当にどうぞ。

Location Scale
X 0 1.5
Y -30 1.5
Z 0.95 0.9

 パック: とりあえず置いてみた。直径は2m、高さが90cm。

Location Scale
X 0 1
Y 0 1
Z 0.5 0.45

パックもマレットも床から少し浮いてるのは、物理で動かそうとしていた時のなごり。別に床に埋まっててもどうでもいい。大事なのは直径(というか半径と言ってもいいけど)。パック2mとマレット3m。跳ね返る時にこの数値を使う。

アウトライナーだとこんな感じになるようにしてね。

f:id:ore2wakaru:20190504210329p:plain

(まだ扱ってないのは、"Stage" コレクションの "Tenjyo" と、"Kami" コレクションだな。)

パックにノードトゥリーを貼る

Trait(トレイト)の付け方は大丈夫だろ? パックに付ける内容は、まずはこう。ただ、今後どんどん変更していくから注意だ。

f:id:ore2wakaru:20190504214349p:plain

ちっちゃくって何が書いてあるか分からないので、部分的に拡大して解説する。(これがノードスクリプトの弱点だよな・・・)

 パックの初期値を設定: ここではパックの初速を決めている。

f:id:ore2wakaru:20190504215401p:plain

On Init: このスクリプトが貼ってあるオブジェクトが Active(アクティブ)な Scene(シーン)に出たとき最初に1回だけ呼び出される。と思う。アクティブなシーンとは何ぞや? って思うが、たぶんメインカメラが映してるシーンのこと。前、レンダリングの1回目とか書いた気がするけど、別にカメラに写ってなくてもここは呼ばれるからね、1回。出てきた時、最初に1回だけ呼ばれるものって覚えて置けばイイと思う。基本、ここではオブジェクトの初期値を設定する。

Set Property: オブジェクトにデータを保存するためのモノ。スクリプトじゃなくてオブジェクトにデータを入れ込むってのがおもしろいね。今回はパックが動く速度を設定したいので "myDir" という名前で下で設定したベクトル値(x: -20, y: 20, z: 0)を保存してもらっている。速度は秒速。だから、毎秒x方向に-20m、y方向に20m進むよってことね。プロパティには何でも入れることが出来るので便利なんだけど、逆に注意が必要とも言える。

Vector: ベクトルの値を入れるとき使う。(あー説明になってないな。) Armory は 3D Game Engine なので、ベクトルは3次元のしか無い。

 パックのメインループ: ここでパックを動かす。(写真クリックで拡大)

f:id:ore2wakaru:20190504221945p:plain

On Update: 毎フレーム呼び出されるモノ。ただ、なんだか Armory が 06 になってから、3パターンで呼べるようになったみたいだな。

  1. Update: 普通のやつ。
  2. Late Update: これは恐らくカメラ用。全部のオブジェクトが動き終えてから、カメラは最後に位置を決めたいでしょ。
  3. Pysics Pre-Update: これは物理用。物理の力で動かす奴はコレ使うはず。物理のタイミングと描画のタイミングが違う為だと思う。多分。

今回は、カメラでもないし、物理も使ってないので普通のでOKだ。

Translate Object: ベクトル値で入れた分(方向と距離)だけオブジェクトを動かす。

Time: 時間に関する2つのデータを取り出せる。

  1. Time: ゲームが始まってからの時間(秒)を取り出せる。Float型で出て来る模様。
  2. Delta: 前のフレームから何秒経ったかを取り出せる。Float型。

1の Time はイイとして、2の Delta は非常に大事。

ゲームの画面描写はパラパラ漫画なのはご存じだとは思うが、1枚の表示を何秒にするかで滑らかさが異なるよね。まー、1秒で何枚使うかって考えてもいいけど。例えば1秒で50枚使ったら、1枚の表示時間は、、、50分の1秒だから、、、0.02秒か? ま、これが Delta だ。パラパラ漫画の1枚の表示時間、それが Delta。

ゲームだとご存知のように、大体1秒60枚(60FPS / FPSはframes per second)で動かすのが多いだろ。すると、0.016666・・・秒ってなるな。この時間のこと。

で、なんで大事かって言うと、オブジェクトを動かしたい時の速度は秒速。つまり1秒で何メートル動かしたいかを設定したわけだ。だけど、On Updateには毎フレーム来ちゃうわけだろ、だから、パラパラ漫画で言えば1枚の表示時間(= Delta 秒)で動かすだけの距離を考えないとイケナイ。

くそ簡単に言えば、例えば「秒速150mで動くものの場合、Delta秒では何メートル進みますか?」を解きましょうって考えるわけだよ。算数だな。秒速150mは1秒で150mなんだから、「1秒で150m進むものは、Delta秒で何mすすみますか?」ってのと同じことじゃろ。

1(秒):150(m) = Delta(秒):X(m)

小学生の時やった比例式、"内項の積=外項の積"で一発や。

X = 150 × Delta

だから、Delta秒で動かす距離は "秒速 × Delta" で出るってことなんだよ。簡単だが、すごい大事なことなのだ。

・それをやってるのがVector Mathの部分。設定した秒速をGet Propertyで取って来て、xとyにDeltaをいれたベクトルと掛け算してる。つまり"秒速 × Delta"をやってるってことね。(zには動かないから入れなくていいよ。) Vector Math"Multiply" は掛け算のとこ。

例えば、Aベクトル(x1, y1, z1)とBベクトル(x2, y2, z2)を "Multiply" するとベクトル(x1 * x2, y1 * y2, z1 * z2)が出て来るってことね。各項を掛けたものが各項に入る感じだ。

ここまでで、一応パックは毎フレーム、進むべき方向と距離を行くわけだ。

だけども壁があったら、跳ね返らないとイケナイだろ。その判断と処理をしてるヶ所が、右に4つならんだSend Eventだ。別に❸~❻を横にならべてもいいけどすげー長くなるからイヤだろ。だからまとまりを作ってその場所に飛ばしているわけだ。まー、見やすくなってよかったねってことだな。

On EventSend Event: On Eventでつくった場所(イベント)にSend Eventで処理を飛ばす。これはセットで覚えてね。イベント名を入れるところがあるだろ、ここに名前を付ければいいだけだ。作ったイベントはリストで出してくれるわけではないので、打ち間違いに注意な。

On Event "LLLL": ここではパックが左の壁で跳ね返るように処理をしている。イベント名 "LLLL" で飛んでくるところ。

f:id:ore2wakaru:20190505001847p:plain

Get Location: オブジェクトの位置を取ってこれる。位置はベクトル型で入ってくる。今回欲しいのはxの値だけなので、、、

Separate XYZ: ベクトルの成分を分解して取り出せる。これを使ってxの値だけ持ってくる。なんでかって言うと、床の大きさが横30m×縦70mだろ。横だけ考えれば、±15mの所が壁だ。で、パックの半径が1mだから、それを引いて、パックの(中心)位置が-14mのところか、それよりもっと過ぎたら跳ね返る処理をすればいい。その判断をGateでしてる。"Less Equal" っていうのは "<=" のこと。

跳ね返すにはどうすればいい? そう、次に自分の進む方向のx方向を反転させればいいだろ。だから、次の所で "myDir"x-1を掛ける処理をしてる。プラスのモノに-1を掛ければマイナスに、マイナスのモノに-1を掛ければプラスになる。つまり-1を掛けるとプラマイが反転するんだよ。簡単だろ?

On Event "RRRR": 今度は右の壁のところまで来てるか調べて、跳ね返るように処理をしている。イベント名 "RRRR" で飛んでくるところ。

f:id:ore2wakaru:20190505005555p:plain

まー、とほとんど同じなんだけど、"-14" じゃなくて、"14"。"Less Equal" じゃなくて "Greater Equal"(">=")になってるだけ。右壁だからね。

On Event "DDDD": 今度は下(自陣の壁)のチェック。

f:id:ore2wakaru:20190505005903p:plain

下の場合はxじゃなくてyのチェックが必要でしょ。だから、Separate XYZではyを持ってくること。あと、床の縦は70m、半分が35、そこからパックの半径-1を引いて34。つまり±34でチェックする。下なら-34ね。

縦の場合、反転させるのはxじゃなくてyだから、 "myDir"yに-1を掛けるぞ。注意。

On Event "UUUU": 上(敵陣壁)。

f:id:ore2wakaru:20190505010601p:plain

  1. xを取って来るのかyをとってくるのか。
  2. "Less Equal" か "Greater Equal" か。
  3. 反転させるのは(-1を掛けるのは)xなのかyなのか。

そこんとこ注意!

テストプレイ

壁のなかで動くはず。ただし、ゴール処理はしてないので永久に動くのだ。(Gif画像はメンドクサイから無し。)

でも、あれだ、ゲーム開始でいきなりパックが動いてたらビックリするじゃん。だからスペースキーを押したら動くようにしてみようと思う。ではまた次回。

本日のポイント
  • On Updateには3パターン。
  • オブジェクトを動かすにはTranslate Object。ただし、TimeDelta を掛け算する意味、理解すべし。
  • べクトルの計算はVector Math。ただのMathとは違いマス。
  • On EventSend Eventのペアで綺麗に処理。

 次回、玉スポーン予定。