여러 IEnumrator를 시간 관리하면서 처리하는 Coroutine

9897 단어 coroutineLINQUnity

만든 것



TaskScheduler

만들려고 생각한 계기



스테이지 클리어형의 퍼즐 게임의 스테이지 셀렉트 화면에서 축소한 맵을 단번에 3x4의 12개 동적으로 읽어들이면서, 표시. 같은 것을 StartCoroutine으로 하고 있으면 FPS가 가타 떨어지고 UI의 반응이 나빠져 버렸기 때문에.


예를 들면



한 GameObject는 어떤 Prefab을 10개 Instantiate 하고 아이 요소로서 붙여 둘 필요가 있다고 한다.
그러나 Unity의 Instantiate는 무겁다. 그래서, StartCoroutine 를 사용해, yield return null 하면서 1개씩 Instantiate 하기로 했다. (자주 있음)
public class HogeObject : MonoBehaviour
{
    public GameObject prefab;
    public void Start()
    {
        StartCoroutine(CreateIterator());
    }

    private IEnumrator CreateIterator()
    {
        for(int i = 0;i < 10;++i)
        {
            var go = Instantiate(prefab) as GameObject;//heavy work
            go.transform.SetParent(this.transform,false);
            yield return null;
        }   
    }
}

그러나이 HogeObject는 화면에 100 개 있었다! (자주 있는(?))
그러면 StartCoroutine 가 100번 불리고 1프레임에 100개 Instantiate 되게 되어 쁘띠 동결과 같은 상태로.

무엇이 나쁜지



Unity의 StartCoroutine 의 구조에는 옆의 연결이 없고, 독립적으로 움직이고 있기 때문에, 몇개 StartCoroutine 되어 왔는지 알았어야 한다는 것.

그래서



처음으로 돌아온다. 자전거 StartCoroutine의 작성.
위의 예는 다음과 같습니다.
public class HogeObject : MonoBehaviour
{
    public GameObject prefab;
    public void Start()
    {
        //StartCoroutine(CreateIterator());//この1行が
        TaskScheduler.Instance.AddIterator(CreateIterator());//これになる
    }

    private IEnumrator CreateIterator()
    {
        for(int i = 0;i < 10;++i)
        {
            var go = Instantiate(prefab) as GameObject;//heavy work
            go.transform.SetParent(this.transform,false);
            yield return null;
        }   
    }
}
StartCoroutine이었던 부분을 TaskScheduler.Instance.AddIterator로 바꾸는 것으로, TaskScheduler(Singletone)로 1개만 달리고 있다 StartCoroutine 개가 있어도 처리 떨어지지 않는다.

메커니즘


  • Unity 의 StartCoroutineIEnumerator 를 건네주고 실행하고 있지만, 구조로서는 IEnumerator#MoveNext 를 부르는 것으로 처리를 중단→계속시키고 있다. 이 MoveNext 를 부르는 처리를 스스로 만들면 자전 StartCoroutine 같은 일이 생긴다.
  • 그래서 복수의 IEnumrator를 받고, 순차적으로 MoveNext 다)
  • 즉, Unity의 yield return null; 의 확장을 Unity의 IEnumrator 를 사용하는 것으로 구현한 느낌 (무슨 말을 했는지 그 2).
  •         public void Start()
            {
                StartCoroutine(Iterator());
            }
            private IEnumerator Iterator()
            {
                while (true)
                {
                    var ts = Time.realtimeSinceStartup;
                    do
                    {
                        if (iteratorList.Count <= 0) break;
                        foreach (var itr in iteratorList.ToList())
                        {
                            if (itr.Value.MoveNext() == false)
                            {
                                iteratorList.Remove(itr.Key);
                            }
                            if (Time.realtimeSinceStartup - ts > targetTime) yield return null;
                        }
                    } while (Time.realtimeSinceStartup - ts <= targetTime);
                    yield return null;
                }
            }
    

    보충


  • Action 건네줄 수 있는 것도 김에 만들었으므로, StartCoroutine
  • StartCoroutine 등에는 미대응. 라고 할까, 처리의 부하 분산 목적이며, 순차 처리 목적으로의 StartCoroutine은 보통 StartCoroutine으로 해 주면 되는 것은 아니다.
  • StartCoroutine 그리고 TaskScheduler.Instance.AddAction(()=>Instantiate(hogeObj)); 라든지는 가치가 있지만 미대응. yield return new WaitForSeconds(ms); 로 하면 사용할 수 있다고 생각한다(시험하지 않는다)
  • 기본은 독립의 StartCoroutine에 옆의 연결을 갖게 했다면, Priority의 개념을 넣는 것이 근육이라고는 생각하지만 미실장. 필요에 따라 달리면 만듭니다.
  • 좋은 웹페이지 즐겨찾기