ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [TIL] 16일차 5일 유니티 데미지 텍스트 만들기 ( 제네릭 오프젝트 풀, DOTween )
    개발일지/스파르타 코딩클럽 부트캠프 2024. 8. 2. 20:12

     

    유니티 2D 데미지 텍스트 띄우기

     

    제네릭 오브젝트 풀 만들기

     

    일단 데미지 텍스트도 그냥 Instantiate와 Destroy를 하면 당연히 가비지 컬렉터가 쌓여서

    게임 최적화에 좋지 않다.

    그래서 오브젝트 풀을 만드는데

    여러가지 타입을 받아서 사용할 수 있게 제네릭 오브젝트 풀을 만들었다.

    구글링은 위대하다.

     

    public class OBJPool<T> : MonoBehaviour where T : Component
    {
        [SerializeField] protected T[] mOrigin;
    
        protected List<T>[] mPool;
    
        private void Start()
        {
            mPool = new List<T>[mOrigin.Length];
            for (int i = 0; i < mOrigin.Length; ++i)
            {
                mPool[i] = new List<T>();
            }
        }
    
        public T GetFromPool(int id = 0)
        {
            for(int i = 0; i < mPool[id].Count; ++i)
            {
                if (!mPool[id][i].gameObject.activeInHierarchy)
                {
                    mPool[id][i].gameObject.SetActive(true);
                    return mPool[id][i];
                }
            }
            return MakeNewInstance(id);
        }
    
        protected virtual T MakeNewInstance(int id)
        {
            T newObj = Instantiate(mOrigin[id]);
            mPool[id].Add(newObj);
            return newObj;
        }
    }
    public class DamageTextPool : OBJPool<Text>
    {
    
    }

     

    이렇게 그냥 제네릭 오브젝트 풀을 상속받은 DamageTextPool을 하나 만들어준다.

     

    그리고 빈 오브젝트에 스크립트를 추가해주고

    Text 오브젝트 풀 리스트를 만들어준다.

    오브젝트 풀 클래스에서 객체가 부족하면 생성해준다.

    하지만 초기 개수도 중요하다 오브젝트 풀의 최종 목적은 최적화이며,

    그 목적중에 런타임 대신 로딩 시간에 객체 생성을 몰아두는 것도 있기 때문에

    초기에 값을 알맞게 설정해놔야 오브젝트풀의 역할을 제대로 할 수 있다.

     

    public class HealthSystem : MonoBehaviour
    {
        public event Action OnDamage;
    
        public bool ChangeHealth(float change)
        {
            if (isInvincibility && change <= 0)
            {
                return false;
            }
    
            if (timeSinceLastChange < healthChangeDelay && transform.CompareTag("Player"))
            {
                return false;
            }
    
            timeSinceLastChange = 0f;
            CurrentHealth += change;
            // 최솟값을 0, 최댓값을 MaxHealth로 하는 구문.
            CurrentHealth = Mathf.Clamp(CurrentHealth, 0, MaxHealth);
            // CurrentHealth = CurrentHealth > MaxHealth ? MaxHealth : CurrentHealth;
            // CurrentHealth = CurrentHealth < 0 ? 0 : CurrentHealth; 와 같다
    
    
            if (CurrentHealth <= 0f)
            {
                CallHealthChanged();
                CallDeath();
                return true;
            }
    
            if (change >= 0)
            {
                OnHeal?.Invoke();
            }
            else
            {
                OnDamage?.Invoke();
                if(this.tag == "Player")
                {
                    CreateDamageText(new Vector3(this.gameObject.transform.position.x,
                                                this.gameObject.transform.position.y + playerTextYNum,
                                                this.gameObject.transform.position.z), change);
                }
                else
                {
                    CreateDamageText(new Vector3(this.gameObject.transform.position.x + UnityEngine.Random.Range(-.5f, .5f),
                                                this.gameObject.transform.position.y + UnityEngine.Random.Range(2, 4),
                                                this.gameObject.transform.position.z), change);
                }
            }
    
            CallHealthChanged();
    
            return true;
        }
        
            public void CreateDamageText(Vector3 pos, float damage)
        {
            Text dmgTxt = GameManager.Instance.damageTextPool.GetFromPool(UnityEngine.Random.Range(0, 4));
            damageTextUI = dmgTxt.GetComponent<DamageTextUI>();
            damageTextUI.Init(dmgTxt, pos, damage, Color.red, damagefontSize);
        }
    }

    HealthSystem의 일부분만 가져왔는데

     

    이 스크립트는 Player와 Enemy모두가 공통으로 사용하는 클래스여서

    여기서 데미지 텍스트를 관리해준다.

    Player와 나머지 몬스터들의 피벗이 약간씩 다르기 때문에 포지션을 약간 조정해줬다.

    체력이 깎여서 CreateDamageText 함수가 발동하면 damageTextUI의 Init 메서드를 실행해준다.

    using UnityEngine;
    using UnityEngine.UI;
    using TMPro;
    using System.Collections;
    using DG.Tweening;
    
    public class DamageTextUI : MonoBehaviour
    {
        public Camera mainCamera;
    
        private WaitForSeconds TextDestroyTime = new WaitForSeconds(3);
    
        public void Init(Text damageTxt, Vector3 pos, float amount, Color color, float size)
        {
            damageTxt.transform.SetParent(UIManager.Instance.worldCanvas.transform , false);
            damageTxt.transform.position = pos;
            damageTxt.text = Mathf.Abs(Mathf.RoundToInt(amount)).ToString();
            damageTxt.color = color;
            StartCoroutine(TextMover(damageTxt.gameObject, damageTxt, pos));
        }
    
        private IEnumerator TextMover(GameObject damageTxtOb, Text damageTxt, Vector3 pos)
        {
            damageTxt.transform.DOMove(new Vector3(pos.x + 1f, pos.y + 1f, pos.z), 2f).SetEase(Ease.OutSine);
            damageTxt.DOFade(0f, 2f);
            yield return TextDestroyTime;
            damageTxtOb.SetActive(false);
        }
    
    }

    그리고 마지막으로 텍스트에 넣어둔 DamageTextUI에서

    하이어라키 창의 월드캔버스에 넣어주고, 텍스트 변경, 색 변경 그리고 애니메이션 코루틴을 실행한다.

    TextMover 코루틴에서는 DOTween을 사용해서 이동과 FadeOut을 조정해준다.

Designed by Tistory.