/// <summary>
        /// 코루틴을 목록에서 제거한다.
        /// <para>코루틴 인스턴스가 즉시 삭제되는 것은 아님</para>
        /// </summary>
        /// <param name="coroutine">제거할 코루틴</param>
        private void RemoveCoroutine(CoroutineNode coroutine)
        {
            if (this._listFirst == coroutine)
            {
                // 목록 처음이면 나의 다음 코루틴을 처음으로 지정
                this._listFirst = coroutine._listNext;
            }
            else
            {
                // 목록의 처음이 아님
                if (coroutine._listNext != null)
                {
                    // 다음 코루틴이 있다면 앞뒤 코루틴을 연결
                    coroutine._listPrev._listNext = coroutine._listNext;
                    coroutine._listNext._listPrev = coroutine._listPrev;
                }
                else if (coroutine._listPrev != null)
                {
                    // 마지막 코루틴임. 앞 코루틴을 마지막으로 만듦
                    coroutine._listPrev._listNext = null;
                }
            }

            // 다른 코루틴 참조 제거
            coroutine._listPrev = null;
            coroutine._listNext = null;
        }
        /// <summary>
        /// 살아있는 모든 코루틴을 다음 yield 만날 떄 까지 업데이트한다.
        /// </summary>
        public void UpdateAllCoroutines()
        {
            CoroutineNode coroutine = this._listFirst;

            while (coroutine != null)
            {
                // UpdateCoroutine 도중에 현재 코루틴이 종료되어 다음 노드 정보가 사라질 수 있으므로 미리 백업
                CoroutineNode listNext = coroutine._listNext;

                if (coroutine.waitForFrame >= 0)
                {
                    if (_currentFrame >= coroutine.waitForFrame)
                    {
                        coroutine.waitForFrame = -1;
                        UpdateCoroutine(coroutine);
                    }
                }
                else if (coroutine.waitForCoroutine != null)
                {
                    if (coroutine.waitForCoroutine.finished)
                    {
                        coroutine.waitForCoroutine = null;
                        UpdateCoroutine(coroutine);
                    }
                }
                else
                {
                    UpdateCoroutine(coroutine);
                }
                coroutine = listNext;
            }
        }
        /// <summary>
        /// </summary>
        public void UpdateAllCoroutines()
        {
            CoroutineNode coroutine = this._listFirst;

            _currentFrame++;
            while (coroutine != null)
            {
                CoroutineNode listNext = coroutine._listNext;

                if (coroutine.waitForFrame >= 0)
                {
                    if (_currentFrame >= coroutine.waitForFrame)
                    {
                        coroutine.waitForFrame = -1;
                        UpdateCoroutine(coroutine);
                    }
                }
                else if (coroutine.waitForCoroutine != null)
                {
                    if (coroutine.waitForCoroutine.finished)
                    {
                        coroutine.waitForCoroutine = null;
                        UpdateCoroutine(coroutine);
                    }
                }
                else
                {
                    UpdateCoroutine(coroutine);
                }
                coroutine = listNext;
            }
        }
 /// <summary>
 /// 코루틴 목록에 추가
 /// <para>코루틴이 새 코루틴 yield 시, 새 코루틴을 먼저 업데이트하고 종료체크 할 수 있도록 목록 맨 앞에 추가</para>
 /// </summary>
 /// <param name="coroutine">추가할 코루틴</param>
 private void AddCoroutine(CoroutineNode coroutine)
 {
     if (_listFirst != null)
     {
         coroutine._listNext  = _listFirst;
         _listFirst._listPrev = coroutine;
     }
     _listFirst = coroutine;
 }
        /// <summary>
        /// 작업을 코루틴으로 등록만 하고 다음 업데이트 호출시 실행
        /// </summary>
        public CoroutineNode RegisterCoroutine(IEnumerator fiber)
        {
            if (fiber == null)
            {
                return(null);
            }

            CoroutineNode coroutine = new CoroutineNode(fiber);

            AddCoroutine(coroutine);
            return(coroutine);
        }
        /// <summary>
        /// </summary>
        /// <param name="fiber"></param>
        public CoroutineNode StartCoroutine(IEnumerator fiber)
        {
            if (fiber == null)
            {
                return(null);
            }

            CoroutineNode coroutine = new CoroutineNode(fiber);

            AddCoroutine(coroutine);
            UpdateCoroutine(coroutine);
            return(coroutine);
        }
        }                                                                          // 이번 업데이트가 호출된 프레임 번호

        /// <summary>
        /// 작업을 코루틴으로 등록한다.
        /// <para>등록 후 바로 한 번 업데이트 실행</para>
        /// <para>실행중이던 코루틴은 yield 문을 만나면 일시정지되며, yield 문의 리턴값은 코루틴어 언제 재개될지를 지정한다.</para>
        /// </summary>
        /// <param name="fiber">작업</param>
        /// <returns>등록된 코루틴</returns>
        public CoroutineNode StartCoroutine(IEnumerator fiber)
        {
            /*
             * 작업 함수가 yield 문을 갖고있지 않을 때는 (예: 바로 return null)
             * 여기서 즉시 실행된 후 목록에 추가되지 않는다.
             */
            if (fiber == null)
            {
                return(null);
            }

            // UpdateAllCoroutine() 호출 전에 등록된다면 한 프레임에 2번 업데이트 될 수도 있음
            CoroutineNode coroutine = new CoroutineNode(fiber);

            AddCoroutine(coroutine);
            UpdateCoroutine(coroutine);
            return(coroutine);
        }
        /// <summary>
        /// 코루틴이 yield 문을 만날 때 까지 진행시킨다.
        /// <para>코루틴이 끝났다면 종료 표시 후 목록에서 제거한다.</para>
        /// </summary>
        /// <param name="coroutine">실행할 코루틴</param>
        private void UpdateCoroutine(CoroutineNode coroutine)
        {
            IEnumerator fiber = coroutine._fiber;

            if (coroutine._fiber.MoveNext())
            {
                object yieldCommand = fiber.Current;

                if (yieldCommand == null)
                {
                    // yield return null 은 1프레임 대기와 동일
                    coroutine.waitForFrame = _currentFrame + 1;
                }
                else if (yieldCommand is WaitForFrames)
                {
                    WaitForFrames cmd = yieldCommand as WaitForFrames;
                    coroutine.waitForFrame = _currentFrame + cmd._frames;
                }
                else if (yieldCommand is WaitForAbsFrames)
                {
                    WaitForAbsFrames cmd = yieldCommand as WaitForAbsFrames;
                    coroutine.waitForFrame = cmd._frames;
                }
                else if (yieldCommand is CoroutineNode)
                {
                    // 새 코루틴 생성
                    coroutine.waitForCoroutine = yieldCommand as CoroutineNode;
                }
                else
                {
                    throw new System.ArgumentException("[CoroutineManager] Unexpected coroutine yield type: " + yieldCommand.GetType());
                }
            }
            else
            {
                // 코루틴 종료
                coroutine.finished = true;
                RemoveCoroutine(coroutine);
            }
        }
        /// <summary>
        /// </summary>
        /// <param name="coroutine"></param>
        private void RemoveCoroutine(CoroutineNode coroutine)
        {
            if (this._listFirst == coroutine)
            {
                this._listFirst = coroutine._listNext;
            }
            else
            {
                if (coroutine._listNext != null)
                {
                    coroutine._listPrev._listNext = coroutine._listNext;
                    coroutine._listNext._listPrev = coroutine._listPrev;
                }
                else if (coroutine._listPrev != null)
                {
                    coroutine._listPrev._listNext = null;
                }
            }

            coroutine._listPrev = null;
            coroutine._listNext = null;
        }
 /// <summary>
 /// 모든 코루틴 강제로 끊기
 /// <para>이미 돌아가고 있던 코루틴의 정상종료가 불가능하므로 쓸 일 적음</para>
 /// </summary>
 public void StopAllCoroutines()
 {
     _listFirst = null;
 }