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

Unity3D Web服务器切换场景后无法再次切换的问题求助

问题根源分析

你遇到的核心问题是场景切换后,原场景中的Scene1组件已被销毁,导致Web服务器持有的main引用失效。

当你从Scene1切换到Scene2时,Unity会销毁Scene1中的所有GameObject(包括挂载Scene1组件的对象)。虽然Web服务器的后台线程还在运行,但后续调用URL时,main.load_scene()实际上是在操作一个已经被销毁的对象——修改的是已销毁对象的scene_active变量,而当前场景中没有有效的Update方法来响应这个变量的变化,自然无法触发后续的场景切换。

解决方案:使用DontDestroyOnLoad保留核心逻辑对象

我们需要把Web服务器和场景切换的核心逻辑放到一个跨场景持久化的GameObject上,通过DontDestroyOnLoad确保它在场景切换时不会被销毁,这样Web服务器的引用始终有效,场景切换逻辑也能持续工作。

修改后的完整代码

1. 跨场景持久化的Web服务器管理器

创建一个名为WebServerManager的脚本,它将作为单例处理Web请求和场景切换:

using System;
using System.Threading;
using System.Net;
using UnityEngine;
using UnityEngine.SceneManagement;

public class WebServerManager : MonoBehaviour
{
    // 单例实例,确保全局唯一访问
    public static WebServerManager Instance { get; private set; }

    private HttpListener _webListener;
    private string _targetScene;

    private void Awake()
    {
        // 单例逻辑+跨场景持久化
        if (Instance == null)
        {
            Instance = this;
            DontDestroyOnLoad(gameObject);
            StartWebServer();
        }
        else
        {
            Destroy(gameObject);
        }
    }

    private void StartWebServer()
    {
        _webListener = new HttpListener();
        _webListener.Prefixes.Add("http://*:8084/");
        _webListener.Start();

        // 后台线程监听请求,设置IsBackground=true确保程序退出时线程自动终止
        new Thread(ListenForIncomingRequests) { IsBackground = true }.Start();
        Debug.Log("Web server started on port 8084");
    }

    private void ListenForIncomingRequests()
    {
        while (_webListener.IsListening)
        {
            try
            {
                var context = _webListener.GetContext();
                // 用线程池处理请求,避免阻塞监听线程
                ThreadPool.QueueUserWorkItem(HandleRequest, context);
            }
            catch (Exception ex)
            {
                Debug.LogError($"Web server error: {ex.Message}");
            }
        }
    }

    private void HandleRequest(object state)
    {
        var context = (HttpListenerContext)state;
        var response = context.Response;

        try
        {
            var requestPath = context.Request.Url.AbsolutePath;

            // 根据请求路径设置目标场景(仅修改变量,不调用Unity主线程API)
            switch (requestPath)
            {
                case "/scene1":
                    _targetScene = "scene1";
                    break;
                case "/scene2":
                    _targetScene = "scene2";
                    break;
                case "/scene3":
                    _targetScene = "scene3";
                    break;
                default:
                    response.StatusCode = 404;
                    SendResponse(response, "Invalid endpoint. Use /scene1, /scene2 or /scene3");
                    return;
            }

            response.StatusCode = 200;
            SendResponse(response, $"Received request: switching to {_targetScene}");
        }
        catch (Exception ex)
        {
            response.StatusCode = 500;
            SendResponse(response, $"Server error: {ex.Message}");
        }
        finally
        {
            response.Close();
        }
    }

    // 封装响应发送逻辑
    private void SendResponse(HttpListenerResponse response, string message)
    {
        var buffer = System.Text.Encoding.UTF8.GetBytes(message);
        response.ContentLength64 = buffer.Length;
        response.OutputStream.Write(buffer, 0, buffer.Length);
    }

    private void Update()
    {
        // 主线程中处理场景切换,符合Unity API要求
        if (!string.IsNullOrEmpty(_targetScene) && SceneManager.GetActiveScene().name != _targetScene)
        {
            SceneManager.LoadScene(_targetScene);
            // 切换后清空目标场景,避免重复触发切换
            _targetScene = null;
        }
    }

    private void OnApplicationQuit()
    {
        // 程序退出时关闭Web服务器,释放资源
        if (_webListener != null && _webListener.IsListening)
        {
            _webListener.Stop();
            _webListener.Close();
            Debug.Log("Web server stopped");
        }
    }
}

2. 使用方式

  1. 在你的初始场景(比如Scene1)中创建一个空GameObject,命名为WebServerHost
  2. 将WebServerManager脚本挂载到这个GameObject上
  3. 删除原来的ServeurWeb和Scene1组件,所有逻辑已整合到新的管理器中
  4. 确保所有需要切换的场景都已添加到Build Settings的场景列表中
关键改进点说明
  • 跨场景持久化:DontDestroyOnLoad保证Web服务器对象在场景切换后不会被销毁,引用始终有效
  • 线程安全:后台线程仅修改目标场景变量,实际的SceneManager.LoadScene在主线程的Update中执行,完全符合Unity的线程要求
  • 健壮性:添加了错误处理、404响应、程序退出时的资源清理逻辑
  • 单例模式:确保全局只有一个Web服务器实例,避免重复启动

内容的提问来源于stack exchange,提问作者Bob5421

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 03:58:06