MegaCity0

MegaCity

關於UnityMMO的啓動流程和登錄流程的,作者大鵬已經非常詳細地寫出來了,這裏是大鵬寫的啓動流程大鵬寫的登錄流程,感興趣的朋友可以先了解一下,我們將深入這個項目進行學習。
原本今天計劃要講啓動流程的,可是發現已經沒有什麼可以說的了,大鵬已經寫得非常詳細了,於是就補充說一下官方的案例MegaCity好了,因爲我才找到MegaCity的源碼,原來官方早就開源了,只是資源非常大,所以沒有放在Github上!

開始之前的準備工作:

0下載Unity編輯器(Unity 2019.1.0 Beta 7 or 更新的版本),if(已經下載了)continue;
1點擊Megacity源代碼下載Zip壓縮包;if(已經下載了)continue;
2這個包有7.11G,解壓後17.6 GB,打開Unity Hub->項目->添加,把MegaCity_GDC2019_Release_OC添加到項目中;
3用Unity Hub打開官方開源項目:MegaCity_GDC2019_Release_OC,等待Unity進行編譯工作;
4光編譯工作就用了兩個多小時,打開Scenes/Megacity場景後,整個電腦都變得卡頓起來,這是接觸過的最宏大的場景。

ECS中SubScene的使用

在Megacity的場景中,我們可以看到大量的SubScene,如下圖所示:
在這裏插入圖片描述
在之前的學習筆記中,我們已經研究過SubScene了,詳細參考基於Unity2019最新ECS架構開發MMO遊戲筆記3
總結一下SubScene的特性好了:

  1. 添加到SubScene的對象會變成實體,在運行的時候可快速加載。
  2. 可以在編輯器模式下隨時修改SubScene的資源。
  3. SubScene可以有效提高大場景的性能。

SubScene的加載和卸載是由一套系統控制,否則數以萬計的實體全部加載太浪費性能了,畢竟大部分實體其實都不在視野範圍內,所以SubScene是採用這套動態加載系統來控制的,我們先看看這套系統的邏輯吧,先從StreamingLogicConfigComponent開始,這裏取名爲StreamingLogicConfigComponent 令我感到困惑:

/// <summary>
/// E:流加載邏輯實體
/// </summary>
public class StreamingLogicConfigComponent : MonoBehaviour, IConvertGameObjectToEntity
{
    /// <summary>
    /// 實例化配置組件
    /// </summary>
    public StreamingLogicConfig Config = new StreamingLogicConfig
    {
        DistanceForStreamingIn = 600,//進場距離,達到該距離的SubScene流將被快速加載
        DistanceForStreamingOut = 800//離場距離
    };

    void OnDrawGizmosSelected()
    {
        Gizmos.color = Color.green;
        Gizmos.DrawWireSphere(transform.position, Config.DistanceForStreamingIn);
        Gizmos.color = Color.red;
        Gizmos.DrawWireSphere(transform.position, Config.DistanceForStreamingOut);
    }
    /// <summary>
    /// 轉化時將組件以及數據添加到實體上
    /// </summary>
    /// <param name="entity">實體</param>
    /// <param name="dstManager">目標實體管理器</param>
    /// <param name="conversionSystem">轉化系統</param>
    public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem)
    {
        dstManager.AddComponentData(entity, Config);
    }
}

我之所以認定StreamingLogicConfigComponent 是實體E,是因爲它實現了IConvertGameObjectToEntity接口。
下面是最簡單的C部分StreamingLogicConfig:

[Serializable]
public struct StreamingLogicConfig : IComponentData
{
    public float DistanceForStreamingIn;
    public float DistanceForStreamingOut;
}

沒啥好說的,看StreamingLogicSystem吧:

/// <summary>
/// 加載流邏輯系統
/// </summary>
[UpdateInGroup(typeof(InitializationSystemGroup))]//初始化系統組
[ExecuteAlways]//總是執行
public class StreamingLogicSystem : JobComponentSystem
{
    /// <summary>
    /// 實體命令緩存系統
    /// </summary>
    EntityCommandBufferSystem m_EntityCommandBufferSystem;
    NativeList<Entity> m_AddRequestList;//添加請求的原生列表
    NativeList<Entity> m_RemoveRequestList;//移除請求的原生列表
    /// <summary>
    /// 子場景進場
    /// </summary>
    [BurstCompile]
    [ExcludeComponent(typeof(RequestSceneLoaded))]
    struct StreamSubScenesIn : IJobProcessComponentDataWithEntity<SceneData>
    {
        public NativeList<Entity> AddRequestList;//添加請求列表
        public float3 CameraPosition;//攝像機位置
        public float MaxDistanceSquared;//最大距離方體
        /// <summary>
        /// 執行:小於最大距離則將實體加入請求列表
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="index"></param>
        /// <param name="sceneData"></param>
        public void Execute(Entity entity, int index, [ReadOnly]ref SceneData sceneData)
        {
            var distanceSq = sceneData.BoundingVolume.DistanceSq(CameraPosition);
            if (distanceSq < MaxDistanceSquared)
                AddRequestList.Add(entity);
        }
    }
    /// <summary>
    /// 子場景出場
    /// </summary>
    [BurstCompile]
    [RequireComponentTag(typeof(RequestSceneLoaded))]
    struct StreamSubScenesOut : IJobProcessComponentDataWithEntity<SceneData>
    {
        public NativeList<Entity> RemoveRequestList;
        public float3 CameraPosition;
        public float MaxDistanceSquared;
        /// <summary>
        /// 執行:大於最大距離則將實體加入移除列表
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="index"></param>
        /// <param name="sceneData"></param>
        public void Execute(Entity entity, int index, [ReadOnly]ref SceneData sceneData)
        {
            if (sceneData.SubSectionIndex == 0)
                return;

            var distanceSq = sceneData.BoundingVolume.DistanceSq(CameraPosition);
            if (distanceSq > MaxDistanceSquared)
                RemoveRequestList.Add(entity);
        }
    }
    /// <summary>
    /// 構建命令緩存任務
    /// </summary>
    struct BuildCommandBufferJob : IJob
    {
        /// <summary>
        /// 實體命令緩存
        /// </summary>
        public EntityCommandBuffer CommandBuffer;
        public NativeArray<Entity> AddRequestArray;
        public NativeArray<Entity> RemoveRequestArray;
        /// <summary>
        /// 執行:遍歷添加列表和移除列表
        /// 通過對列表中的實體添加和移除組件來標記子場景是否加載
        /// 後面進行管理的時候會利用該組件來進行刷選
        /// </summary>
        public void Execute()
        {
            foreach (var entity in AddRequestArray)
            {
                CommandBuffer.AddComponent(entity, default(RequestSceneLoaded));
            }
            foreach (var entity in RemoveRequestArray)
            {
                CommandBuffer.RemoveComponent<RequestSceneLoaded>(entity);
            }
        }
    }
    /// <summary>
    /// 在創建管理器的時候對實體命令緩存系統進行獲取
    /// 這裏相當於初始化工作
    /// </summary>
    protected override void OnCreateManager()
    {
        m_EntityCommandBufferSystem = World.GetOrCreateManager<EndPresentationEntityCommandBufferSystem>();

        m_AddRequestList = new NativeList<Entity>(Allocator.Persistent);
        m_RemoveRequestList = new NativeList<Entity>(Allocator.Persistent);

        RequireSingletonForUpdate<StreamingLogicConfig>();
    }
    /// <summary>
    /// 在摧毀管理器時釋放內存
    /// </summary>
    protected override void OnDestroyManager()
    {
        m_AddRequestList.Dispose();
        m_RemoveRequestList.Dispose();
    }
    /// <summary>
    /// 每幀更新
    /// </summary>
    /// <param name="inputDeps"></param>
    /// <returns></returns>
    protected override JobHandle OnUpdate(JobHandle inputDeps)
    {
        //這是個單例實體,沒看出有什麼特別的
        var configEntity = GetSingletonEntity<StreamingLogicConfig>();
        //緩存配置,方便下面使用
        var config = EntityManager.GetComponentData<StreamingLogicConfig>(configEntity);
        //該組件就掛在攝像機上,所以通過Position來獲取其位置
        var cameraPosition = EntityManager.GetComponentData<LocalToWorld>(configEntity).Position;

        m_AddRequestList.Clear();//每次更新先清空
        var streamInHandle = new StreamSubScenesIn
        {
            AddRequestList = m_AddRequestList,
            CameraPosition = cameraPosition,
            MaxDistanceSquared = config.DistanceForStreamingIn * config.DistanceForStreamingIn
        }.ScheduleSingle(this, inputDeps);//預約

        m_RemoveRequestList.Clear();
        var streamOutHandle = new StreamSubScenesOut
        {
            RemoveRequestList = m_RemoveRequestList,
            CameraPosition = cameraPosition,
            MaxDistanceSquared = config.DistanceForStreamingOut * config.DistanceForStreamingOut
        }.ScheduleSingle(this, inputDeps);

        var combinedHandle = JobHandle.CombineDependencies(streamInHandle, streamOutHandle);
        var commandHandle = new BuildCommandBufferJob
        {
            CommandBuffer = m_EntityCommandBufferSystem.CreateCommandBuffer(),
            AddRequestArray = m_AddRequestList.AsDeferredJobArray(),
            RemoveRequestArray = m_RemoveRequestList.AsDeferredJobArray()
        }.Schedule(combinedHandle);
        m_EntityCommandBufferSystem.AddJobHandleForProducer(commandHandle);

        return commandHandle;
    }
}

邏輯很簡單,進入一定視野範圍則加載,超出視野則移除。

小結

今天外出參加了兩場宴席,晚上回來又是中元節祭祖,所以寫了非常少,明天多補幾篇吧!

更新計劃

Mon 12Mon 19Mon 261. ForEach 2. IJobForEach 3. IJobChunk 4. SubScene 5. SpawnFromMonoBehaviour 6. SpawnFromEntity 7. SpawnAndRemove 休息修正更新計劃參加表哥婚禮進階:FixedTimestepWorkaround進階:BoidExample初級:SceneSwitcher我是休息時間 資源整合 部署服務器 啓動流程 登錄流程 MegaCity 遊戲主世界 待計劃 待計劃 待計劃 待計劃 我是休息時間 待計劃 待計劃 待計劃 待計劃 待計劃 我是休息時間 讀取Excel自動生成Entity 讀取Excel自動生成Component讀取數據庫自動生成Entity讀取數據庫自動生成ComponentESC LuaFrameWork Skynet DOTS 官方示例學習筆記 -----休息-----基於ECS架構開發MMO學習筆記LuaFrameWork學習筆記-----休息-----基於Skynet架構開發服務器學習筆記 製作代碼自動生成工具總結基於Unity2019最新ECS架構開發MMO遊戲筆記

作者的話

AltAlt

如果喜歡我的文章可以點贊支持一下,謝謝鼓勵!如果有什麼疑問可以給我留言,有錯漏的地方請批評指證!
如果有技術難題需要討論,可以加入開發者聯盟:566189328(付費羣)爲您提供有限的技術支持,以及,心靈雞湯!
當然,不需要技術支持也歡迎加入進來,隨時可以請我喝咖啡、茶和果汁!( ̄┰ ̄*)

ECS系列目錄

ECS官方示例1:ForEach

ECS官方案例2:IJobForEach

ECS官方案例3:IJobChunk

ECS官方案例4:SubScene

ECS官方案例5:SpawnFromMonoBehaviour

ECS官方案例6:SpawnFromEntity

ECS官方案例7:SpawnAndRemove

ECS進階:FixedTimestepWorkaround

ECS進階:Boids

ECS進階:場景切換器

ECS進階:MegaCity0

ECS進階:MegaCity1

UnityMMO資源整合&服務器部署

UnityMMO選人流程

UnityMMO主世界

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章