Unity初心者が2Dタワーディフェンスゲームを制作しております。
今回は、敵を攻撃したときや城が攻撃されたときのビジュアルエフェクトを追加していきます。これでゲーム画面がグッと華やいでお客様の満足度も急上昇するのではないでしょうか。
- Mac mini (M1, 2020)
- Unity 2022.3.36f1
- Cartoon FX Remaster Free R 1.5.0
敵が攻撃されたときに動きを止める
敵が攻撃されたときに一瞬動きを止める処理を追加したいと思います。
EnemyControllerスクリプトを修正します。
using UnityEngine;
using DG.Tweening;
using System.Linq;
// ここから追加
using System.Collections; // IEnumerableを使用するために必要
// ここまで
public class EnemyController : MonoBehaviour
{
// ...省略...
/// <summary>
/// ダメージを受ける
/// </summary>
/// <param name="damageAmount">ダメージ量</param>
public void TakeDamage(int damageAmount)
{
if (currentHp <= 0) return; // すでに死亡していたら何もしない
// HPの値を減算した結果値を、最小値と最大値の範囲内に収まるようにして更新
currentHp = Mathf.Clamp(currentHp - damageAmount, 0, maxHp);
// HPバーの更新
if (hpBar != null)
hpBar.UpdateHpBar(currentHp);
// ここから追加
StartCoroutine(StopMoveForSeconds(0.1f));
// ここまで
if (currentHp <= 0)
{
// HPが0以下になったら敵を破壊
DefeatEnemy();
}
}
// ここから追加
/// <summary>
/// 一時的に移動を停止する
/// </summary>
/// <param name="seconds">停止する秒数</param>
private IEnumerator StopMoveForSeconds(float seconds)
{
if (moveTween != null && moveTween.IsPlaying())
{
moveTween.Pause();
yield return new WaitForSeconds(seconds);
if (moveTween != null && !moveTween.IsPlaying())
{
moveTween.Play();
}
}
}
// ここまで
// ...省略...
}
敵の移動を一時的に停止するコルーチンStopMoveForSeconds()
を追加して、TakeDamage()
内で呼び出します。
StopMoveForSeconds()
では、moveTween
を一時停止(Pause
)して、指定時間後に再生(Play
)しています。moveTween
には敵が経路に沿って移動するDOTweenの処理が入っています。
Cartoon FX Remaster Freeを使う
さあ、このゲームにもっとクールな華やぎをもたらすために、アセットを導入しましょう。Cartoon FX Remaster Freeです。安心してください、無料です。

Cartoon FX Remaster Freeのインポート
Unityエディタのメニューの「Window > Package Manager」を選択して、Package Managerを開きます。
My AssetsのCartoon FX Remaster Freeを選択して、右上の「Download」ボタンでダウンロードし、「Import」ボタンでインポートします。

Import Unity Packageウインドウが表示されたら「Import」ボタンをクリックします。

インポートが完了すると以下のウェルカムウインドウが開くので「Close and don’t show again」ボタンを押して閉じます。「Close」ボタンを押すと、Unityエディタを起動するたびにこのウインドウが開きます。お好みでどうぞ。

インポートに成功すると「Assets > JMO Assets」フォルダが作成されます。
「JMO Assets > Cartoon FX Remaster > CFXR Prefabs」にエフェクトのプレハブが入っています。プレハブをダブルクリックすると、Sceneタブでどんなエフェクトなのか確認できます。
敵がダメージを受けたときのエフェクト
敵がダメージを受けたときのエフェクトをCartoon FX Remaster Freeを使って実装します。
EnemyControllerスクリプトを修正します。
public class EnemyController : MonoBehaviour
{
// ...省略...
// ここから追加
[SerializeField, Tooltip("ダメージエフェクトのプレハブ")] private GameObject damageEffectPrefab;
// ここまで
// ...省略...
/// <summary>
/// ダメージを受ける
/// </summary>
/// <param name="damageAmount">ダメージ量</param>
public void TakeDamage(int damageAmount)
{
if (currentHp <= 0) return; // すでに死亡していたら何もしない
// HPの値を減算した結果値を、最小値と最大値の範囲内に収まるようにして更新
currentHp = Mathf.Clamp(currentHp - damageAmount, 0, maxHp);
// HPバーの更新
if (hpBar != null)
hpBar.UpdateHpBar(currentHp);
// ここから追加
// ダメージエフェクトを表示
if (damageEffectPrefab != null)
{
// エフェクトを敵の位置に生成
GameObject effect = Instantiate(damageEffectPrefab, transform.position, Quaternion.identity);
Destroy(effect, 2f); // エフェクトの長さに応じて調整
}
// ここまで
StartCoroutine(StopMoveForSeconds(0.1f));
if (currentHp <= 0)
{
// HPが0以下になったら敵を破壊
DefeatEnemy();
}
}
// ...省略...
}
- エフェクトのプレハブを入れる変数
damageEffectPrefab
を定義します。 TakeDamage()
内で、ダメージエフェクトを表示します。- プレハブからエフェクトを生成。
- 2秒後にエフェクトを削除。
damageEffectPrefab
にプレハブをアサインします。
Enemyプレハブをダブルクリックしてプレハブの編集モードに入ります。インスペクターの「Enemy Controller (Script) > Damage Effect Prefab」に「JMO Assets > CFXR Prefabs > Misc > CFXR3 Hit Misc A」をアサインします。

以上で、敵が攻撃されたときにエフェクトが表示されるようになったのですが、エフェクトが最前面に表示されません。表示の重なり順を調整する必要がありそうです。
「JMO Assets > CFXR Prefabs > Misc > CFXR3 Hit Misc A」をダブルクリックしてプレハブの編集モードに入ります。
インスペクターの「Particle System > Renderer > Sorting Layer ID」を「Object」に変更します。子オブジェクトのSorting Layer IDも同様に変更します。

ちなみに、Sorting Layerは以下の3層構造になっています。
- Default: 奥。草原を配置。
- Way: 中。道と障害物を配置。
- Object: 手前。砲台や敵を配置。
エフェクトの大きさを調整します。
これは「Transform > Scale」でいけました。

Cartoon FX Remaster Freeのエフェクトは、実行時にカメラを揺らす効果がついているものが多いのですが、敵が攻撃されるたびに揺れるのは鬱陶しいので無効化します。
「CFRX_Effect (Script) > Camere Shake > Enabled」のチェックを外します。

「Shake Strength」の値を0にしてもOKです。ということは、この値を大きくすると揺れが大きくなるっちゅーことですね。
敵が撃破されたときのエフェクト
敵が撃破されたときのエフェクトを実装します。ダメージを受けたときのエフェクトとほぼ同じ手順です。
EnemyControllerスクリプトを修正します。
public class EnemyController : MonoBehaviour
{
// ...省略...
// ここから追加
[SerializeField, Tooltip("撃破エフェクトのプレハブ")] private GameObject defeatEffectPrefab;
// ここまで
// ...省略...
/// <summary>
/// 敵を撃破する
/// </summary>
private void DefeatEnemy()
{
// ここから追加
// 撃破エフェクトを表示
if (defeatEffectPrefab != null)
{
GameObject effect = Instantiate(defeatEffectPrefab, transform.position, Quaternion.identity);
Destroy(effect, 2f); // エフェクトの長さに応じて調整
}
// ここまで
// ...省略...
}
// ...省略...
}
- エフェクトのプレハブを入れる変数
defeatEffectPrefab
を定義します。 DefeatEnemy()
内で、撃破エフェクトを表示します。- プレハブからエフェクトを生成。
- 2秒後にエフェクトを削除。
defeatEffectPrefab
にプレハブをアサインします。
Enemyプレハブをダブルクリックしてプレハブの編集モードに入り、インスペクターの「Enemy Controller (Script) > Defeat Effect Prefab」に「JMO Assets > CFXR Prefabs > Explosions > CFXR Explosion 1」をアサインします。
CFXR Explosion 1プレハブをダブルクリックして、インスペクターで以下の設定を修正します。
- 表示の重なり順の設定。「Particle System > Renderer > Sorting Layer ID」をObjectに変更。子オブジェクトも同様に。
- 「Transform > Scale」で、エフェクトの大きさの調整。
- カメラを揺らす効果の無効化。「CFRX_Effect (Script) > Camere Shake > Enabled」のチェックを外す。
城がダメージを受けたときのエフェクト
城がダメージを受けたときのエフェクトを実装します。
FortressControllerスクリプトを修正します。
public class FortressController : MonoBehaviour
{
// ...省略...
// ここから追加
[SerializeField, Tooltip("ダメージエフェクトのプレハブ")] private GameObject damageEffectPrefab;
// ここまで
// ...省略...
/// <summary>
/// ダメージを受ける
/// </summary>
public void TakeDamage()
{
// HPを1減らす
currentHp--;
// HPスライダーを更新
UpdateHpSlider();
// ここから追加
// ダメージエフェクトを表示
if (damageEffectPrefab != null)
{
Vector3 effectPos = transform.position + new Vector3(0, -0.5f, 0); // エフェクトの位置を調整
GameObject effect = Instantiate(damageEffectPrefab, effectPos, Quaternion.identity);
Destroy(effect, 2f); // エフェクトの長さに応じて調整
}
// ここまで
// HPが0以下になったらゲームオーバー
if (currentHp <= 0)
{
GameOver();
}
}
// ...省略...
}
- エフェクトのプレハブを入れる変数
damageEffectPrefab
を定義します。 TakeDamage()
内で、ダメージエフェクトを表示します。- エフェクトの位置を少し下にずらす。
- プレハブからエフェクトを生成。
- 2秒後にエフェクトを削除。
デフォルトのエフェクトの表示位置が気に入らなかったので半マス分下にずらしました。最初、エフェクトプレハブの「Transform > Position」で設定を変更しようとしたのですが、うまくいかなかったのでスクリプトで変更しました。なんでだろう。
damageEffectPrefab
にプレハブをアサインします。
Map1プレハブをダブルクリックしてプレハブの編集モードに入り、子オブジェクトのFortressのインスペクターの「Fortress Controller (Script) > Damage Effect Prefab」に「JMO Assets > CFXR Prefabs > Explosions > CFXR2 WW Explosion」をアサインします。
CFXR2 WW Explosionプレハブをダブルクリックして、インスペクターで以下の設定を修正します。
- 表示の重なり順の設定。「Particle System > Renderer > Sorting Layer ID」をObjectに変更。子オブジェクトも同様に。
- 「Transform > Scale」で、エフェクトの大きさの調整。
- カメラを揺らす効果は、そのまま残します。
Map2〜5のFortressにもエフェクトのプレハブをアサインします。
ゲームの初期化時にエフェクトを削除
ゲーム終了時に表示されていたエフェクトは、次のゲーム開始時にも残ったままになってしまうので、ゲームの初期化メソッドInitializeGame()
にエフェクトを削除する処理を追加します。
GameManagerスクリプトを修正します。
public class GameManager : MonoBehaviour
{
// ...省略...
/// <summary>
/// ゲーム全体の初期化(スコア・ゴールド・城HP・敵・砲台などのリセット)
/// </summary>
private void InitializeGame()
{
// スコア初期化
if (ScoreManager.Instance != null)
ScoreManager.Instance.ResetScore();
// ゴールド初期化
if (GoldManager.Instance != null)
GoldManager.Instance.ResetGold();
// 城HP初期化
if (FortressController.Instance != null)
FortressController.Instance.ResetHp();
// 敵の生成をリセット
var spawner = FindObjectOfType<EnemySpawner>();
if (spawner != null)
spawner.ResetSpawner();
// 砲台の設置情報をリセット
var turretGenerator = FindObjectOfType<TurretGenerator>();
if (turretGenerator != null)
turretGenerator.ResetTurretPlacements();
// tileHighlighterを非表示
if (tileHighlighter != null)
tileHighlighter.SetActive(false);
// マップ上の敵・HPバー・砲台・インジケータ削除
foreach (var enemy in GameObject.FindGameObjectsWithTag("Enemy"))
Destroy(enemy);
foreach (var hpBar in GameObject.FindGameObjectsWithTag("HpBar"))
Destroy(hpBar);
foreach (var turret in GameObject.FindGameObjectsWithTag("Turret"))
Destroy(turret);
foreach (var indicator in GameObject.FindGameObjectsWithTag("Indicator"))
Destroy(indicator);
// ここから追加
foreach (var effect in GameObject.FindGameObjectsWithTag("Effect"))
Destroy(effect);
// ここまで
}
// ...省略...
}
このゲームで使用するエフェクトのプレハブに「Effect」タグを設定します。全部で3つあります。

動作確認
動作確認をします。いい感じですね。
さいごに
はい、ほぼ完成ですね。次回はBGMと効果音をつけたいと思います。
でわでわ
コメント