You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 22:22:46