Unity初心者が2Dタワーディフェンスゲームを制作中です。
今回は防衛拠点に敵が到達したら拠点のHPが減り、HPが0になったらゲームオーバーになるという処理を実装します。
- Mac mini (M1, 2020)
- Unity 2022.3.36f1
トップバーのUIを作る
まず、ゲーム画面の上部に表示されるUIを作ります。完成形はこんな感じになります。

ヒエラルキーのCanvasオブジェクト上で右クリックして「UI > Panel」を選択します。Canvasの配下にPanelオブジェクトが作成されるので、名前を「TopBar」にします。
TopBarがゲーム画面の上部に配置されるように「Rect Transform」を調整します。

「Image > Source Image」をNoneに、「Color」を透明にして、TopBarが見えなくなるようにします。
TopBarに「Horizontal Layout Group」コンポーネントを追加します。これは、子オブジェクトを横並びに整列させるためのコンポーネントです。
以下のように「Child Alignment(子オブジェクトの整列)」をMiddle Centerにして、左右の「Padding」と「Spacing(子オブジェクト同士の間隔)」を4にしました。

TopBarの配下に各オブジェクトを作成します。
- TopBar
- Stage: ステージ情報を入れておくためのオブジェクト。色は透明。
- StageLabel: 「STAGE」というラベルを表示するためのTextMeshProオブジェクト。
- StageText: ステージ番号を表示するためのTextMeshProオブジェクト。
- FortressHp: 城のHP情報を入れておくためのオブジェクト。色は白の半透明。
- FortressHpIcon: アイコンを表示するためのImageオブジェクト。
- FortressHpLabel: 「HP」というラベルを表示するためのTextMeshProオブジェクト。
- FortressHpSlider: 城の残りHPを表示するためのSliderオブジェクト。
- FortressHpSliderScale: HPバーの目盛りをあらわすImageオブジェクトです。バーを20等分に区切る縦線の画像です。
- Gold: お金の情報を入れておくためのオブジェクト。色は白の半透明。
- GoldIcon: アイコンを表示するためのImageオブジェクト。
- GoldLabel: 「GOLD」というラベルを表示するためのTextMeshProオブジェクト。
- GoleText: 現在の所持金額を表示するためのTextMeshProオブジェクト。
- Score: スコアの情報を入れておくためのオブジェクト。色は白の半透明。
- ScoreIcon: アイコンを表示するためのImageオブジェクト。
- ScoreLabel: 「SCORE」というラベルを表示するためのTextMeshProオブジェクト。
- ScoreText: 現在のスコアを表示するためのTextMeshProオブジェクト。
- Wave: ウェーブの情報を入れておくためのオブジェクト。色は白の半透明。
- WaveIcon: アイコンを表示するためのImageオブジェクト。
- WabeLabel: 「WAVE」というラベルを表示するためのTextMeshProオブジェクト。
- WaveText: 現在のウェーブ番号を表示するためのTextMeshProオブジェクト。
- TimeToNextWaveText: 次のウェーブまでの残り時間を表示するためのTextMeshProオブジェクト。
- NextWaveButton: 次のウェーブを発生させるためのボタン。
- NextWaveButtonText: ボタンのテキスト。
- StartPauseButton: ゲームをスタート/ポーズするためのボタン。
- StartPauseButtonText: ボタンのテキスト。
- Stage: ステージ情報を入れておくためのオブジェクト。色は透明。
オブジェクトの角を丸くするためにShapes2Dというアセットを使いました。無料です。
- アセットストアのShapes2Dのページに行き「Add to My Assets」ボタンをクリックします。
- UnityのPackage Managerでインポートします。
- 角を丸くしたいオブジェクトに「Shape(Shapes2D)」コンポーネントを追加します。
- インスペクターで「Shape (Script) > Roundness」の値を設定します。
防衛拠点(城)を作る
防衛拠点となる城のオブジェクトをマップ上に配置します。
城の画像(fortress.png)を「Assets > Images > Map」ディレクトリにインポートします。
このfortress.pngをヒエラルキーにドラッグ&ドロップすると簡単にオブジェクトを作成することができます。

名前は大文字で始まるFortressに修正しておきました。
ヒエラルキーの「Sprite Renderer > Additional Settings > Sorting Layer」を「Object」にします。これで城が最前面に表示されるようになりました。
「Transform > Position」を調整して、城が敵の移動経路のゴール地点に配置されるようにします。

敵が城に到達したときに当たり判定をしたいので、FortressオブジェクトにBox Collider 2Dコンポーネントを追加します。
物理的な挙動はせずに当たり判定だけしたいので、「Is Trigger」にチェックを入れます。
敵が防衛拠点(城)に到達したらHPを減らす
敵が防衛拠点(城)に到達したらHPを減らすスクリプトを書いていきます。
FortressControllerスクリプトを新規作成します。
using UnityEngine;
using UnityEngine.UI;
public class FortressController : MonoBehaviour
{
[SerializeField, Header("最大HP")] private int maxHp = 20; // 防衛拠点の最大HP
[SerializeField, Header("現在のHP")] private int currentHp; // 防衛拠点の現在のHP
[SerializeField, Header("HP表示スライダー")] private Slider hpSlider; // HPを表示するスライダー
private void Start()
{
// ゲーム開始時にHPを最大値に設定
currentHp = maxHp;
// HPスライダーを更新
UpdateHpSlider();
}
/// <summary>
/// 敵が防衛拠点に接触したときの処理
/// </summary>
private void OnTriggerEnter2D(Collider2D collision)
{
// 敵と接触した場合
if (collision.TryGetComponent(out EnemyController enemy))
{
// ダメージを受ける
TakeDamage();
// 敵にゴール到達を通知する
if (enemy != null)
{
enemy.ReachedGoal();
}
}
}
/// <summary>
/// ダメージを受ける
/// </summary>
public void TakeDamage()
{
// HPを1減らす
currentHp--;
// HPスライダーを更新
UpdateHpSlider();
// HPが0以下になったらゲームオーバー
if (currentHp <= 0)
{
GameOver();
}
}
/// <summary>
/// HPスライダーを更新する
/// </summary>
private void UpdateHpSlider()
{
// スライダーの最大値を設定
hpSlider.maxValue = maxHp;
// スライダーの値を現在のHPに設定
hpSlider.value = currentHp;
}
/// <summary>
/// ゲームオーバー処理
/// </summary>
private void GameOver()
{
// ゲームオーバー処理を記述
Debug.Log("ゲームオーバー");
}
}
FortressControllerスクリプトをFortressオブジェクトにドラッグ&ドロップしてアタッチします。
Fortressオブジェクトのインスペクターで、「Fortress Controller (Script) > Hp Slider」にFortressHpSliderオブジェクトをドラッグ&ドロップしてアサインします。

EnemyControllerスクリプトにメソッドを追加します。
// ...省略...
// ここから
/// <summary>
/// 敵がゴールに到達した
/// </summary>
public void ReachedGoal()
{
DestroyEnemy(); // 敵を破壊する
}
// ここまで
// ...省略...
これでゲームを実行すればうまくいくと思ったのですがダメでした。Geminiに頼っても解決せず、100万年悩んだ末にChatGPTに相談したところあっさりと解決。ChatGPTのほうが賢いかも。解決策は次のSTEPで。
「Assets > Prefabs > Enemy」をダブルクリックしてプレハブの編集モードに入ります。そして、EnemyオブジェクトにRigidbody 2Dコンポーネントを追加します。
「Gravity Scale」を「0」にして重力を無効化します。

というわけで、当たり判定をするには城と敵のどちらかにRigidbodyをつける必要があるのでした。
動作確認
ゲームを実行して動作確認をします。
敵が城に接触すると、敵が消えて城のHPは減るようになりました。いえい。
さいごに
今回、新しいことは何もしていないんですけど、Rigidbodyのところで引っ掛かったのが悔しいです!
でわでわ
コメント