DoTweenPRO / 상태 변화 코드 간소화

유니티 에셋스토어 링크

https://assetstore.unity.com/packages/tools/visual-scripting/dotween-pro-32416?aid=1101l7zGS

 

DOTween Pro | 비주얼 스크립팅 | Unity Asset Store

Get the DOTween Pro package from Demigiant and speed up your game development process. Find this & other 비주얼 스크립팅 options on the Unity Asset Store.

assetstore.unity.com

가격

15달러 / 무료버전도 있음.

무료버전과 유료버전의 차이는 TextMeshPro 텍스트애니메이션 가능여부 + 애니메이션 프리뷰가능한 컴포넌트 제공

 

어떤 에셋인가요?

DoTween은 'A'에서 'B'로 상태(포지션, 컬러, 스케일 등)를 변화시키는 동작을 수행할 때

사용될 수 있는 플러그인 이다.

무료버전과의 차이는 인스펙터에서 사용될 수 있는 애니메이션 컴포넌트와 TextMeshPro용 타이핑효과 등이다.

필자는 TextMeshPro를 사용하기 때문에 Pro를 사용 중.

 

개인 평가

이제는 유니티 개발자라면 필수에셋이라고 불러도 과언이 아닌 에셋이다.
워낙 널리 사용되고 있으니, 레퍼런스도 많다. 

 

주의할 점?

아직까지는 별도의 불편한점 없음.

 

대표적으로 0.5초 동안 캔버스의 투명도를 0에서 1로 바꿔야한다면,

Update문이나 코루틴으로 작성해야하니, 코드가 최소 4-5 줄은 나올 수 있다.

 

하지만 DoTween으로 작성하면 아래와 같이 한 줄로 끝난다.

 

 

캔버스 그룹 페이드

_CanvasGroup.DOFade(1, 0.5f);

 

트윈 중지

transform.DOKill();
CanvasGroup.DOKill(); // 등과 같이 DOTween을 실행한 컴포넌트를 직접 호출.
DoTween.KillAll(); // 은 존재하는 모든 오브젝트의 DOTween을 중지하니 주의
DoTween.Kill(this); // 해당 오브젝트의 DoTween 중지.

 

타임스케일

SetUpdate (true) 를 붙여주면 Time.timeScale에 영향을 안받음.

.SetUpdate (true); 

 

완료 콜백

transform.DOMoveX(4, 1).OnComplete(MyCallback);

 

코루틴 완료 대기

yield return myTween.WaitForCompletion();

코루틴 시간 대기

yield return myTween.WaitForCompletion(); // 끝날때 까지 대기

yield return myTween.WaitForPosition(duration); // 해당 시간까지 대기 normalizetime이 아님에 주의!

시퀀스 생성....

Sequence seq = DOTween.Sequence(); // new 가아니다.

여러번 사용한다면 Start 에서 생성 및 캐싱해서 사용하자.

추가로 Pause()를 붙여줘야 생성하자마자 실행안된다.

 

시퀀스 재사용....

시퀀스는 사용후 자동으로 삭제되는데, SetAutoKill(false)을 붙여주면 삭제가 안되어 재사용이 가능하다.

Sequence.SetAutoKill(false);

 

 

Loop / UI에서 위아래로 움직이는 화살표 이미지

 

Sequence를 미리 생성해 두는데,

Awake에서는 DoTween 전역 매니저가 생성되지 않았기 때문에

반드시 Start()에서 호출함에 주의하자.

 

using DG.Tweening;
using UnityEngine;

public class LoopAnimTween : MonoBehaviour
{
    public float halfMoveAmount = 0.1f;
    public float halfMoveTime = 1f;
    Sequence sequence;

    private void Start()
    {
        sequence = DOTween.Sequence();

        RectTransform l_rectTransform = transform.GetComponent<RectTransform>();
        float l_centerY = l_rectTransform.anchoredPosition.y;

        l_rectTransform.anchoredPosition3D = l_rectTransform.anchoredPosition3D + new Vector3(0f, -halfMoveAmount, 0f);
        sequence.Append(l_rectTransform.DOAnchorPos3DY(l_centerY + halfMoveAmount, halfMoveTime).SetEase(Ease.InOutQuad));
        sequence.Append(l_rectTransform.DOAnchorPos3DY(l_centerY - halfMoveAmount, halfMoveTime).SetEase(Ease.InOutQuad));
        sequence.SetLoops(-1);
        sequence.Play();
    }

    private void OnEnable()
    {
        sequence.Restart();
    }

    private void OnDisable()
    {
        sequence.Pause();
    }
}

 

 

Loop 애니메이션 2

 

 

라인렌더러의 Width 바꾸기

LineRenderer l_LineRenderer = GetComponent<LineRenderer>();
DOTween.To(() => l_LineRenderer.widthMultiplier, x => l_LineRenderer.widthMultiplier = x, 1f, 0.2f);

 

UI 애니메이션 스크립트

더보기
using DG.Tweening;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class UITween : MonoBehaviour
{
    public enum AnimType
    {
        Fade,
        Scale,
    }

    public AnimType AnimationType = AnimType.Fade;
    public bool CloseDisable = true;

    CanvasGroup _CanvasGroup;
    CanvasGroup CanvasGroup
    {
        get
        {
            if (_CanvasGroup == null)
                _CanvasGroup = GetComponent<CanvasGroup>();

            return _CanvasGroup;
        }
        set
        {
            _CanvasGroup = value;
        }

    }

    void OnEnable()
    {
        if (AnimationType == AnimType.Fade)
        {
            CanvasGroup.alpha = 0f;
            CanvasGroup.DOFade(1, 0.3f)
                .OnComplete(OnOpenComplete)
                .SetUpdate (true); 
        }
        else if(AnimationType == AnimType.Scale)
        {
            transform.localScale = new Vector3(0f, 0f, 0f);
            transform.DOScale(new Vector3(1f, 1f, 1f), 0.3f)
                .SetEase(Ease.OutBack)
                .OnComplete(OnOpenComplete)
                .SetUpdate(true);
        }
    }

    
    private void OnDisable()
    {
       transform.DOKill();
       CanvasGroup.DOKill();
    }

    void OnOpenComplete()
    {

    }

    public void Close()
    {
        if (AnimationType == AnimType.Fade)
        {
            CanvasGroup.alpha = 1f;
            CanvasGroup.DOFade(0, 0.2f)
               .OnComplete(OnCloseComplete)
                .SetUpdate(true);
        }
        else if (AnimationType == AnimType.Scale)
        {
            transform.localScale = new Vector3(1f, 1f, 1f);
            transform.DOScale(new Vector3(0f, 0f, 0f), 0.2f)
               .SetEase(Ease.InBack)
               .OnComplete(OnCloseComplete)
               .SetUpdate(true);
        }
    }

    void OnCloseComplete()
    {
        if(CloseDisable)
            gameObject.SetActive(false);
    }
}

 

UI 오브젝트에 직접 붙이는 스크립트 (템플릿 메서드 패턴)

더보기
using DG.Tweening;
using UnityEngine;

public class TweenAnim : MonoBehaviour
{
    public enum AnimType
    {
        Fade,
        Scale,
    }

    public AnimType AnimationType = AnimType.Fade;

    ITweenAnimSub tweenAnimSub;

    [SerializeField] float openTime = 0.3f;
    [SerializeField] float closeTime = 0.2f;
    [SerializeField] bool CloseDisable = true;

    CanvasGroup canvasGroup;

    void Awake()
    {
        canvasGroup = GetComponent<CanvasGroup>();

        if (AnimationType == AnimType.Fade)
        {
            tweenAnimSub = FadeAnim.instance;
        }
        else if (AnimationType == AnimType.Scale)
        {
            tweenAnimSub = ScaleAnim.instance;
        }
    }

    void OnEnable() => tweenAnimSub.OnEnable(this);

    void OnDisable() => tweenAnimSub.OnDisable(this);

    void OnOpenComplete()
    {
    
    }

    public void Close()
    {
        tweenAnimSub.Close(this);
    }

    void OnCloseComplete()
    {
        if (CloseDisable)
            gameObject.SetActive(false);
    }

    [Button]
    public void AddCanvasGroup() => gameObject.AddComponent<CanvasGroup>();

    interface ITweenAnimSub
    {
        void OnEnable(TweenAnim tweenAnim);
        void Close(TweenAnim tweenAnim);
        void OnDisable(TweenAnim tweenAnim);
    }


    /// <summary>
    /// 트윈 애니메이션을 정의하는 내부 클래스
    /// </summary>
    class FadeAnim : ITweenAnimSub
    {
        public static FadeAnim instance = new FadeAnim();

        public void OnEnable(TweenAnim tweenAnim)
        {
            tweenAnim.canvasGroup.alpha = 0f;
            tweenAnim.canvasGroup.DOFade(1, tweenAnim.openTime)
                .OnComplete(tweenAnim.OnOpenComplete)
                .SetUpdate(true);
        }

        public void Close(TweenAnim tweenAnim)
        {
            tweenAnim.canvasGroup.alpha = 1f;
            tweenAnim.canvasGroup.DOFade(0, tweenAnim.closeTime)
               .OnComplete(tweenAnim.OnCloseComplete)
                .SetUpdate(true);
        }

        public void OnDisable(TweenAnim tweenAnim)
        {
            tweenAnim.canvasGroup.DOKill();
        }
    }

    class ScaleAnim : ITweenAnimSub
    {
        public static ScaleAnim instance = new ScaleAnim();

        public void OnEnable(TweenAnim tweenAnim)
        {
            tweenAnim.transform.localScale = Vector3.zero;
            tweenAnim.transform.DOScale(Vector3.one, tweenAnim.openTime)
                .SetEase(Ease.OutBack)
                .OnComplete(tweenAnim.OnOpenComplete)
                .SetUpdate(true);
        }

        public void Close(TweenAnim tweenAnim)
        {
            tweenAnim.transform.localScale = Vector3.one;
            tweenAnim.transform.DOScale(Vector3.zero, tweenAnim.closeTime)
               .SetEase(Ease.InBack)
               .OnComplete(tweenAnim.OnCloseComplete)
               .SetUpdate(true);
        }

        public void OnDisable(TweenAnim tweenAnim)
        {
            tweenAnim.transform.DOKill();
        }
    }
}

 

호출을 위해 만든 St.cs 전역함수 내용

더보기
    public static void CallClose(GameObject a_obj)
    {
        CallClose(a_obj.transform);
    }
    public static void CallClose(Transform a_transform)
    {
        if (a_transform.gameObject.activeInHierarchy == false)
            return;

        UITween l_UITween = a_transform.GetComponent<UITween>();

        if (l_UITween != null)
            l_UITween.Close();
        else
            Debug.Log(a_transform.name + " 오브젝트에 UITween없음");
    }

    public static void CallCloseAnim(GameObject a_obj)
    {
        CallAnimClose(a_obj.transform);
    }
    public static void CallAnimClose(Transform a_transform)
    {
        if (a_transform.gameObject.activeInHierarchy == false)
            return;

        Animator l_Animator = a_transform.GetComponent<Animator>();

        if (l_Animator != null)
            l_Animator.SetTrigger("Close");
    }
    
    public static void ChildCallClose(GameObject a_obj)
    {
        ChildCallClose(a_obj.transform);
    }
    public static void ChildCallClose(Transform a_transform)
    {
        foreach (Transform child in a_transform)
        {
            if (child.gameObject.activeInHierarchy)
                CallClose(child);
        }
    }

    public static void ChildSetActive(GameObject a_obj, bool a_active)
    {
        ChildSetActive(a_obj.transform, a_active);
    }
    public static void ChildSetActive(Transform a_transform, bool a_active)
    {
        foreach (Transform child in a_transform)
        {
            child.gameObject.SetActive(a_active);
        }
    }

 

 

댓글

Designed by JB FACTORY