MegaCity0

31 篇文章 2 订阅
31 篇文章 3 订阅

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 12 Mon 19 Mon 26 1. ForEach 2. IJobForEach 3. IJobChunk 4. SubScene 5. SpawnFromMonoBehaviour 6. SpawnFromEntity 7. SpawnAndRemove 休息 修正更新计划 参加表哥婚礼 进阶:FixedTimestepWorkaround 进阶:BoidExample 初级:SceneSwitcher 我是休息时间 资源整合 部署服务器 启动流程 登录流程 MegaCity 游戏主世界 待计划 待计划 待计划 待计划 我是休息时间 待计划 待计划 待计划 待计划 待计划 我是休息时间 读取Excel自动生成Entity 读取Excel自动生成Component 读取数据库自动生成Entity 读取数据库自动生成Component ESC 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主世界

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CloudHu1989

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值