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

