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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
 
public enum CoroutinePriority
{
    Low = 1,       // 유휴
    Medium = 2,    // 움직임
    High = 3,      // 기절
    Critical = 4   // 넉백
}
 
/// <summary>
/// 우선순위를 가진 코루틴을 관리하는 커스텀 코루틴 빌더.
/// </summary>
public class EntityCoroutineBuilder : MonoBehaviour, IEntityCoroutine
{
    /// <summary>
    /// 코루틴과 그 우선순위를 저장하는 클래스.
    /// </summary>
    private class CoroutineInfo
    {
        public CoroutinePriority Priority;
        public IEnumerator Routine;
        public bool IsActive;
 
        // For handling CustomWaitForSeconds
        public float WaitTimeRemaining;
 
        // For handling WaitForFixedUpdate
        public bool WaitForFixedUpdate;
 
        public CoroutineInfo(CoroutinePriority priority, IEnumerator routine)
        {
            Priority = priority;
            Routine = routine;
            IsActive = true;
            WaitTimeRemaining = 0f;
            WaitForFixedUpdate = false;
        }
    }
    public class CustomWaitForSeconds
    {
        public float seconds;
        public CustomWaitForSeconds(float seconds)
        {
            this.seconds = seconds;
        }
    }
 
    // 활성화된 코루틴 목록
    private List<CoroutineInfo> activeCoroutines = new List<CoroutineInfo>();
 
    /// <summary>
    /// 우선순위를 지정하여 코루틴을 시작
    /// </summary>
    /// <param name="priority">코루틴의 우선순위.</param>
    /// <param name="routine">실행할 코루틴.</param>
    public void StartCoroutineWithPriority(CoroutinePriority priority, IEnumerator routine)
    {
        if (!gameObject.activeInHierarchy)
        {
            Debug.LogWarning("코루틴을 시작할 수 없습니다. EntityCoroutineBuilder가 비활성화 상태");
            return;
        }
 
        // 더 높은 우선순위의 코루틴이 시작되면 낮은 우선순위의 코루틴을 중단
        List<CoroutinePriority> lowerPriorities = new List<CoroutinePriority>();
        foreach (var coroutine in activeCoroutines)
        {
            if (coroutine.Priority < priority && coroutine.IsActive)
            {
                lowerPriorities.Add(coroutine.Priority);
            }
        }
 
        foreach (var lowerPriority in lowerPriorities)
        {
            Debug.Log($"Stopping coroutines with priority {lowerPriority} due to new coroutine with priority {priority}.");
            StopCoroutinesByPriority(lowerPriority);
        }
 
        // 새로운 코루틴 추가
        activeCoroutines.Add(new CoroutineInfo(priority, routine));
        Debug.Log($"Starting coroutine with priority {priority}.");
    }
 
    /// <summary>
    /// 특정 우선순위의 모든 코루틴을 중지
    /// </summary>
    /// <param name="priority">중지할 코루틴의 우선순위.</param>
    public void StopCoroutinesByPriority(CoroutinePriority priority)
    {
        for (int i = activeCoroutines.Count - 1; i >= 0; i--)
        {
            var coroutine = activeCoroutines[i];
            if (coroutine.Priority == priority && coroutine.IsActive)
            {
                coroutine.IsActive = false;
                activeCoroutines.RemoveAt(i);
                Debug.Log($"Stopped coroutine with priority {priority}.");
            }
        }
    }
 
    /// <summary>
    /// 특정 우선순위의 코루틴이 실행 중인지 확인
    /// </summary>
    /// <param name="priority">확인할 우선순위.</param>
    /// <returns>해당 우선순위의 코루틴이 실행 중이면 true, 아니면 false.</returns>
    public bool IsCoroutineRunning(CoroutinePriority priority)
    {
        foreach (var coroutine in activeCoroutines)
        {
            if (coroutine.Priority == priority && coroutine.IsActive)
                return true;
        }
        return false;
    }
 
    private void Update()
    {
        // 코루틴 목록을 복사하여 수정 중 오류 방지
        var coroutinesToProcess = new List<CoroutineInfo>(activeCoroutines);
 
        foreach (var coroutine in coroutinesToProcess)
        {
            if (!coroutine.IsActive)
                continue;
 
            if (coroutine.WaitForFixedUpdate)
                continue// FixedUpdate에서 처리
 
            if (coroutine.WaitTimeRemaining > 0f)
            {
                coroutine.WaitTimeRemaining -= Time.deltaTime;
                if (coroutine.WaitTimeRemaining > 0f)
                    continue;
            }
 
            try
            {
                bool hasNext = coroutine.Routine.MoveNext();
                if (!hasNext)
                {
                    // 코루틴이 완료되었으면 목록에서 제거
                    activeCoroutines.Remove(coroutine);
                    Debug.Log($"Coroutine Priority {coroutine.Priority} completed.");
                }
                else
                {
                    HandleYieldInstruction(coroutine, coroutine.Routine.Current);
                }
            }
            catch (Exception ex)
            {
                Debug.LogError($"코루틴 실행 중 예외 발생 (우선순위 {coroutine.Priority}): {ex.Message}\n{ex.StackTrace}");
                activeCoroutines.Remove(coroutine);
            }
        }
    }
 
    private void FixedUpdate()
    {
        // 코루틴 목록을 복사하여 수정 중 오류 방지
        var coroutinesToProcess = new List<CoroutineInfo>(activeCoroutines);
 
        foreach (var coroutine in coroutinesToProcess)
        {
            if (!coroutine.IsActive)
                continue;
 
            if (!coroutine.WaitForFixedUpdate)
                continue;
 
            try
            {
                bool hasNext = coroutine.Routine.MoveNext();
                if (!hasNext)
                {
                    // 코루틴이 완료되었으면 목록에서 제거
                    activeCoroutines.Remove(coroutine);
                    Debug.Log($"Coroutine Priority {coroutine.Priority} completed.");
                }
                else
                {
                    HandleYieldInstruction(coroutine, coroutine.Routine.Current);
                }
            }
            catch (Exception ex)
            {
                Debug.LogError($"코루틴 실행 중 예외 발생 (우선순위 {coroutine.Priority}): {ex.Message}\n{ex.StackTrace}");
                activeCoroutines.Remove(coroutine);
            }
        }
    }
 
    /// <summary>
    /// YieldInstruction을 처리하는 메서드.
    /// </summary>
    /// <param name="coroutine">현재 코루틴 정보.</param>
    /// <param name="yieldObj">현재 YieldInstruction 객체.</param>
    private void HandleYieldInstruction(CoroutineInfo coroutine, object yieldObj)
    {
        if (yieldObj == null)
        {
            // 단순히 다음 프레임으로 넘어감
            return;
        }
 
        if (yieldObj is CustomWaitForSeconds customWait)
        {
            coroutine.WaitTimeRemaining = customWait.seconds;
            Debug.Log($"Coroutine Priority {coroutine.Priority}: Waiting for {customWait.seconds} seconds.");
        }
        else if (yieldObj is WaitForFixedUpdate)
        {
            coroutine.WaitForFixedUpdate = true;
            Debug.Log($"Coroutine Priority {coroutine.Priority}: Waiting for FixedUpdate.");
        }
        else
        {
            Debug.LogWarning($"지원하지 않는 YieldInstruction: {yieldObj.GetType().Name}");
        }
    }
}
 
cs


C# 내장 코루틴 이용해서 만들어볼려고 이렇게 했는데


여기서 Update랑 FixedUpate() 처리를 이벤트로해서, 기본적으로 이벤트 없을때 Upate랑 FixedUpdate() 없게 해야겠지?


기본적으로 내 아키텍쳐가 이벤트 기반이라서 코루틴 동시 다발적으로 발생할 수 있어서 스택으로 우선순위 조절해서 쓰고 있는데


Unity에서 코루틴 우선순위 관련해서 만들려고하니까 기본적으로 Update가 있어야한다네