Unity C#中StopAllCoroutines无法停止协程致地图类型切换失败
问题解决:Unity协程无法停止导致地图类型无法切换
问题根源
调用setMapTypeRoadMap()时,StopAllCoroutines()无法立即终止正在执行网络请求的UpdateMap协程——因为协程正卡在UnityWebRequest的异步等待阶段,Unity要等异步操作完成后才会终止协程。此时旧协程会继续执行后续逻辑,用原来的mapType(satellite)生成URL并更新地图,覆盖新协程的roadmap设置,导致地图类型切换失败。
解决方案:优雅控制协程生命周期
用布尔标志位替代无限循环,配合协程引用精准管理,让协程可以优雅退出,同时避免资源泄漏:
修改后的完整代码
using UnityEngine; using UnityEngine.UI; using UnityEngine.Networking; using System; using System.Collections; public class Map : MonoBehaviour { private string apiKey = "MY API"; public enum resolution { low = 1, high = 2 }; public resolution mapResolution = resolution.high; public enum type { roadmap, satellite, gybrid, terrain }; private type mapType = type.satellite; private int mapWidth = 800; private int mapHeight = 540; private float lat = 0; private float lon = 0; private int zoom = 18; private string url = ""; private Rect rect; // 协程引用,用于精准停止 private Coroutine _updateMapCoroutine; // 协程运行标志位 private bool _isMapUpdating = false; void Start() { StartMapUpdate(); } // 封装协程启动逻辑 private void StartMapUpdate() { _isMapUpdating = true; _updateMapCoroutine = StartCoroutine(UpdateMap()); } IEnumerator UpdateMap() { // 用标志位替代while(true) while (_isMapUpdating) { string latLast = ""; string lonLast = ""; using (UnityWebRequest webRequest = UnityWebRequest.Get("MY SERVER")) { // 检查标志位,提前终止 if (!_isMapUpdating) yield break; yield return webRequest.SendWebRequest(); // 再次检查标志位 if (!_isMapUpdating) yield break; if (webRequest.result != UnityWebRequest.Result.Success) { Debug.LogError("Error: " + webRequest.error); } else { Coordinates data = JsonUtility.FromJson<Coordinates>(webRequest.downloadHandler.text); String[] parts = data.coordinates.Split(','); latLast = parts[0]; lonLast = parts[1]; Debug.Log(mapType); url = "https://maps.googleapis.com/maps/api/staticmap?" + "center=" + latLast + "," + lonLast + "&zoom=" + zoom + "&markers=color:red|" + latLast + "," + lonLast + "&size=" + mapWidth + "x" + mapHeight + "&scale=" + (int)resolution.high + "&maptype=" + mapType + "&key=" + apiKey; using (UnityWebRequest www = UnityWebRequestTexture.GetTexture(url)) { // 检查标志位 if (!_isMapUpdating) yield break; yield return www.SendWebRequest(); // 检查标志位 if (!_isMapUpdating) yield break; if (www.result != UnityWebRequest.Result.Success) { Debug.Log("WWW ERROR: " + www.error); } else { gameObject.GetComponent<RawImage>().texture = ((DownloadHandlerTexture)www.downloadHandler).texture; } } } } // 等待前检查标志位 if (_isMapUpdating) { yield return new WaitForSeconds(2); } } } [Serializable] private class Coordinates { public string coordinates; } public void setMapTypeRoadMap() { // 停止旧协程:先设置标志位让协程优雅退出 _isMapUpdating = false; // 如果协程存在,强制停止(保险措施) if (_updateMapCoroutine != null) { StopCoroutine(_updateMapCoroutine); _updateMapCoroutine = null; } // 更新地图类型 mapType = type.roadmap; // 启动新协程 StartMapUpdate(); } }
关键修改点
- 新增
_isMapUpdating标志位:替代while(true),让协程在循环的各个关键节点(网络请求前、等待后)检查标志位,主动退出,避免强制停止的延迟问题。 - 保存协程引用
_updateMapCoroutine:精准停止目标协程,而非用StopAllCoroutines()影响其他协程(如果后续新增其他协程的话)。 - 网络请求块内增加标志位检查:确保在协程被要求停止时,即使网络请求还在进行,也能在请求完成后立即终止后续逻辑,不会用旧的
mapType更新地图。 - 封装协程启动逻辑:统一管理协程的启动和状态,避免重复代码。
额外注意事项
- 不要用
Thread替代协程:Unity的UI和Texture操作必须在主线程执行,用Thread会导致报错,协程才是Unity异步操作的正确方式。 - 确保
using块正确包裹UnityWebRequest:即使协程终止,using会自动释放网络请求资源,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Morad Zahri
相关产品推荐
相关产品推荐

