Yang.공부방

IEnumerator : 제너릭이 아닌 컬렉션을 단순반복할 수 있도록 지원하는 인터페이스

while 문과 foreach문을 사용하기 편하게 Coroutine으로 사용하는 인터페이스다.

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
public class App : MonoBehaviour
{
    public Character hero;
    public Character monster;
    private Animation anim;
    private Character target;
    private float attackRange = 0.5f;
    private bool isStart;
 
    void Start()
    {
        StartCoroutine(this.hero.Move(new Vector3(505)));
        this.monster.Position(new Vector3(505));
        StartCoroutine(this.monster.Attack(this.hero));
    }
 
    private void Awake()
    {
        this.anim = this.gameObject.GetComponent<Animation>();
    }
 
    public void Position(Vector3 position)
    {
        this.transform.position = position;
    }
 
    public IEnumerator Move(Vector3 tPos)
    {
        this.anim.Play("run@loop");
        this.isStart = true;
        while(true)
        {
            if (this.isStart == true)
            {
                this.transform.LookAt(tPos);
                var dir = (tPos - this.transform.position).normalized;
                var speed = 1;
                this.transform.position += dir * speed * Time.deltaTime;
                var distance = Vector3.Distance(tPos, this.transform.position);
                if (attackRange > distance)
                {
                    this.anim.Play("idle@loop");
                    this.isStart = false;
                    if (distance <= 0.1f && this.isStart == true)
                    {
                        anim.Play("idle@loop");
                        Debug.Log("이동완료");
                        break;
                    }
                }
            }
            yield return null;
        }
    }
 
    public IEnumerator Attack(Character target)
    {
        this.anim.Play("idle@loop");
        this.target = target;
 
        while (true)
        {
            var distance = Vector3.Distance(this.transform.position, target.transform.position);
            if (attackRange > distance)
            {
                this.transform.LookAt(target.transform.position);
                anim.Play("attack_sword_01");
                break;
            }
            yield return null;
        }
    }
 
}
cs

예를들면 전에 사용했던 코루틴을 사용하여 이동과 공격하는 인터페이스를 만든 부분이다.

while문 안에는 yield 키워드가 들어가며 기능은 "한 프레임마다" 반복이라고 생각하면 편하다.

코루틴을 사용할 때에는 StartCoroutine(); 을 사용해야 한다.


이것이 번거롭다면 미리 메서드를 생성해 두는 것도 좋은 방법이다.

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
public void Move(Vector3 tPos)
    {
        StartCoroutine(this.MoveImpl(tPos));
    }
 
private IEnumerator MoveImpl(Vector3 tPos)
    {
        this.anim.Play("run@loop");
        this.isStart = true;
        while(true)
        {
            if (this.isStart == true)
            {
                this.transform.LookAt(tPos);
                var dir = (tPos - this.transform.position).normalized;
                var speed = 1;
                this.transform.position += dir * speed * Time.deltaTime;
                var distance = Vector3.Distance(tPos, this.transform.position);
                if (attackRange > distance)
                {
                    this.anim.Play("idle@loop");
                    this.isStart = false;
                    if (distance <= 0.1f && this.isStart == true)
                    {
                        anim.Play("idle@loop");
                        Debug.Log("이동완료");
                        break;
                    }
                }
            }
            yield return null;
        }
    }
cs

예시이다.
위와 같이 메서드를 사용하여 외부에서는 Move메서드를 호출하면 코루틴을 사용하기 용이하다.

(여기서 주의할 점)
코루틴은 재대로 종료하지 않을 시 작업이 겹치므로 다음과 같은 코딩을 하면 좋다.
1
2
3
4
5
6
7
8
public void Move(Vector3 tPos)
    {
        if (routine != null)
        {
            StopCoroutine(this.routine);
        }
        this.routine = StartCoroutine(this.MoveImpl(tPos));
    }
cs

지역변수로 private Coroutine routine;을 선언하며 지역변수에 값을 넣어주며 그 값이 null이 아닐경우 중지시킨다.

참조 : https://docs.microsoft.com/ko-kr/dotnet/api/system.collections.ienumerator?view=netframework-4.7.2

참조 : https://www.slideshare.net/jungsoopark104/ienumerator


yield 키워드 : 호출자(Caller)에게 컬렉션 데이타를 하나씩 리턴할 때 사용


yield return  : 각 요소를 따라 반환 , while문에서 쓴 이유 = 한 프레임마다 다시 돌리기 위함이다.

yield break : 리턴을 중지하고 Iteration 루프를 빠져나올 때 사용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    //1번
    public IEnumerator test()
    {
        //1번째 프레임 진입 
        yield return null;  // 첫 프레임은 여기서 종료 
                            //2번째 프레임 진입 
        Debug.Log("Hello world"); // 2번째 프레임에서 실행 
                                  // 함수내에 yield가 더이상 없기 때문에 종료. 
    }
 
    //2번.
    public IEnumerator test2()
    {
        // 1번째 프레임 진입 
        Debug.Log("Hello world");  // 1번째 프레임에서 실행 
        yield return null;  // 1번째 프레임에서 yield 시킴 
                            // 2번째 프레임 진입 
                            // 더이상 실행할 게 없으므로 종료. 
    }
cs

참조 : http://www.devkorea.co.kr/bbs/board.php?bo_table=m03_qna&wr_id=77246