상세 컨텐츠

본문 제목

Unity Study_015 HudText, Coroutine

C#/수업내용

by McRobbin 2020. 5. 26. 12:25

본문

HudText

 

 

이와같이 UIHudText 프리팹 작성. 빈 오브젝트 안에 띄울 text를 가지고 있음.

UIHudText는 UIHudText.cs Script를 가지고 있음.

 

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
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using DG.Tweening;
 
public class UITest1 : MonoBehaviour
{
    public UIHudText uiHudTextPrefab;
 
    public void Init()
    {
 
    }
 
    public void ShowHud(Vector2 position, int value)
    {
        var hudGo = Instantiate<UIHudText>(this.uiHudTextPrefab);
        hudGo.Init(position, value);
        hudGo.transform.localPosition = new Vector3(position.x, position.y);
        hudGo.transform.SetParent(this.transform, false);
 
        hudGo.transform.DOScale(2.0f, 0.5f).OnComplete(() =>
        {
            hudGo.transform.DOScale(0.5f, 0.5f).OnComplete(() =>
            {
 
            });
        });
 
 
        hudGo.transform.DOLocalMoveY(position.y + 170f, 0.5f).OnComplete(() =>
        {
            hudGo.transform.DOLocalMoveY(position.y + 70f, 0.5f).OnComplete(() =>
            {
                Destroy(hudGo.gameObject);
            });
        });
    }
 
}
 
cs

UITest1.cs 임.

 

Init()에 따로 내용은 없고 UIHudText 프리팹만 가지고 있게 했음.

ShowHud 호출시 UIHudText 동적 생성하고 Init()해주고, 위치 옮기고 등등 후에

해당 오브젝트 삭제!! 좌표찾기, 랜덤값 넣기 등등은 Test1.cs에 있음.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
 
public class UIHudText : MonoBehaviour
{
    public Text hudText;
 
    public void Init(Vector2 position, int value)
    {
        this.transform.localPosition = new Vector3(position.x, position.y);
        this.hudText.text = value.ToString();
    }
}
 
cs

UIHudText.cs

ㅇㅇ.. 위치와 값 세팅이 전부인 클래스.

 

 

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
using JetBrains.Annotations;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class Test1 : MonoBehaviour
{
 
 
    public UITest1 uiTest1;
    public Hero hero;
    public Hero monster;
 
    private void Awake()
    {
        this.uiTest1.Init();
    }
 
    private void Start()
    {
        this.hero.onArrived = (() =>
        {
            if (this.hero.coroutine != null)
                StopCoroutine(this.hero.coroutine);
            this.hero.coroutine = this.hero.Attack();
            StartCoroutine(this.hero.coroutine);
        });
 
        this.hero.onAttackEnd = (() =>
        {
            if (this.hero.coroutine != null)
                StopCoroutine(this.hero.coroutine);
            this.hero.coroutine = this.hero.Move();
            StartCoroutine(this.hero.coroutine);
        });
 
        this.hero.onAttackTime = (() =>
        {
            var effectGo = Instantiate<GameObject>(hero.attackEffectPrefab);
            effectGo.transform.SetParent(hero.attackEffectPivot.transform, false);
            this.monster.Damaged();
 
 
            //허드 텍스트 계산.
            var hudPos = Camera.main.WorldToScreenPoint(this.monster.transform.position);
            float xPos = hudPos.x - 1920 / 2;
            float yPos = hudPos.y - 1080 / 2;
            int hudDamage = new System.Random(DateTime.Now.Millisecond).Next(5001000);
            this.uiTest1.ShowHud(new Vector3(xPos, yPos), hudDamage);
        });
 
        StartCoroutine(this.hero.Move());
    }
}
 
 
cs

Test1 메인 스크립트.

Test1씬 안의 메인 오브젝트에 달려있음.

 

UITest1과 Hero타입의 hero, monster 객체를 가지고 있음.

 

hero는 unityAction 델리게이트를 가지고 있는데 하나하나 설명하겠음.

 

1. onArrived

목적지까지 Move Coroutine을 수행하게 되는데 도착했때 수행할 함수를 넣어놓았음.

현재 실행중인 코루틴을 중지하고 도착했다는 것은 목표와 거리가 가까워 졌다는 의미 이므로

Attack 코루틴을 실행함.

 

2. onAttackEnd

공격이 끝날 수 있는 상황은 여러가지 있음. 예를들어 몬스터가 죽었거나 몬스터와 거리가 멀어졌거나.

이때 이 델리게이트의 함수를 호출함. 현재는 단순히 몬스터를 따라가고 도착했을 때 공격만 하도록 했으므로

몬스터가 죽지 않기때문에 공격이 끝나는 시점은 몬스터와의 거리가 멀어졌을때 뿐임.

그래서 이 델리게이트에는 몬스터를 다시 쫓아가는 코루틴을 수행하도록 했음.

 

역시나 현재 진행중인 코루틴을 중단하고 수행하며 중단을 하지 않거나 코루틴 안에서 또 코루틴을 호출할 경우

성능이 매우 안좋아짐...

 

3. onAttackTime

이 델리게이트의 경우는 상황에 따라 쓸수도 안쓸수도 있다고 생각함. 공격 코루틴 진행중에 모션 실행 후

진짜 어택이 이루어지는 시점까지 기다리는데, 기다리고난 시점에서 이 델리게이트의 실행 내용과 동일한 것을

적어줘도 무방함.

 

하지만 나는 구조상 hero가 monster를 가지고 있게 하는 것이 너무나 불편해 Test1 스크립트에서 관리하길 원했고

또한 HudText를 띄우는 것은 Test1의 UITest1 객체 호출해야 하는데 hero에서 이를 호출하게 하는것은 너무 불편

했음. 그래서 public으로 델리게이트를 열고 거기에 ui관련 함수, 몬스터의 데미지 받기, 이펙트 터지기(이건 히어로가

가지고 있는것이 구조상 맞다고 생각) 등등 모든것을 넣어두고 실행하도록 했음.

 

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
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
 
public class Hero : MonoBehaviour
{
    public UnityAction onArrived;
    public UnityAction onAttackEnd;
    public UnityAction onAttackTime;
 
    public IEnumerator coroutine;
 
    public GameObject model;
    public Transform targetTrans;
    public GameObject attackEffectPrefab;
    public GameObject hitEffectPrefab;
    public GameObject attackEffectPivot;
 
    private Animation anim;
    
 
    private void Awake()
    {
        this.anim = this.model.GetComponent<Animation>();
        this.anim.Play("idle@loop");
    }
 
    public IEnumerator Attack()
    {
        while(Vector3.Distance(this.transform.position, this.targetTrans.position) <= 0.5f)
        {
            this.anim.Stop();
            var attackIndex = new System.Random(DateTime.Now.Millisecond).Next(14);
            this.anim.Play("attack_sword_0" + attackIndex);
            var attackTime = (attackIndex == 1) ? 0.3f : 0.6f;
 
            yield return new WaitForSeconds(attackTime);
            this.onAttackTime();
 
            yield return new WaitForSeconds(this.anim["attack_sword_0" + attackIndex].length - attackTime);
        }
 
        this.onAttackEnd();
    }
 
    public IEnumerator Move()
    {
        this.anim.Play("run@loop");
        
        while(Vector3.Distance(this.transform.position, this.targetTrans.position) > 0.5f)
        {
            this.transform.LookAt(this.targetTrans);
            this.transform.position += this.transform.forward * Time.deltaTime * 1.0f;
            yield return null;
        }
 
        this.anim.Play("idle@loop");
 
        this.onArrived();
    }
 
    public void Damaged()
    {
        this.anim.Stop();
        this.anim.Play("damage");
        var effectGo = Instantiate<GameObject>(this.hitEffectPrefab);
        effectGo.transform.SetParent(this.transform, false);
    }
}
 
 
cs

다음 hero.cs 임

 

코루틴 배우고 사용하면서 델리게이트가 너무 늘어나서 불편해지기 시작했음.

코루틴 특성상 코루틴에서 코루틴 호출은 매우 안좋아서 특히 오브젝트가 유기적으로 행동하게 하기 위해서는

(예를들어 Move후 자동 Attack후 거리가 멀어지면 다시 Move) 델리게이트를 사용해 코루틴을 중단하고 새

코루틴을 호출해야함.

 

이제부터 델리게이트가 미친듯이 늘어갈텐데 따로 관리하는 방법을 찾아봐야 겠음.

딕셔너리로 관리하거나 리스트에 넣어놓는 등등...

 

코루틴을 유기적으로 써먹는게 어렵지 내용 자체는 Update조지는 것보다 훨씬 쉽기 때문에 내용에 대한

설명은 적지 않겠음.

특징은 각 코루틴이 끝나면 다음 코루틴이 자동 실행되도록 델리게이트를 호출하는거? 정도.

 

 

관련글 더보기