[유니티] 코루틴, 반복자패턴

YongSeok·2022년 11월 7일
0

✏️
A

📌 코루틴

  • CoRoutine : 협력하는 루틴
  • 유니티는 기본적으로 싱글스레드를 권장하는 엔진이다
  • 멀티스레드를 사용하지않고 그런한 기법을 흉내낼수 있을지 고민결과 코루틴이란것을 사용하였다

✏️ 싱글스레드


👆 직원 한명이 순차적으로 하나씩 일을 처리


✏️ 멀티스레드


👆 직원 두명이 순차적으로 하나씩 일을 처리

그러나 이 경우의 단점은 같은 업무가 동시에 일어났을때 중복되어 충돌이 일어날경우가 있다 이를 방지하기 위하여 유니티에서는 멀티스레드를 사용하지않고 싱글스레드를 사용한다


✏️ 코루틴


👆 지원은 한명이지만 동시 다발적으로 여러 일들을 처리하기에 멀티스레드가 하는 역할을 흉내를 낼 수 있다


👇 코루틴 사용법

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GameManager : MonoBehaviour
{
    private void Start()
    {
        StartCoroutine(TestCo());   
    }
    IEnumerator TestCo()
    {
        yield return new WaitForSeconds(1);
        Debug.Log("1초후의 처리");
    }
}

✏️ 여기서 IEnumerator 과 yield return 은 무슨 의미를 하는가?
IEnumerator 은 일종의 순서와 같으며 열거자 라고도 한다.


👇Coroutine, StopCoroutine 예시

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GameManager : MonoBehaviour
{
    void Start()
    {
        StartCoroutine(TestCo());   
    }

    IEnumerator TestCo()
    {
        yield return new WaitForSeconds(1);
        Debug.Log("1초후의 처리");
        yield return new WaitForSeconds(1);
        Debug.Log("2초후의 처리");
        yield return new WaitForSeconds(1);
        Debug.Log("3초후의 처리");
        yield return new WaitForSeconds(1);
        Debug.Log("4초후의 처리");
        yield return new WaitForSeconds(1);
        Debug.Log("5초후의 처리");
    }

    void Update()
    {
        if(Input.GetKeyDown(KeyCode.Space))
        {
            StopCoroutine(TestCo());
        }
    }
}

이러한 형식의 코루틴과 코루틴을 멈추게 하는 기능을 만들었을 경우 코루틴은 멈추게 될까?
정답은 아니다

왜냐하면 서로 다른함수이기 때문 함수의 이름이 같다고 하여 함수의 내부마저 전부 같다고 할 수 없다

그렇다면 저 코루틴은 멈출수 있는 방법은 없을까? 여러 방법이 있지만 StopCoroutine("TestCo"); 로 리플렉션을 활용할수 있지만 연산이 많이 필요해진다
혹은 testCo = TestCO(); 로 캐싱을 한후 StopCoroutine(testCo); 로 멈추게 하는 방법이 있다


using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GameManager : MonoBehaviour
{
    IEnumerator testCo;
    void Start()
    {
        testCo = TestCo();
    }

    IEnumerator TestCo()
    {
        yield return new WaitForSeconds(1);
        Debug.Log("1초후의 처리");
        yield return new WaitForSeconds(1);
        Debug.Log("2초후의 처리");
        yield return new WaitForSeconds(1);
        Debug.Log("3초후의 처리");
        yield return new WaitForSeconds(1);
        Debug.Log("4초후의 처리");
        yield return new WaitForSeconds(1);
        Debug.Log("5초후의 처리");
        yield return new WaitForSeconds(1);
        Debug.Log("6초후의 처리");
    }

    void Update()
    {
        if(Input.GetKeyDown(KeyCode.Space))
        {
            StopCoroutine(testCo);
        }
        if (Input.GetKeyDown(KeyCode.V))
        {
            StopCoroutine(testCo);
        }
    }
}

👆 코드를 살짝 수정하여 멈추고 다시 실행시키고 멈추고 실행시키는 식으로 만들었을때 IEnumerator 는 순서가 있기에 1, 2, 3, 4, 5.... 의 순서대로 실행된다 위에 말했단 열거자 와 비슷한 맥락이다.


️✏️ IEnumerator, IEnumerable

IEnumerator : 특정한 순서
IEnumerable : 순서를 가지고 있는 것

👇 IEnumerator

namespace System.Collections
{
    public interface IEnumerator
    {
        object Current { get; }

        bool MoveNext();	// 순서를 준다
        void Reset();		// 초기화 시켜준다
    }
}

👇 IEnumerable - IEnumerator(순서) 를 가져올수 있는

namespace System.Collections.Generic
{
    public interface IEnumerable<out T> : IEnumerable
    {
        IEnumerator<T> GetEnumerator();
    }
}

👇 List나 Statck 에서 Ienumerator가 사용되는 부분

✏️순서를 가져온다는것은 어떤 의미이기에 List나 특정 콜렉션들은 순서를 따를까??

반복자 패턴을 활용할때에 IEnumerator들을 순차적으로 가져올수 있어야 하기 때문이다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

// 짝수만 들어가는 콜렉션
public class EvenNumberCollections : IEnumerable    // IEnumerable 인터페이스 구현해라
{
    public List<int> collection = new List<int>();

    public void Add(int value)
    {
        if(value % 2 == 0)
        {
            collection.Add(value);
        }
        else
        {
            Debug.Log(value + "is not even number");
        }
    }

    public IEnumerator GetEnumerator()
    {
        throw new System.NotImplementedException();
    }
}
public class InOrderEnumerator : IEnumerator    // IEnumerator 구현해라
{
    public object Current => current;
    public EvenNumberCollections evenNumberCollections;

    public int current;
    public int index = 0;

    public InOrderEnumerator(EvenNumberCollections inputCollection)
    {
        evenNumberCollections = inputCollection;
    }
    public bool MoveNext()
    {
        current = evenNumberCollections.collection[index];
        index++;
        if(index < evenNumberCollections.collection.Count)
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    public void Reset()
    {
        index = 0;
    }
}


public class Test : MonoBehaviour
{
    public EvenNumberCollections evenNumbers = new EvenNumberCollections();
    private void Start()
    {
        for (int i = 0; i < 100; i++)
        {
            evenNumbers.Add(i);
        }
        foreach (int current in evenNumbers)
        {
            Debug.Log(current);// evenNumbers 는 IEnumerable 이여야 한다
        }
    }
}

👆 순차적으로 짝수를 출력해내는 IEnumerable 상속


️✏️ yield

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public enum Mouse_TYPE
{
    RIGHT=1,LEFT=0
}

public class WaitForMouseDown : CustomYieldInstruction
{
    private Mouse_TYPE type;
    public WaitForMouseDown(Mouse_TYPE type = Mouse_TYPE.LEFT)
    {
        this.type = type;
    }
    public override bool keepWaiting => !Input.GetMouseButtonDown((int)type);
}


public class Test : MonoBehaviour
{
    private void Start()
    {
        StartCoroutine(TestCo());
    }
    IEnumerator TestCo()
    {
        yield return new WaitForMouseDown(Mouse_TYPE.RIGHT);
        Debug.Log("마우스 우클릭 눌림");

        yield return new WaitForMouseDown(Mouse_TYPE.LEFT);
        Debug.Log("마우스 좌클릭 눌림");
        // yield : WaitForMouseDown 가 true가 될때까지 실행해라
        // 특정한 상태가 true 가 될때까지 대기        
    }
}

✏️ 반복자 패턴

0개의 댓글

관련 채용 정보