💘

テンプレ通りのゲーム制作 FPS⑦耐久力と攻撃力の概念

2024/12/09に公開

耐久力と攻撃力のパラメータを持たせる

前回はランダムな位置にスポーンさせ、プレイヤーに向かって飛んでくる処理を扱いました。その際に「継承」の概念を使って親クラスを決めてそこから子クラスに「継承」する手法を扱いました。今は弾丸がターゲットにヒットすると一発で破壊されますが、耐久力を持たせて破壊しづらいターゲットを持たせましょう。前回の記事はこちらです。

https://zenn.dev/yukumoto/articles/e7d06e5e9e43cd

いわゆる「堅い」耐久力のあるターゲットと「もろい」耐久力の低いターゲットを作ります。耐久力の概念はどちらにも必要なので親クラスをまずは修正します。
コンテンツ/Blueprintsの「BP_TargetBase」を開きます。

左側の変数の項目のプラスボタンを押して変数を作ります。Integer型にして「HP」(ヒットポイント)という変数名にします。コンパイルして詳細パネル下部のデフォルト値を「10」にしておきます。
Int型の変数HPを作る

コンパイルしてデフォルト値を設定
つぎに攻撃を与える弾の方にも攻撃力を設定します。
コンテンツ/FirstPerson/Blueprintsの「BP_FirstPersonProjectile」を開きます。
ターゲット同様に攻撃力の情報が入った変数を用意します。左側の変数の項目のプラスボタンを押して変数を作ります。Integer型にして「AP」(アタックポイント)という変数名にします。コンパイルして詳細パネル下部のデフォルト値を「10」にしておきます。
Int型の変数APを作る
コンパイルしてデフォルト値を設定

耐久力を減らす

それぞれの変数にパラメータを入れたら、ヒットしたときに耐久力を減らすカスタムイベントを用意します。BP_TargetBaseに戻ります。BPエディタ上で空いているところを右クリックしAdd Custom Event...を選びカスタムイベントを作ります。名前は「ReceiveDamage」にします。

つぎに受けるダメージ量をカスタムイベントに伝えるために詳細パネルのインプット項目のプラスボタンを押してデータの型をInteger型にします。名前は「DamagePoint」にします。インプットはプログラミングにおいて「引数」と呼ばれるものです。カスタムイベントは関数と同じく処理をまとめたものですが、カスタムイベントを呼び出す際にデータを渡す窓口になります。
カスタムイベントで引数を受け取る
では、カスタムイベントの中の処理を記述していきましょう。HP変数をカスタムイベントの近くにドラッグしてきてGetノードを作ります。Getノードからワイヤを伸ばしてテンキーなどの「-(マイナス)」キーを押します。Subtract(引き算)を選びます。HP変数からダメージ量をコレから引いていきます。

次にカスタムイベントのインプットからSubtractノードの下段のピンにつないで「引く数」とします。引かれる数であるHP変数から引く数であるインプットのDamagePointを引く形になりますね。最後にその計算結果をHPのセットノードを作り、次の画像のように接続してセットします。

これでHPを減らすことが出来ました。次にダメージを受けた後にHPが0以下ならDestroyActorして自分自身を消します。この処理を書いていきましょう。セットしたHPの出力ピンからワイヤを伸ばして「<」キーを押し「<=(Less Equal)」を選びます。赤ピンからワイヤをさらに伸ばし「Branch」と検索してBranchノードを作ります。最後に実行ピンをつなぎ、BranchノードのTrueのピンからワイヤを伸ばしてDestroyActorノードを作ります。画像のようにつなぎます。

なおBranchノードの上部の「...」をクリックして出来たテキストボックスに「のこりのHPが0以下か?」と記入しておきます。これはコメント行と言っていわゆる覚え書きです。プログラムが複雑になってくるとどういう処理か分からなくなるのでこのようにコメントを入れておくと後から思い出しやすくなります。これでHPの残りが0以下なら自分自身(BP_TargetBase)を破壊して消すという処理が出来ました。

ヒット時にカスタムイベントを呼び出す

では、ターゲット側に作ったカスタムイベントを弾がヒットしたときに呼び出しましょう。
BP_FirstPersonProjectileをもう一度開きます。このFPSテンプレートのコラムの第1回「テンプレ通りのゲーム制作 FPS⓵当たったら消える的を作る」の中でEvent Hitノードに最後にDestroyActorを追加したと思いますがこれを探して選択ののち、Delキーを押して削除します。
最後のDestroyActorを削除する
削除したDestroyActorに変わってターゲットの親クラスであるBP_TargetBaseクラスへのキャストノードを作ります。「キャスト」とは一時的に他のブループリントクラスの中身にアクセスする仕組みです。削除したDestroyActorのひとつ前のDestroyActorの実行ピンからワイヤを伸ばしてCast To BP_TargetBaseノードを作ります。左のObjectピンにはEvent HitのOtherから伸びているRerouteノードをつなぎます。キャストがうまくいけば先ほど親クラスに作ったカスタムイベントにアクセスできます。青ピンからReceive Damageカスタムイベントを呼び出します。カスタムイベントにはインプットを指定していますので弾のAP変数のGetノードを作ってつなぎましょう。画像のようにノードを組みます。
キャストしてカスタムイベントを呼び出している
これで再生してみましょう。
以前と同様にブロックが撃ち落とせる
ゲーム的には見た目は以前と変わらりませんが、内部的にはHPを削って残りゼロなら破壊しています。

耐久力のあるターゲットを作る

さて、これでターゲットに耐久力を持たせて弾がヒットすると攻撃力に応じて減らしていく仕組みが出来ました。この仕組みは親クラスに持たせました。親クラスを継承して今度は耐久力のある堅い別のターゲットを作りましょう。BP_TargetBaseを右クリックして子ブループリントクラスを作成しますを選びます。名前を「BP_TargetCylinder」にします。作成したブループリントクラスを開き、コンポーネントパネルからStaticMeshを選択、詳細パネルのStatic Mesh項目からShape_Cylinderを選びます。トランスフォーム項目で拡大・縮小をXYZすべて2.0にします。南京錠マークを押しておくと等比拡大が出来ます。

StaticMeshをCylinderに差し替える
2.0に拡大しておきます
さらにブループリントエディタからBeginPlayノードを探し、ワイヤを伸ばしてSet HPノードを作ります。HPのテキストボックスに100を入れておきます。つまり耐久力10倍です。親クラスではデフォルト値として耐久力10が設定されていますが、子クラスではHPの初期値を再設定することができます。

設定出来たらコンパイルして保存し、レベルエディタにうつってBP_TargetCylinderをレベルに配置しましょう。配置出来たらCylinderに攻撃してみましょう。

弾にはじかれて飛んで行ってしまう…
確かに弾が一発当たったくらいでは破壊されず10発当たって壊れるようになったのですが、どうも弾にはじかれて飛んで行ってしまいますね。見た目に反して軽い物体のように見えます。これを防ぐたびにCylinderを重くします。BP_TargetCylinderを開き、コンポーネントパネルからStaticMeshを選択して詳細パネルの検索ボックスに「mass」と入力します。massとは「質量」を意味します。物理項目のMass(kg) にチェックを入れ10000.0を入れます。10000㎏なのでつまり10トンの重さを設定しました。プレイしてみましょう。
Cylinderが重くなった
どうでしょうか?Cylinderが飛ばなくなった筈です。ただ、爆発が小さいですね。もうひと手間かけて爆発もCylinderのスケールに合わせましょう。BP_TargetBaseを開いてEvent Destroyedの内容を以下の画像のようにします。爆風を発生させるSpawn Emitter at LocationのScaleピンにGet Actor Scale 3Dをつないで位置だけでなくスケールも取得しています。

爆風も大きくなった!
これでサイズなりの爆発の大きさになったはずです。

光るマテリアルを用意する

BP_TargetCylinderに弾をあててもイマイチ当たっている感覚がないので当たった瞬間に光るようにしていきましょう。ヒット時に光るようにしたいので、光った時のマテリアルを作ります。マテリアルを格納するフォルダをコンテンツフォルダを右クリックして作りましょう。今回はコンテンツ/Materialsという名前のフォルダを作っています。このフォルダの中でさらに右クリックしてマテリアルを選んで作成します。名前を「M_Flash」にします。



作成したマテリアルをダブルクリックします。マテリアルのエディタ画面が開きます。マテリアルもノードベースで作成することができます。左上にサンプル球がありますのでこれを確認しながら作っていきます。

左の詳細パネルの中からShading Modelという項目を探し、プルダウンをUnlitにします。Unlitとは「Un Lighting」(ライティングなし)という意味で、ライティングの影響を受けない事を表します。自己発光するランプなどのマテリアルに最適なシェーディングモデルです。

次に発行する色を作ります。シェーディングモデルをUnlitに変更したことによりエディタ内中央のマテリアルの結果ノードの多くの項目がグレーアウトされていると思います。残ってる項目の中にエミッシブカラーという項目があるので、ピンから左へワイヤを伸ばし、Constant3Vectorを選びます。

Constant3VectorはRGB(RED GREEN BLUE)の3つの要素を調整して色を作るノードです。黒い資格をダブルクリックしてカラーピッカーを開き、青白い色を画像のように作ってOKボタンを押します。

これで青いマテリアルが作れましたがこれだとまだ光ってはいません。エミッシブカラーのピンからもう一度左にドラッグしてMultiply(掛け算)ノードを作ります。このノードはカラーを増幅することができます。Bピンの方は「かける数」になりますので5.0という数値を入れてみます。準備が出来たらエディタ上部の適用ボタンを押して保存します。

サンプル球がかすかに光るようになりました。後ほど適宜好みの色と発光量にしてみてください。

ヒット時にマテリアルを入れ替える

マテリアルを用意出来たところでヒット時にこのM_Flashマテリアルに差し替える処理を作りましょう。BP_TargetBaseを開いてReceiveDamageカスタムイベントを探します。カスタムイベントの最後にBranchノードでのこりHPをチェックしていますがFalse側に画像のようにSet Materialノードを組みます。ターゲットにStaticMeshをドラッグしてきてつなぎます。False側に流れる条件は「のこりHPが残っている」という事です。
このノードが組めたらテストしてみましょう。
ヒットしたら光った!
Cylinderが光ったはずです。が、光りっぱなしです。これではちょっと困りますね。0.何秒か経ったら元に戻す仕組みを次に作っていきましょう。

元のマテリアルに戻す

現在のマテリアルを最初に保存しておき、一定時間経ったら元の戻す仕組みを作ります。プログラム開始時に実行されるBeginPlayノードを作りましょう。BeginPlayノードの右側にGet Materialノードを作ります。ターゲットにStaticMeshをつなぎます。

Get MaterialのReturnValueからワイヤを出してPromote to variable(変数へ昇格)を選びます。マテリアルインターフェース型の変数が出来るので左の変数項目でNew Varから「BaseMat」に名前を変えます。

最終的には以下の画像のようになります。これでプログラム開始時にこのクラスは変数に現在のマテリアルを記憶しておきます。

つぎに残りHPをチェックするBranchノードのFalse側で光ったマテリアルに変更された後、SetTimerノードを設置して記憶しておいたマテリアルに戻す処理を書きます。画像のようになります。SetTimerのEventピンからワイヤを伸ばし、AddCustomEventでカスタムイベントを設置し、そこからSetMaterialで戻しています。

これでテストしてみましょう。
元のマテリアルに戻らない!
発光は一瞬で消えるようになりました。ただ、元のゴールドのマテリアルに戻らず白くなってしまいました(うまくいく場合もあります)。
【マテリアルが元の戻らない場合の対処法】
光った後、白くなってしまう場合はうまくマテリアルが保存できていないようです。回避策があるので組み替えていきましょう。画像のようにBeginPlayノードに繋がっているノードを選んでCTRL+Xキーを押してカットします。

カットしたら上部のタブの中でConstruction Scriptというタブを押して切り替えます。このタブはコンストラクションスクリプトと言って、BeginPlayよりさらに先にゲーム実行時に構築されるスクリプトです。

コンストラクションスクリプトが開いたらCTRL+Vを押してBeginPlayノードでカットしたものをペーストし、下記の画像のようにつなぎ直します。

テストしてみましょう。
光って元マテリアルと交互に表示された!
これでうまくマテリアルを戻せたと思います。このように処理をゲームが構築される(コンストラクション)前に実行するべき場合があることを覚えておきましょう。

Discussion