Unity-Mirror网络框架-从入门到精通之AdditiveScenes 示例

文章目录

前言

在现代游戏开发中,网络功能日益成为提升游戏体验的关键组成部分。Mirror是一个用于Unity的开源网络框架,专为多人游戏开发设计。它使得开发者能够轻松实现网络连接、数据同步和游戏状态管理。本文将深入介绍Mirror的基本概念、如何与其他网络框架进行比较,以及如何从零开始创建一个使用Mirror的简单网络项目。

Additive Levels和Additive Scenes

在Unity Mirror中,Additive Levels和Additive Scenes虽然看起来相似,但它们的应用和功能存在一些区别。

  • Additive Levels 是一个示例,旨在展示如何使用附加场景进行场景管理和玩家移动。这个示例主要关注利用Scene Interest Management进行场景间的切换,例如通过传送门在不同的关卡之间传送玩家。它使得在不同的场景中,玩家只能看到并与同一场景中的其他玩家进行交互,而不影响其他场景的玩家。通过此示例,可以学习如何实现自定义的场景加载和淡入淡出效果,同时也演示了如何处理在多个场景间的玩家状态和同步 10 。

  • Additive Scenes 则是一个更广义的概念,指的是在Unity中同时加载多个场景的能力。通过添加添加场景,开发者可以在一个主场景中加载多个次要场景,以便实现更复杂的游戏逻辑和界面布局。添加场景通常用于开发大型游戏世界,而不必将整个游戏内容打包到一个单一场景中。

Additive Levels

场景介绍

我们可以看到 AdditiveScenes总共4个场景,分别是Offline,OnLine,SubLevel1,SubLevel,

Offline是启动场景,加载NetworkManager,然后进入OnLine场景

进入Online场景后,在额外添加两个SubLeve场景,但是SubLevel是处于未激活状态。

csharp 复制代码
        /// Called on the server when a scene is completed loaded, when the scene load was initiated by the server with ServerChangeScene().
        public override void OnServerSceneChanged(string sceneName)
        {
            if (sceneName == onlineScene)
                StartCoroutine(ServerLoadSubScenes());
        }

        IEnumerator ServerLoadSubScenes()
        {
            foreach (string additiveScene in additiveScenes)
                yield return SceneManager.LoadSceneAsync(additiveScene, new LoadSceneParameters
                {
                    loadSceneMode = LoadSceneMode.Additive,
                    localPhysicsMode = LocalPhysicsMode.Physics3D 
                });

            subscenesLoaded = true;
        }

场景中有一个透明的传送区域,上面写着SubLevel1和SubLevel2,当我们控制WASD走过去,进入传送区域以后,就会加载指定的SubLevel场景为激活状态。

Portal传送门

csharp 复制代码
        void OnTriggerEnter(Collider other)
        {
            if (isServer)
                StartCoroutine(SendPlayerToNewScene(other.gameObject));
        }

        [ServerCallback]
        IEnumerator SendPlayerToNewScene(GameObject player)
        {
            if (!player.TryGetComponent(out NetworkIdentity identity)) yield break;

            NetworkConnectionToClient conn = identity.connectionToClient;
            if (conn == null) yield break;

            // Tell client to unload previous subscene with custom handling (see NetworkManager::OnClientChangeScene).
            conn.Send(new SceneMessage { sceneName = gameObject.scene.path, sceneOperation = SceneOperation.UnloadAdditive, customHandling = true });

            // wait for fader to complete.
            yield return new WaitForSeconds(AdditiveLevelsNetworkManager.singleton.fadeInOut.GetFadeInTime());

            // Remove player after fader has completed
            NetworkServer.RemovePlayerForConnection(conn, RemovePlayerOptions.Unspawn);
            yield return null;

            // reposition player on server and client
            player.transform.position = startPosition;
            player.transform.LookAt(Vector3.up);

            // Move player to new subscene.
            SceneManager.MoveGameObjectToScene(player, SceneManager.GetSceneByPath(destinationScene));

            // Tell client to load the new subscene with custom handling (see NetworkManager::OnClientChangeScene).
            conn.Send(new SceneMessage { sceneName = destinationScene, sceneOperation = SceneOperation.LoadAdditive, customHandling = true });

            // Player will be spawned after destination scene is loaded
            NetworkServer.AddPlayerForConnection(conn, player);
        }

其实这个传送门核心代码就三句

从服务器移除传送玩家

1.NetworkServer.RemovePlayerForConnection(conn, RemovePlayerOptions.Unspawn);

移动玩家到新场景中

2.SceneManager.MoveGameObjectToScene(player, SceneManager.GetSceneByPath(destinationScene));

添加传送玩家到服务器

3.NetworkServer.AddPlayerForConnection(conn, player);

FadeInOut特效

传送效果,其实非常简单,就是一个Image遮罩的透明度变化。详细请参考代码。

csharp 复制代码
        private IEnumerator FadeImage(float startAlpha, float endAlpha, float duration)
        {
            if (panelImage == null) yield break;

            if (isFading) yield break;

            // Short circuit if the alpha is already at endAlpha
            Color color = panelImage.color;
            if (Mathf.Approximately(color.a, endAlpha)) yield break;

            isFading = true;

            float elapsedTime = 0f;
            float fixedDeltaTime = Time.fixedDeltaTime;

            while (elapsedTime < duration)
            {
                elapsedTime += fixedDeltaTime;
                float alpha = Mathf.Lerp(startAlpha, endAlpha, elapsedTime / duration);
                panelImage.color = new Color(color.r, color.g, color.b, alpha);
                yield return new WaitForFixedUpdate();
            }

            // Ensure the final alpha value is set
            panelImage.color = new Color(color.r, color.g, color.b, endAlpha);

            isFading = false;
        }

Additive Scenes

示例介绍

该实例,默认只有服务端加载了SubScene场景,这样可以减少客户端的开销。

启动Server后,加载SubScene,仅在服务器上。

csharp 复制代码
 public class AdditiveNetworkManager : NetworkManager
    {
        [Tooltip("Trigger Zone Prefab")]
        public GameObject Zone;
        public string[] subScenes;

        public override void OnStartServer()
        {
            base.OnStartServer();

            // load all subscenes on the server only
            StartCoroutine(LoadSubScenes());

            // Instantiate Zone Handler on server only
            Instantiate(Zone);
        }

        IEnumerator LoadSubScenes()
        {
            Debug.Log("Loading Scenes");

            foreach (string sceneName in subScenes)
                yield return SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Additive);
        }
    }

ZoneHandler

当Client的Player移动到指定碰撞区域后,会发送SceneMessage 消息给客户端,这时客户端才会加载对应的SubScene。

csharp 复制代码
public class ZoneHandler : MonoBehaviour
    {
        [Scene]
        public string subScene;

        [ServerCallback]
        void OnTriggerEnter(Collider other)
        {
            // ignore collisions with non-Player objects
            if (!other.CompareTag("Player")) return;

            if (other.TryGetComponent(out NetworkIdentity networkIdentity))
            {
                SceneMessage message = new SceneMessage { sceneName = subScene, sceneOperation = SceneOperation.LoadAdditive };
                networkIdentity.connectionToClient.Send(message);
            }
        }

        [ServerCallback]
        void OnTriggerExit(Collider other)
        {
            // ignore collisions with non-Player objects
            if (!other.CompareTag("Player")) return;

            if (other.TryGetComponent(out NetworkIdentity networkIdentity))
            {
                SceneMessage message = new SceneMessage { sceneName = subScene, sceneOperation = SceneOperation.UnloadAdditive };
                networkIdentity.connectionToClient.Send(message);
            }
        }
    }

SceneMassage

客户端加载场景的机制是:

NetworkManager中有SceneMassage事件的监听
NetworkClient.RegisterHandler<SceneMessage>(OnClientSceneInternal, false);

接收到SceneMassage后,会自动加载场景

最后

综上所述,Additive Levels是一个具体的实现示例,重点展示如何在多人环境中通过附加场景管理和传送玩家,

而Additive Scenes则是Unity提供的一个更普遍的功能,支持在同一游戏会话中使用多个场景,并且可以通过服务器检测来决定客户端什么时候加载SubScene。达到客户端按需加载必要场景的目的。

通过理解这两者的差别,开发者可以更加有效地利用Unity的场景管理系统,以实现更复杂的游戏机制和体验。

好了,这篇文章就到这里,希望这篇文章对你有所帮助。

相关推荐
omegayy1 天前
Unity 2022.3.x部分Android设备播放视频黑屏问题
android·unity·视频播放·黑屏
与火星的孩子对话1 天前
Unity3D开发AI桌面精灵/宠物系列 【三】 语音识别 ASR 技术、语音转文本多平台 - 支持科大讯飞、百度等 C# 开发
人工智能·unity·c#·游戏引擎·语音识别·宠物
向宇it1 天前
【零基础入门unity游戏开发——2D篇】2D 游戏场景地形编辑器——TileMap的使用介绍
开发语言·游戏·unity·c#·编辑器·游戏引擎
牙膏上的小苏打23332 天前
Unity Surround开关后导致获取主显示器分辨率错误
unity·主屏幕
Unity大海2 天前
诠视科技Unity SDK开发环境配置、项目设置、apk打包。
科技·unity·游戏引擎
浅陌sss2 天前
Unity中 粒子系统使用整理(一)
unity·游戏引擎
维度攻城狮2 天前
实现在Unity3D中仿真汽车,而且还能使用ros2控制
python·unity·docker·汽车·ros2·rviz2
为你写首诗ge2 天前
【Unity网络编程知识】FTP学习
网络·unity
神码编程3 天前
【Unity】 HTFramework框架(六十四)SaveDataRuntime运行时保存组件参数、预制体
unity·编辑器·游戏引擎
菲fay3 天前
Unity 单例模式写法
unity·单例模式