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的场景管理系统,以实现更复杂的游戏机制和体验。

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

相关推荐
omegayy2 小时前
.NET framework、Core和Standard都是什么?
unity·c#·.net
龚子亦4 小时前
Unity使用Vuforia插件进行AR开发
unity·游戏引擎·ar
Tatalaluola14 小时前
【《游戏编程模式》实战04】状态模式实现敌人AI
学习·游戏·unity·c#·状态模式
谢斯1 天前
[Unity]MacOS下开发Unity
macos·unity·游戏引擎
奔跑的犀牛先生1 天前
unity学习14:unity里的C#脚本的几个基本生命周期方法, 脚本次序order等
学习·unity·c#
Thomas_YXQ1 天前
Unity3D中基于ILRuntime的组件化开发详解
开发语言·网络·游戏·unity·unity3d
我爱一根柴哈2 天前
Unity 3D游戏开发从入门进阶到高级
3d·unity·游戏引擎
fanfan_hongyun2 天前
Unity自定义编辑器:基于枚举类型动态显示属性
unity·编辑器·游戏引擎
weixin_448065312 天前
Unity学习笔记(七)使用状态机重构角色攻击
笔记·学习·unity