🍑 타이머

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class Timer : MonoBehaviour
{
    private Coroutine curTimer;
    
    public void StartTimer(int time, Action<float> onValueChanged)
    {
        //진행되던 타이머가 있다면 멈추고 새로 시작
        if (curTimer != null)
        {
            StopCoroutine(curTimer);
        }
        curTimer = StartCoroutine(CoStartTimer(time, onValueChanged));
    }
    //타이머를 진행할 시간, 시간이 바뀔때마다 어떠한 행동을할건지
    IEnumerator CoStartTimer(int time, Action<float> onValueChanged)
    {
        float timer = 0;
        while (true)
        {
            //타이머가 지정된 시간을 넘기면 break!
            if (timer >= time)
            {
                break;
            }
            //시간을 더해주자!
            timer += Time.deltaTime;
            yield return null;
            //남은 시간을 보내주기
            onValueChanged(time - timer);
        }
        //타이머가 끝나면 코루틴을 null
        curTimer = null;
    }
}

💚 deltaTime 사용 이유!

지난 프레임이 완료되는 데 까지 걸린 시간을 나타내며, 단위는 초를 사용합니다.(읽기전용) 사용자의 프레임 률(frame rate)을 독립적으로 적용하기 위해서 사용합니다.

즉! 사용자의 각자 프레임률이 다르기 때문에 프레임 카운트로 타이머를 만든다면 각 사용자의 타이머는 시간이 모두 다르다. 하지만 deltaTime을 쓴다면, 같은 결과값을 얻을 수 있다!

여기서, 매 프레임마다 실행되는 update와 달리 coroutine을 사용했는데 내용이 적용이 될까? 라는 의문이 들 수 있다. yield return null 을 사용함으로 update바로 다음에 실행이 되도록 하였다! 즉, update에서 사용하는 것과 동일하게 사용이 가능하다.

실행순서

🍑 스킬아이콘

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
public class SpellIcon : MonoBehaviour
{
    //연출에 사용할 커브!
    [SerializeField] private AnimationCurve ease;
    //스킬 아이콘 이미지
    private Image img_icon;
    //쿨타임이 돌 때 이미지(딤드)
    private Image img_cooltime;
    //쿨타임을 표시해 줄 텍스트
    private Text txt_timer;
    //타이머를 가지고 있자!
    private Timer timer;
    //임시 쿨타임 적용
    private int cooltime = 5;

    private void Awake()
    {
        foreach (var img in GetComponentsInChildren<Image>())
        {
            if (img.name == "Image_Spell")
            {
                img_icon = img;
            }
            else if (img.name == "Image_CoolTime")
            {
                img_cooltime = img;
            }
        }
        
        var btn = img_icon.gameObject.AddComponent<Button>();
        btn.onClick.AddListener(OnClickSpell);

        timer = GetComponent<Timer>();
        txt_timer = GetComponentInChildren<Text>();
    }
    
    //바깥에서 불러줘야해!
    //useAble : 바로 사용이 가능한지?
    public void Initialize(Sprite sprite, int cooltime, bool useAble = true)
    {
        img_icon.sprite = sprite;
        this.cooltime = cooltime;
        //바로사용이 불가능하다면 쿨타임을 실행!
        OnTimeChanged(useAble ? cooltime : 0);
    }
    
    void UseSpell()
    {
        img_cooltime.raycastTarget = true;
        timer.StartTimer(cooltime, OnTimeChanged);
    }
    void OnClickSpell()
    {
        //어디론가 보내서 스킬을 발동되도록 해야겠지 ?
        Debug.Log("아 스킬 발동ㅋ");
        ClickAnimation();//연출!
        UseSpell();
    }

    void ClickAnimation()
    {
        //클릭시 커졌다가 다시 돌아오는 핑퐁형태의 연출을 추가!
        transform.Scale(Vector3.one, Vector3.one * 1.1f, 0.1f).SetEase(ease,true).Play();
    }
    //타임이 변경될 때 마다 업데이트를 시켜줌
    void OnTimeChanged(float time)
    {
        UpdateCooltimeImage(time);
        UpdateTimerText(time);
    }
    void UpdateCooltimeImage(float time)
    {
        if (time <= 0)
        {
            //쿨타임이 끝났으니 아이콘을 누를 수 있도록 해당 이미지를 raycastTarget을 꺼줌.
            img_cooltime.raycastTarget = false;
        }
        if (cooltime > 0)
        {
            img_cooltime.fillAmount = time / cooltime;
        }
        else
        {
            img_cooltime.fillAmount = 0;
        }
    }
    void UpdateTimerText(float time)
    {
        txt_timer.text = time <= 0 ? string.Empty :  txt_timer.text = Math.Ceiling(time).ToString();
    }
}

결과

스킬 아이콘을 초기화해주고 정보를 넣어주는 것은 원래 해당 클래스 바깥에서 관리를 해주어야 할 것이다. 이 스킬 아이콘들의 상태를 관리하고 이벤트들을 실제로 사용하는 곳으로 전달하는 역할의 매니저 클래스가 필요하다. 다음에 이 클래스를 관리하는 클래스도 추가해보겠다.

이 클래스는 실 데이터(스킬의 데미지, 타겟, 효과 등…)를 다루거나 그 데이터를 기반으로 행위를 하는 것보다 사용자의 눈에 보여지는 부분만 처리하기 위해 존재한다.(UI적 부분만)