企画記事
【完全ガイド】制作経験ゼロの社会人向けゲーム制作入門。夏休みを使って,Unreal EngineのFPSミニゲームを完成させてみよう
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
9 | メイン処理 | 的を作ってみよう | P.4(9)へ |
9.的を作ってみよう
メイン処理 1/2
ここからは,シューター要素に欠かせない「的」を作っていこう。「的」がなんであれ,それを素早く正確に狙って撃ち抜くことが,FPSの醍醐味の一つだ。
「的」の制作手順は,ブループリントというファイルの作成,3Dモデルの設定,当たった時の処理の実装という流れだ。
※本記事では「分わかりやすさ」を優先した実装を行うため,効率や拡張性などは後回しにしている。あらかじめご了承いただきたい。
クラスとは,かみ砕いて簡単に言うと,何かを作るための「共通した処理」をまとめたものだ。オブジェクトに対して,「設計図」や「ひな形」に相当し,各オブジェクトは,この設計図に基づいて作成を行う。
例えば,丼ものを作るとしよう。牛丼や親子丼,中華丼などさまざまな種類があると思うが,とりあえず「ご飯を炊く」という共通した処理がある。
もし,あなたが牛丼と親子丼,中華丼のそれぞれの設計図を作るとした場合,どれも「ご飯を炊く」という部分を含める必要が出てくる。ほとんど同じ内容を,繰り返し作るのは面倒だ。
ここで「ご飯を炊く」という処理を1つの設計図「ご飯クラス」にまとめて,各丼はその設計図をベースに,「ご飯に乗せる具材」をそれぞれ追加すれば良い。
牛丼の作り方をまとめた「牛丼クラス」,親子丼の作り方をまとめた「親子丼クラス」はそれぞれ別のクラスだが,両方とも「ご飯クラス」をベースにすることで,ご飯を炊く処理を省略できる。
当然の話だが,もし中華丼の設計図「中華丼クラス」を作るとして,「牛丼クラス」や「親子丼クラス」をベースにする人はいないだろう。
一方,牛丼にチーズを乗せた「チーズ牛丼クラス」を作るとしたら,「ご飯クラス」よりも「牛丼クラス」をベースにした方が効率的だ。
また,炊飯器を新しくしたり,米を精米から無洗米に変更したりする場合も,それぞれの丼クラスで個別に更新することなく「ご飯クラス」を更新すれば一括で更新できるメリットがある。
クラス | オブジェクトを作るための「設計図」に相当するもの 「的」のように「処理付きのオブジェクトを作成する」ということは,基本的に何らかの「クラス」をベースに,新しく「別のクラス」を作ることである つまり「設計図」をベースに,新しい「別の設計図」を作ることである 作成したブループリントはあくまで「設計図」で,レベル上に配置した際に「実体」となる |
話は戻るが,先ほど選択した「Actorクラス」は,すべての丼のベースとなる「ご飯クラス」に相当し,「Pawnクラス」や「Characterクラス」は以下のように説明できる。
Actorクラス | ご飯クラス |
ベースとなる,ご飯を炊くための手順書 | ベースとなるシンプルなもの |
Pawnクラス | 牛丼クラス |
Actorクラスに「操作できる機能」などを追加したもの | ご飯クラスに「牛肉」などを追加したもの |
Characterクラス | チーズ牛丼クラス |
Pawnクラスに「歩行機能」などを追加したもの | 牛丼クラスに「チーズ」などを追加したもの |
このようにクラスは,ベースとなるクラスに処理を追加していく使い方をする。
さて,今回「的」を作るために「Actorクラス」を選択したが,「的」は“牛肉”や“チーズ”(操作機能や歩行機能)が必要なく,むしろ邪魔になってしまう。
機能がいろいろと追加された「Characterクラス」が万能なのでは,逆にシンプルな「Actorクラス」で全部作ってしまえるのでは,と思った人もいるかもしれない。
しかしながら,クラスは適切に使い分けないと,似た処理を何度も作ることになったり,機能が多くて汎用性が減り,逆に不便になったりするのだ。
これらは抽象的な概念なので,感覚を掴むのが非常に難しい内容だ。筆者は「Java」というプログラミング言語を勉強していた際に,クラスの理解にかなり悩まされた記憶がある。同じように難しく感じる人もいるだろう。
筆者がUE初心者向けにアドバイスするならば,「クラス」そのものを理解するのは,後回しにしてしまおう。UEやプログラミングの概念に慣れ始めた頃に,改めて勉強すれば案外,簡単に理解できたりする。
とりあえず今は,クラスとは「何かを作る際のベースになるものだ」と覚えておけば大丈夫だ。
ファイルが何の役割をしているのかが,名前だけで分かるのが理想だ。まあ理想通りにならないのが現実でもあるわけだが。
ゲームエンジンやプログラミング言語などは,公式や団体が命名規則という「名前付けのルール」を公開していることが多い。あくまで管理をしやすくするためなので,必ずその通りにしろという訳ではないが参考にしよう。
命名規則 | 「名前付けのルール」のこと Unreal Engineで推奨されている命名規則では,アセットのプレフィックス(名前の先頭)に,アセットのタイプを入力する ・ブループリント:BP_ 例.BP_SimpleTarget ・マテリアル:M_ ・テクスチャ:T_ |
プレフィックス | 単語などの先頭に付ける文字のこと 日本語にすると「接頭辞」 少ない文字数で,簡潔に役割や目的を示すために,プログラミングでよく使う表現である 「BP_SimpleTarget」の「BP_」が,これに相当する なお,単語の先頭ではなく,末尾に付ける文字は「サフィックス」(接尾辞)という |
Unreal Engineで推奨されている命名規則に興味のある人は,下記サイトを参考にしてほしい。
Epic Developer Community
「アセットの命名規則に関する推奨事項」
次に,「的」に弾が当たった際の処理を実装する。各オブジェクトには,自身と何かが衝突したことを検知する機能があるので,「的」が衝突を検知したタイミングで,処理が動き出すように実装していこう。
エアガンを使ったスポーツのサバイバルゲームだと,当てた人ではなく,当てられた人が「ヒット!」と叫んで退場するのだが,イメージはそんな感じである。撃たれた側に,処理を実行してもらおう。
作業の前に,用語を2つ説明させてほしい。「イベント」と「トリガー」についてだ。
何かをきっかけに動き始める処理を「イベント」,動かすきっかけのことを「トリガー」と呼ぶ。銃でイメージすると,「引き金を引く」ことがトリガーであり,それによって「銃弾が発射される」ことがイベントに相当する。
イベント | 何かをきっかけに動き始める処理 |
トリガー | イベントを動かすきっかけとなるもの |
イベントグラフ | 「ブループリント」を開くと表示されるタブ このタブを押すと,ブループリント用のエディタ「グラフエディタ」が表示され,その中の「グラフエリア」で編集作業を行える ※本記事では,用語による混乱を避けるため,ブループリントの編集画面も「イベントグラフ」と,まとめて表現する [操作方法] ・移動:右ドラッグ ・拡大/縮小:マウスホイール |
ブループリント | ブロック状にまとまった機能を組み合わせることで,ゲームの処理実装するシステム |
ノード | 「ジャンプ」「横移動」「四則演算」「表示」など,さまざまな処理をそれぞれブロック状にしたもの 各ノードは「○○ノード」と表記する 例.「Jumpノード」 ノードの種類によって色が異なり,「イベント」は赤色で示される |
上記画像には,赤色と青色のブロックがあるが,この各ブロックがそれぞれの機能を持つ「ノード」である。
基本的に赤いノードは「イベント」であり,何かをきっかけにして動作する。
「EnhancedInputAction IA_Jumpノード」は,Spaceバーを押した/離した際に「トリガー」される「イベント」としてテンプレートに実装されている。
Spaceバーを押した時に「Jumpノード」,Spaceバーを離した時に「Stop Jumpingノード」へとそれぞれ進む。
処理は呼び出されない限り実行されないので,基本的に何かを実装する時は「イベントノード」(赤いノード)から始まり,そこに動かしたい処理の「ノード」を接続していくと覚えておこう。
以下は,本記事の内容から少し脱線しているので,興味のある人向けの補足だ。
「Jumpノード」は文字通り「ジャンプ」なのだが,「Stop Jumpingノード」,つまり「ジャンプを止める」とは何だ,と気になる人もいるだろう。
実はジャンプと言っても,実際にキャラクターが上にあがる処理と,ジャンプのアニメーションはまったく関係のない別物である。キャラクターが足に力を入れてジャンプしていても,実際に足から地面に力を加えて上に飛んでいるのではなく,システム側から力を加えている(正確に言うと「垂直方向へ速度を与える処理」をしている)のだ。
この力を加える処理が「Jumpノード」の役割であり,アニメーションはキャラクターの状態をもとに,別の処理で付け足すように再生されている。
また,パラメータを変更することで「上昇し続ける時間」を変えることができる。「Stop Jumpingノード」は,この上昇し続ける処理を中断するものだ。
勘のいい人は気づいたかもしれないが,ジャンプボタンを押した長さによって,ジャンプ力が変わるような処理を実装する際に「Stop Jumpingノード」が活躍する。
ジャンプボタンを短く押す,つまり,「ボタン離す」トリガーによって「Stop Jumpingノード」がすぐに呼び出され,上昇する処理を中断し,低いジャンプ(小ジャンプ)を実現できる。
なお,テンプレートのキャラクターは,ジャンプで「上昇し続ける時間の最大値」(JumpMaxHoldTime)が0.0になっているため,Spaceバーを長押ししてもジャンプ力の違いは確認できない。
開発中は「このノード,何で置いたんだっけ」と作業内容を忘れることがあるので,コメントを残す癖を付けていこう。
(1)ノードの左上にマウスカーソルを合わせ,吹き出しを押す |
(2)入力ボックスが出現する |
(3)メモ書きを残していこう |
コメント | プログラムなどの中に残すメモ書きのこと ファイルの名前の付け方で「命名規則」があるように,コメントにも規則や流派があったりする 本記事では,各ノードに「ノード自体の説明」をコメントで入れていることが多い 慣れてくると「ノード自体の説明」は不要になりがちだが,初心者のうちはコメントの練習にもなるので,気が向いたら自分なりにメモを残してみてほしい |
ピン | 処理やデータを繋ぐ「点」 「○」や野球のホームベースのような形などをしている [実行ピン] ・処理を繋いでいくピン [データピン] ・データのやり取りを行うピン ・データの種類によって色が異なる |
ワイヤー | 処理やデータを繋ぐ「線」 ワイヤーで繋いだ処理は,左から右へ流れるように進む 繋いだワイヤーは,Altキーを押しながら,ワイヤーを左クリックすれば削除可能 [実行ワイヤー] ・処理を繋いだワイヤー ・白色で表現される [データワイヤー] ・データを繋いだワイヤー ・データの種類によって色が異なる |
「弾」がヒットした時だけに処理を変更するには,ヒットしたものが「弾」であるか,確かめる処理を実装する必要がある。
テンプレートの「弾」は「BP_FirstPersonProjectile」というクラスを利用しているため,当たったものが「BP_FirstPersonProjectileクラス」であれば「弾」であり,そうでなければ「別のオブジェクト」だと判別ができる。
この判別は「Cast To BP_FirstPersonProjectileノード」で実装可能だ。
画像のようにノードを配置することで,プレイヤーが「的」にぶつかっても「Hello」と表示されなくなった。
Cast To BP_FirstPersonProjectileノード | ||
「Objectピン」に接続されたものが「BP_FirstPersonProjectileクラス」を持っていたら,上の実行ピンへ,そうでないなら下の「Cast Failed」と書かれた実行ピンへ進むノード 次章にて似たようなノードを別の使い方で使用するため,詳しい説明は後ほど行う |
なお,現状では,同じ「的」に複数回弾がヒットしたら,その分だけ「Hello」が表示される。
このまま「的」にスコア処理を追加したら,同じ「的」を何度も撃って無限にスコアを稼げてしまう。処理を1回だけする「Do Once」というノードを追加し,スコアの荒稼ぎを阻止しよう。
Do Onceノード | ||
「Completedピン」(右の実行ピン)に接続された処理の実行を,1度だけにするノード 左側の実行ピンに「Reset」と書かれたピンがある これに何らかのノードからワイヤーを繋いで処理を流すと,「Do Onceノード」がリセットされ,「Completedピン」に接続された処理を,再び実行できる状態になる そして,右の処理を1度したら,再びリセットされるまで右の処理をしない |
「Do Onceノード」は必ず,「Cast To BP_FirstPersonProjectileノード」の後に配置しよう。
もし前に設置してしまうと,プレイヤーがぶつかった時も「Do Onceノード」が動作し,それ以降,銃で撃っても処理が動かないので注意だ。
弾が当たった際の処理を,1回だけにする(1回は必ず実行できるようにする)ことが重要である。
「Hello」の表示に続けて,「的」が撃たれた時の変化を加えてみよう。ヒットエフェクトは,FPSの気持ちよさに直結する。
まずは,「的」を撃ち「落とせる」ようにしてみよう。撃たれたタイミングで「的」の物理演算を有効にすれば,空中にある「的」は自由落下する。
物理演算と聞いて「何かさっき触ったような気がする」,という人がいれば鋭い。前のページで,レベル上にある青い箱について説明した際に,物理演算は「Simulate Physics」というオプションで有効/無効を切り替えられることを説明した。
つまり,撃たれたタイミングで「Simulate Physics」を有効にするだけで実装完了だ。
まずヒット音を実装しよう。今回は「Play Sound at Locationノード」を利用する。このノードは,指定した位置(座標)で音を鳴らすことができる。遠くで鳴らせば音は小さく,近くで鳴らせば大きく聞こえる。
今回は「的」が撃たれたら,「的」の座標でヒット音を鳴らす処理を実装する。オブジェクトの座標を取得するには,「Get Actor Locationノード」を使う。このノードで取得した座標を「Play Sound at Locationノード」に伝えれば,「的」の位置で音を鳴らせる。
(1)「Play Sound at Locationノード」を追加 |
(2)「Sound」のプルダウンメニューから,「Explosion_Cue」を指定 |
(3)空いている場所を右クリックして,「Get Actor Locationノード」を追加 |
(4)座標のデータは黄色で表現されている。図のように,黄色のデータピン同士を接続しよう |
なお「Sound」を選択する際,「サウンドウェーブ」と「サウンドキュー」の2種類が表示されるだろう。前者は音源となるwavファイルをそのままアセット化したもので,それを編集して再生するものが後者だ。
今回選択した「Explosion_Cue」は,サウンドウェーブ「Explosion01」と「Explosion02」のうち一方を,ランダムで再生する処理をしている。
この状態でプレイすると,弾を当てた時に「的」から爆発音がなる。
とはいえ銃の発射音が大きくてスピーカーだと分かりにくいので,目に見えるエフェクトも付けよう。スターターコンテンツには「爆発エフェクト」も用意されているので,これを「的」の位置で発生させることで,エフェクトを表示するようにする。
使用するのは「Spawn Emitter at Locationノード」だ。先ほどのヒット音の実装と同じく,「的」の位置を伝える必要がある。
Spawn Emitter at Locationノード | ||
位置を指定して,エフェクトをレベル上に表示するノード 「Emitter Template」に指定したエフェクトを表示でき,その位置は「Location」で指定する ほかにも「Rotation」で向き,「Scale」で大きさを設定可能 |
(1)「Spawn Emitter at Locationノード」を追加 |
(2)「Emitter Template」のプルダウンメニューから,「P_Explosion」を指定 |
(3)「Get Actor Locationノード」の「Return Valueピン」から,「Locationピン」にワイヤーを接続する |
(4)「的」を撃つと,爆発エフェクトが表示されるようになった |
なお,本記事では「Spawn Emitter at Locationノード」を利用した方法について紹介した。実のところ,これは少し古いシステムで,現在主流の「Niagara」というシステムのエフェクトを使う場合は,別のノードを使うことになる。
名前を紹介すると,「Spawn System at Locationノード」だ。名前が似ているように,ノードの使い方が大きく変わることはないので安心してほしい。
Spawn System at Locationノード | ||
「Niagara VFX System」(外部リンク)で作成したエフェクトを,レベル上に表示するノード |
Niagara VFX System |
UE5で標準のエフェクトシステム ベースとなるエフェクトの「Niagara エミッタ」と,それらを組み合わせて管理する「Niagara システム」の2つで構成される エフェクトを細かく素材(アセット)にして管理できるため,再利用性が高いなど,「Cascade」よりも高性能なエフェクトシステムである 「Spawn System at Locationノード」の名前に含まれる「System」は,「Niagara システム」のことだ |
Cascade Particle Systems |
UE5以前に主に使われていたエフェクトシステム 「Niagara」で「エミッタ」「システム」に相当するものが,1つのアセットで構成される UE5から非推奨となり,廃止が予定されている なお,UE5.3.2時点では,レガシー(旧型)システムとして残っている |
今後,ゲーム開発をしていく上で,スターターコンテンツ以外の素材を試す機会があるだろう。その際,エフェクトの表示は「別のノードを使うかもしれない」とだけ覚えておいてほしい。
本章の内容をすべて行った例を以下に示す。
・BP_SimpleTarget
- 物理演算処理の設定
- ヒットしたオブジェクトの判定
- 処理の重複対策(一度だけ処理)
- Actorの位置取得
- 音の3D空間再生
- 爆発エフェクトの表示
- 関連タイトル:
Unreal Engine
- この記事のURL: