想象你要做一个咖啡机:
// 错误做法:一帧完成所有流程
void MakeCoffee()
{
GrindBeans(); // 磨豆1秒
HeatWater(); // 烧水3秒
BrewCoffee(); // 冲泡2秒
}
// 正确做法:协程分步执行
IEnumerator MakeCoffeeCoroutine()
{
yield return StartCoroutine(GrindBeansAsync());
yield return StartCoroutine(HeatWaterAsync());
yield return StartCoroutine(BrewCoffeeAsync());
}
// 启动协程的三种方式
Coroutine co = StartCoroutine("MyCoroutine"); // 通过方法名
StartCoroutine(MyCoroutine()); // 直接调用
StartCoroutine("MyCoroutine", 5); // 带参数
// 停止协程的三种方式
StopCoroutine("MyCoroutine");
StopCoroutine(co);
StopAllCoroutines();
yield return null; // 等待一帧
yield return new WaitForSeconds(2); // 等待2秒
yield return new WaitUntil(() => player.isReady); // 条件等待
yield return new WaitForFixedUpdate(); // 物理帧后
IEnumerator SkillCooldown(float cooldownTime)
{
skillButton.interactable = false;
float timer = 0;
while(timer < cooldownTime)
{
timer += Time.deltaTime;
coolDownImage.fillAmount = 1 - (timer / cooldownTime);
yield return null;
}
skillButton.interactable = true;
}
IEnumerator PatrolPath(List<Transform> points)
{
int index = 0;
while(true)
{
Vector3 target = points[index].position;
while(Vector3.Distance(transform.position, target) > 0.1f)
{
transform.position = Vector3.MoveTowards(transform.position, target, 2f * Time.deltaTime);
yield return null;
}
yield return new WaitForSeconds(1); // 在点位停留1秒
index = (index + 1) % points.Count;
}
}
// 手动控制迭代器示例
IEnumerator Counter(int max)
{
for(int i=0; i<max; i++)
{
yield return i;
}
}
void Test()
{
var enumerator = Counter(3);
while(enumerator.MoveNext())
{
Debug.Log(enumerator.Current); // 输出0,1,2
}
}
public class CoroutineMgr : MonoBehaviour
{
private static CoroutineMgr _instance;
public static CoroutineMgr Instance => _instance;
// 协程池
private Dictionary<string, List<CoroutineTask>> _taskGroups = new();
// 协程任务类
public class CoroutineTask
{
public string GroupName;
public IEnumerator Routine;
public Coroutine Handle;
public bool IsPaused;
}
void Awake() => _instance = this;
// 启动协程
public CoroutineTask StartTask(IEnumerator routine, string groupName = "default")
{
var task = new CoroutineTask
{
Routine = routine,
GroupName = groupName
};
if(!_taskGroups.ContainsKey(groupName))
_taskGroups[groupName] = new List<CoroutineTask>();
_taskGroups[groupName].Add(task);
task.Handle = StartCoroutine(RunTask(task));
return task;
}
// 核心运行逻辑
private IEnumerator RunTask(CoroutineTask task)
{
while(true)
{
if(task.IsPaused)
{
yield return null;
continue;
}
if(!task.Routine.MoveNext())
break;
yield return task.Routine.Current;
}
_taskGroups[task.GroupName].Remove(task);
}
// 暂停组内所有协程
public void PauseGroup(string groupName)
{
if(_taskGroups.TryGetValue(groupName, out var tasks))
{
foreach(var task in tasks)
task.IsPaused = true;
}
}
// 停止组内所有协程
public void StopGroup(string groupName)
{
if(_taskGroups.TryGetValue(groupName, out var tasks))
{
foreach(var task in tasks.ToArray())
{
StopCoroutine(task.Handle);
tasks.Remove(task);
}
}
}
}
// 启动协程
var task = CoroutineMgr.Instance.StartTask(MyCoroutine(), "ui");
// 暂停UI组所有协程
CoroutineMgr.Instance.PauseGroup("ui");
// 恢复UI组协程
CoroutineMgr.Instance.ResumeGroup("ui");
// 停止场景切换组
CoroutineMgr.Instance.StopGroup("scene");
// 等待按钮点击
IEnumerator WaitForClick()
{
bool isClicked = false;
button.onClick.AddListener(() => isClicked = true);
yield return new WaitUntil(() => isClicked);
}
// 用UniTask改造
async UniTask WaitForClickAsync()
{
await button.OnClickAsync(); // 使用UniTask扩展
}
IEnumerator UploadScore(int score)
{
var form = new WWWForm();
form.AddField("score", score);
using var request = UnityWebRequest.Post(url, form);
yield return request.SendWebRequest();
if(request.result != UnityWebRequest.Result.Success)
{
Debug.LogError("上传失败");
yield break;
}
Debug.Log("上传成功");
}
// 错误示例:每帧创建新对象
IEnumerator BadExample()
{
while(true)
{
var temp = new DataObject(); // 每帧产生GC
ProcessData(temp);
yield return null;
}
}
// 优化方案:对象池
IEnumerator GoodExample()
{
var buffer = ObjectPool.GetBuffer(); // 复用对象
while(true)
{
buffer.Clear();
FillData(buffer);
ProcessData(buffer);
yield return null;
}
}
IEnumerator ProcessBigData(List<Data> allData)
{
int batchSize = 100; // 每批处理100个
for(int i=0; i<allData.Count; i+=batchSize)
{
ProcessBatch(allData, i, Mathf.Min(i+batchSize, allData.Count));
yield return null; // 每批处理完暂停一帧
}
}
现象 | 可能原因 | 解决方案 |
---|---|---|
协程未启动 | 忘记调用StartCoroutine | 检查启动代码 |
对象被销毁 | MonoBehaviour已被销毁 | 添加空引用检查 |
时间缩放影响 | Time.timeScale=0 | 使用UnscaledDeltaTime |
协程被意外停止 | 调用StopCoroutine错误 | 使用管理器统一管理 |
public class ScenePersistent : MonoBehaviour
{
static ScenePersistent _instance;
void Awake()
{
if(_instance == null){
_instance = this;
DontDestroyOnLoad(gameObject);
}
else Destroy(gameObject);
}
public static void RunPersistentCoroutine(IEnumerator co)
{
_instance.StartCoroutine(co);
}
}
读了多篇大神文章!!!
感谢大佬们的分享!!!