タワーデフェンスゲーム　Unity タレット機能  9/10　ダメージ被弾時ダメージ量を表示する

# 概要
目次

https://qiita.com/RYA234/items/c9925ce07d6afe64be7e


今回は敵がダメージを受けた時、ダメージ量が表示されるように実装します。
以下gifアニメは実装後の様子です。被弾時にダメージ量が表示されていることを確認できます。

![animationresult.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710623/69a48caf-db18-a8bd-96a3-a6272626d458.gif)

# 開発環境

IDE:Rider
Unity:2020.3.42(LTS)
OS:Windows10

# UnityEditor上の設定
## DamageTextCanvas（プレハブ）
uGuiでダメージ量を表示するUIを作成します。　
animatorと DamageTextスクリプトをアタッチします。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710623/a11c89bd-9b9c-c70c-d845-a604a779f65d.png)


以下アニメーションを動かしているときの様子

![animation.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710623/f7cc3005-b7c0-07b6-1a69-4443e47e010f.gif)


## DamageTextManager(ヒエラルキー)
DamageTextManagerとObjectPoolerをアタッチします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710623/9c10be2c-b262-36b7-4137-bbae3246b9c9.png)


## EnemyObject
EnemyFxスクリプトをアタッチします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710623/6a82f9d2-5ad0-ceec-97ce-9274636de470.png)


# 実装のポイント
Observerパターンを使います。ObservaleはProjectileクラスでObserverはEnemyFxです。

新規クラスはEnemyFx DamageText DamangeTextManagerです。
EnemyFxはダメージ表示機能のメイン部分です。
DamageTextはプレハブ内のテキストコンポーネントを取得するために作成します。
DamageTextManagerは同じオブジェクトにアタッチされているObjectPoolerのデータを取得しています。これはDamageTextManager経由でEnemyFxがObjectPoolerにアクセスするための処置です。



# コード部分
## EnemyFX
```diff_c_sharp:EnemyFx.cs
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;

public class EnemyFX : MonoBehaviour
{
    [SerializeField] private Transform textDamageSpawnPosition;
    private Enemy _enemy;
    
    void Start()
    {
        _enemy = GetComponent<Enemy>();
    }

    private void EnemyHit(Enemy enemy, float damage)
    {
        if (_enemy == enemy)
        {
            GameObject newInstance = DamageTextManager.Instance.Pooler.GetInstanceFromPool();
            TextMeshProUGUI damageText = newInstance.GetComponent<DamageText>().DmgText;
            damageText.text = damage.ToString();
            
            newInstance.transform.SetParent(textDamageSpawnPosition);
            newInstance.transform.position = textDamageSpawnPosition.position;
            newInstance.SetActive(true);
        }
    }

    private void OnEnable()
    {
        Projectile.OnEnemyHit += EnemyHit;
    }
    
    private void OnDisable()
    {
        Projectile.OnEnemyHit -= EnemyHit;
    }
}



```

## DamageText
```diff_c_sharp:DamageText.cs
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;

public class DamageText : MonoBehaviour
{
    public TextMeshProUGUI DmgText => GetComponentInChildren<TextMeshProUGUI>();

}



```

## DamageTextManager
```diff_c_sharp:DamageTextManager.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class DamageTextManager : MonoBehaviour
{
    
    public ObjectPooler Pooler { get; set; }

    public static DamageTextManager Instance;

    private void Awake()
    {
        Instance = this;
    }
    // Start is called before the first frame update
    void Start()
    {
        Pooler = GetComponent<ObjectPooler>();
    }
    
}



```

## Projectile
```diff_c_sharp:Projectile.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Projectile : MonoBehaviour
{
+    public static Action<Enemy, float> OnEnemyHit;
    [SerializeField] private float moveSpeed = 10f;
    private Enemy _enemyTarget;
    [SerializeField] private float damage = 2f;
    [SerializeField] private float minDistanceToDealDamage = 0.1f;
    
    public TurretProjectTile TurretOwner { get; set; }
    private float _nextAttackTime;
    
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        if (_enemyTarget != null)
        {
            MoveProjecttile();
            RotateProjetile();
        }
        
    }

    private void MoveProjecttile()
    {
        transform.position = Vector2.MoveTowards(transform.position,_enemyTarget.transform.position,moveSpeed * Time.deltaTime);
        float distanceToTarget = (_enemyTarget.transform.position - transform.position).magnitude;
        if (distanceToTarget < minDistanceToDealDamage)
        {
+            OnEnemyHit?.Invoke(_enemyTarget, damage);
            _enemyTarget.EnemyHealth.DealDamage(damage);
            TurretOwner.ResetTurretProjectile();
            ObjectPooler.ReturnToPool(gameObject);
            
        }
    }

    private void RotateProjetile()
    {
        Vector3 enemyPos = _enemyTarget.transform.position - transform.position;
        float angle = Vector3.SignedAngle(transform.up, enemyPos, transform.forward);
        transform.Rotate(0f,0f,angle);
    }

    
    public void SetEnemy(Enemy enemy)
    {
        _enemyTarget = enemy;
        
    }
    public void ResetProjectile()
    {
        _enemyTarget = null;
        transform.localRotation = Quaternion.identity;
    }    
    
}



```


# 参考




Section5 27

https://www.udemy.com/course/learn-how-to-create-a-2d-tower-defense-game-in-unity-2020/learn/lecture/23141888#overview


github コミット分

https://github.com/RYA234/2DTowerDeffenceMyLearning/commit/00d750368a37073213e7938ac1f9860119648846#diff-e80e1f4ae9a84513f8ddb10ee7a71e7bd4de60b340982ae5e8a12d159da7e0f3