协程是Unity中一种特殊的程序执行方式,它允许我们将一个长时间的操作分散到多个帧中执行,而不是在一帧内完成所有操作。可以将协程理解为一种"能够暂停执行"的函数。
与普通函数相比,协程具有以下特点:
可暂停性:
延时执行:
灵活控制:
在游戏开发中,协程解决了许多实际问题:
性能优化:
时序控制:
异步操作:
public class CoroutineExample : MonoBehaviour
{
// 声明协程
private IEnumerator SimpleCoroutine()
{
Debug.Log("协程开始");
yield return new WaitForSeconds(2f); // 等待2秒
Debug.Log("协程结束");
}
// 启动协程的几种方式
private void Start()
{
// 方式1:直接启动
StartCoroutine(SimpleCoroutine());
// 方式2:通过字符串启动(不推荐,因为不类型安全)
StartCoroutine("SimpleCoroutine");
// 方式3:保存协程引用
Coroutine coroutine = StartCoroutine(SimpleCoroutine());
}
}
private IEnumerator WaitExample()
{
// 等待一帧
yield return null;
// 等待固定时间
yield return new WaitForSeconds(1f);
// 等待固定时间(不受时间缩放影响)
yield return new WaitForSecondsRealtime(1f);
// 等待直到下一个固定物理更新
yield return new WaitForFixedUpdate();
// 等待直到条件满足
yield return new WaitUntil(() => someCondition == true);
// 等待直到条件不满足
yield return new WaitWhile(() => someCondition == true);
// 等待异步操作完成
yield return StartCoroutine(AnotherCoroutine());
}
public class FadeEffect : MonoBehaviour
{
private Image fadeImage;
private IEnumerator FadeIn(float duration)
{
float elapsedTime = 0f;
Color color = fadeImage.color;
while (elapsedTime < duration)
{
elapsedTime += Time.deltaTime;
float alpha = Mathf.Lerp(1f, 0f, elapsedTime / duration);
fadeImage.color = new Color(color.r, color.g, color.b, alpha);
yield return null;
}
}
private IEnumerator FadeOut(float duration)
{
float elapsedTime = 0f;
Color color = fadeImage.color;
while (elapsedTime < duration)
{
elapsedTime += Time.deltaTime;
float alpha = Mathf.Lerp(0f, 1f, elapsedTime / duration);
fadeImage.color = new Color(color.r, color.g, color.b, alpha);
yield return null;
}
}
}
public class SkillSystem : MonoBehaviour
{
private Dictionary<string, bool> skillCooldowns = new Dictionary<string, bool>();
private IEnumerator SkillCooldown(string skillName, float cooldownTime)
{
skillCooldowns[skillName] = true;
yield return new WaitForSeconds(cooldownTime);
skillCooldowns[skillName] = false;
}
public void CastSkill(string skillName, float cooldownTime)
{
if (!skillCooldowns.ContainsKey(skillName) || !skillCooldowns[skillName])
{
// 使用技能
Debug.Log($"使用技能: {skillName}");
// 启动冷却
StartCoroutine(SkillCooldown(skillName, cooldownTime));
}
else
{
Debug.Log($"技能 {skillName} 正在冷却中");
}
}
}
private IEnumerator MainCoroutine()
{
Debug.Log("主协程开始");
// 等待子协程完成
yield return StartCoroutine(SubCoroutine());
Debug.Log("主协程结束");
}
private IEnumerator SubCoroutine()
{
Debug.Log("子协程开始");
yield return new WaitForSeconds(1f);
Debug.Log("子协程结束");
}
public class CoroutineManager : MonoBehaviour
{
private List<Coroutine> activeCoroutines = new List<Coroutine>();
public void StartManagedCoroutine(IEnumerator routine)
{
Coroutine coroutine = StartCoroutine(routine);
activeCoroutines.Add(coroutine);
}
public void StopAllManagedCoroutines()
{
foreach (var coroutine in activeCoroutines)
{
if (coroutine != null)
StopCoroutine(coroutine);
}
activeCoroutines.Clear();
}
private void OnDisable()
{
StopAllManagedCoroutines();
}
}
避免过多协程:
合理使用yield:
生命周期问题:
错误处理:
public class CoroutineDebugger : MonoBehaviour
{
private IEnumerator DebugCoroutine(IEnumerator routine)
{
Debug.Log($"协程开始: {Time.time}");
while (true)
{
try
{
if (!routine.MoveNext())
break;
}
catch (System.Exception e)
{
Debug.LogError($"协程执行错误: {e.Message}");
yield break;
}
yield return routine.Current;
}
Debug.Log($"协程结束: {Time.time}");
}
public void StartDebugCoroutine(IEnumerator routine)
{
StartCoroutine(DebugCoroutine(routine));
}
}
private IEnumerator LoadAssetAsync()
{
// 异步加载资源
ResourceRequest request = Resources.LoadAsync<GameObject>("Prefabs/Character");
// 等待加载完成
while (!request.isDone)
{
float progress = request.progress * 100f;
Debug.Log($"加载进度: {progress}%");
yield return null;
}
// 使用加载的资源
GameObject character = Instantiate(request.asset as GameObject);
}
private IEnumerator LoadSceneWithTransition(string sceneName)
{
// 开始淡出
yield return StartCoroutine(FadeOut(1f));
// 异步加载场景
AsyncOperation asyncLoad = SceneManager.LoadSceneAsync(sceneName);
asyncLoad.allowSceneActivation = false;
while (asyncLoad.progress < 0.9f)
{
Debug.Log($"场景加载进度: {asyncLoad.progress * 100}%");
yield return null;
}
// 等待淡出完成
asyncLoad.allowSceneActivation = true;
// 开始淡入
yield return StartCoroutine(FadeIn(1f));
}
协程是Unity中非常强大的功能,合理使用可以让游戏逻辑更加清晰,性能更好。在实际开发中,需要根据具体情况选择合适的使用方式,并注意避免常见陷阱。