自分好みにカスタマイズ
Coddyの「Unityゲーム:タワーディフェンス」コースのレッスン 18/18。
ゲームにはエンディングが必要です。ライフが0になると負け、すべてのウェーブが送り出され、ボード上に敵が残っていなければ勝ちです。Spawnerは最後のウェーブが送り出されたことを把握しており、Enemy.Allは残っている敵の数を把握しているため、Spawnerはボードが空になるまで待ち、その後、GameManagerが監視するイベントを発生させます。コルーチンはWaitUntilを使って条件を待ちます。これはラムダ式をフレームごとに1回チェックします。すべてのスイッチがオンになると出口が開くパズルルームの例です:
yield return new WaitUntil(() => switchesOn == switches.Length);
exit.SetActive(true);どちらのエンディングでも、メッセージを表示したEndPanelが現れ、Time.timeScale = 0でゲームが停止します。ライフが0を下回ることはありません。残り1ライフのときにTankが2ダメージを受けると、ちょうど0かどうかのチェックを通り過ぎてしまうため、Mathf.Maxでライフを0以上に保ちます。
Play Againを押すと、シーンが再読み込みされます。Time.timeScaleはシーンの一部ではなく、シーンを読み込んでも値が維持されるため、ゲームが0で停止していると、再開後も停止したままになります。読み込む前に1に戻しましょう。
次に、ゲームを自分好みに調整しましょう。ゲームバランスに関わる設定はすべてInspectorにあります:
- GameのGameManager:
startMoney(150)とstartLives(20)。 - Spawnerの
waves:敵の種類、数、間隔を指定する8行と、timeBetweenWaves(8)。 - EnemyDataアセット:速度、体力、報酬、および各種類の敵が減らすライフ数。Bossは体力が450で、ライフを10減らします。
- TowerDataアセット:コスト、ダメージ、射程、連射速度、アップグレード時の数値、Cannonの範囲攻撃、Frostタワーのスロウ効果。
初期設定の数値なら、道のカーブに複数種類のタワーを配置し、お金が増えるにつれてアップグレードすればクリアできます。同じ種類のタワーを少数置くだけでは持ちこたえられません。数値を一度に1つずつ変更して、プレイしながらその影響を確かめましょう。
チャレンジ
難しいWaveSpawnerにpublic event Action AllWavesClearedを追加します。最後のウェーブを送り出した後、Enemy.Allが空になるまで待ち、その後イベントを発生させます。GameManagerに、シリアライズされたフィールドendPanel(GameObject)とendText(TMP_Text)を追加します。ライフが 0 未満にならないようにし、0 になったらGame Overでゲームを終了します。ライフが残っている状態で全ウェーブをクリアしたら、You Win!で終了します。終了時にはメッセージとともにパネルを表示し、Time.timeScaleを 0 に設定します。- パネルのボタンから呼び出す
public void PlayAgain()を記述します。時間を通常に戻し、アクティブなシーンを再度読み込みます。
Play を押し、8 つすべてのウェーブを通して基地を守り、その後 Check を押します。その後、Inspector でゲームを調整して、自分のやり方でプレイしましょう。
自分で試してみよう
using UnityEngine;
using TMPro;
public class GameManager : MonoBehaviour
{
[SerializeField] private int startMoney = 100;
[SerializeField] private int startLives = 20;
[SerializeField] private WaveSpawner spawner;
[SerializeField] private TMP_Text moneyText;
[SerializeField] private TMP_Text livesText;
[SerializeField] private TMP_Text waveText;
private int money;
private int lives;
void OnEnable()
{
Enemy.ReachedBase += OnReachedBase;
Enemy.Killed += OnKilled;
spawner.WaveStarted += ShowWave;
}
void OnDisable()
{
Enemy.ReachedBase -= OnReachedBase;
Enemy.Killed -= OnKilled;
spawner.WaveStarted -= ShowWave;
}
void Start()
{
money = startMoney;
lives = startLives;
ShowMoney();
ShowLives();
}
public bool TrySpend(int amount)
{
if (amount > money)
{
return false;
}
money -= amount;
ShowMoney();
return true;
}
public void Earn(int amount)
{
money += amount;
ShowMoney();
}
void OnKilled(Enemy enemy)
{
Earn(enemy.data.reward);
}
void OnReachedBase(Enemy enemy)
{
lives -= enemy.data.damage;
ShowLives();
}
void ShowMoney()
{
moneyText.text = "Money: " + money;
}
void ShowLives()
{
livesText.text = "Lives: " + lives;
}
void ShowWave(int wave, int total)
{
waveText.text = "Wave " + wave + "/" + total;
}
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。