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主世界

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