首先我们要理解一个框架的思想,必不可少的就是阅读其中的代码。
故而这节主要是阅读框架的启动流程,从Scene找到启动游戏的入口,也就是Global下挂载的MonoBehaviour脚本,打开Init,边阅读边写注释:

代码以及注释

//Init.cs的代码
using System;
using CommandLine;
using UnityEngine;

namespace ET
{
    /// <summary>
    /// 整个框架入口 继承MonoBehaviour
    /// </summary>
    public class Init: MonoBehaviour
    {
        private void Start()
        {
            this.StartAsync().Coroutine();
        }
        
        private async ETTask StartAsync()
        {
            //防止切换场景后销毁
            DontDestroyOnLoad(gameObject);
            
            //监听未处理的异常,然后打印
            AppDomain.CurrentDomain.UnhandledException += (sender, e) =>
            {
                Log.Error(e.ExceptionObject.ToString());
            };

            // 服务器启动服务的命令行参数
            string[] args = "".Split(" ");
            //解析配置
            Parser.Default.ParseArguments<Options>(args)
                .WithNotParsed(error => throw new Exception($"命令行格式错误! {error}"))
                .WithParsed((o)=>World.Instance.AddSingleton(o));
            //开始游戏配置的文件夹
            Options.Instance.StartConfig = $"StartConfig/Localhost";
            
            //添加Logger单例
            World.Instance.AddSingleton<Logger>().Log = new UnityLogger();
            ETTask.ExceptionHandler += Log.Error;
            
            //添加时间信息类单例
            World.Instance.AddSingleton<TimeInfo>();
            //添加纤程管理类单例
            World.Instance.AddSingleton<FiberManager>();

            //异步等待添加资源加载组件单例,并且执行YooAsset资源包初始化(ET框架没有资源热更,自行编写)
            await World.Instance.AddSingleton<ResourcesComponent>().CreatePackageAsync("DefaultPackage", true);
            
            //添加代码加载器单例
            CodeLoader codeLoader = World.Instance.AddSingleton<CodeLoader>();
            //执行代码热更文件的加载
            await codeLoader.DownloadAsync();
            //执行代码热更
            codeLoader.Start();
        }

        private void Update()
        {
            //更新时间信息
            TimeInfo.Instance.Update();
            //更新纤程管理器
            FiberManager.Instance.Update();
        }

        private void LateUpdate()
        {
            //更新纤程管理器
            FiberManager.Instance.LateUpdate();
        }

        private void OnApplicationQuit()
        {
            //关闭,移除全部单例类
            World.Instance.Dispose();
        }
    }
}
//ResourcesComponent.cs的代码
public async ETTask CreatePackageAsync(string packageName, bool isDefault = false)
{
    //加载YooAsset配置好的包
    ResourcePackage package = YooAssets.CreatePackage(packageName);
    if (isDefault)
    {
        YooAssets.SetDefaultPackage(package);
    }

    //读取全局配置文件,包括代码执行类型(客户端/服务端/双端)、打包类型(Develop/Release)、App类型(状态同步/帧同步)、运行模式
    GlobalConfig globalConfig = Resources.Load<GlobalConfig>("GlobalConfig");
    //运行模式
    EPlayMode ePlayMode = globalConfig.EPlayMode;

    //资源初始化
    switch (ePlayMode)
    {
        //编辑器下的模拟模式
        case EPlayMode.EditorSimulateMode:
        {
            EditorSimulateModeParameters createParameters = new();
            createParameters.SimulateManifestFilePath = EditorSimulateModeHelper.SimulateBuild("ScriptableBuildPipeline", packageName);
            await package.InitializeAsync(createParameters).Task;
            break;
        }
        //离线运行模式
        case EPlayMode.OfflinePlayMode:
        {
            OfflinePlayModeParameters createParameters = new();
            await package.InitializeAsync(createParameters).Task;
            break;
        }
        //联网运行模式
        case EPlayMode.HostPlayMode:
        {
            string defaultHostServer = GetHostServerURL();
            string fallbackHostServer = GetHostServerURL();
            HostPlayModeParameters createParameters = new();
            createParameters.BuildinQueryServices = new GameQueryServices();
            createParameters.RemoteServices = new RemoteServices(defaultHostServer, fallbackHostServer);
            await package.InitializeAsync(createParameters).Task;
            break;
        }
        default:
            throw new ArgumentOutOfRangeException();
    }
}
//CodeLoader.cs的代码
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using HybridCLR;
using UnityEngine;

namespace ET
{
    /// <summary>
    /// 代码加载器
    /// </summary>
    public class CodeLoader: Singleton<CodeLoader>, ISingletonAwake
    {
        /// <summary>
        /// model程序集
        /// </summary>
        private Assembly modelAssembly;
        /// <summary>
        /// modelView程序集
        /// </summary>
        private Assembly modelViewAssembly;

        /// <summary>
        /// Dll
        /// </summary>
        private Dictionary<string, TextAsset> dlls;
        /// <summary>
        /// 静态编译Dll
        /// </summary>
        private Dictionary<string, TextAsset> aotDlls;
        private bool enableDll;

        public void Awake()
        {
            //赋值是否启用Dll
            this.enableDll = Resources.Load<GlobalConfig>("GlobalConfig").EnableDll;
        }

        /// <summary>
        /// 加载Dll
        /// </summary>
        public async ETTask DownloadAsync()
        {
            //非编辑器下才需要加载Dll
            if (!Define.IsEditor)
            {
                //这里的API调用,路径参数只需要文件夹里的任一文件即可,具体底层会获取当前资源包文件夹里的全部文件
                this.dlls = await ResourcesComponent.Instance.LoadAllAssetsAsync<TextAsset>($"Assets/Bundles/Code/Unity.Model.dll.bytes");
                this.aotDlls = await ResourcesComponent.Instance.LoadAllAssetsAsync<TextAsset>($"Assets/Bundles/AotDlls/mscorlib.dll.bytes");
            }
        }

        /// <summary>
        /// 非Mono生命周期,在DownloadAsync后执行
        /// </summary>
        public void Start()
        {
            //如果不是编辑器,直接读取已经加载好的Dll
            if (!Define.IsEditor)
            {
                byte[] modelAssBytes = this.dlls["Unity.Model.dll"].bytes;
                byte[] modelPdbBytes = this.dlls["Unity.Model.pdb"].bytes;
                byte[] modelViewAssBytes = this.dlls["Unity.ModelView.dll"].bytes;
                byte[] modelViewPdbBytes = this.dlls["Unity.ModelView.pdb"].bytes;
                // 如果需要测试,可替换成下面注释的代码直接加载Assets/Bundles/Code/Unity.Model.dll.bytes,但真正打包时必须使用上面的代码
                //modelAssBytes = File.ReadAllBytes(Path.Combine(Define.CodeDir, "Unity.Model.dll.bytes"));
                //modelPdbBytes = File.ReadAllBytes(Path.Combine(Define.CodeDir, "Unity.Model.pdb.bytes"));
                //modelViewAssBytes = File.ReadAllBytes(Path.Combine(Define.CodeDir, "Unity.ModelView.dll.bytes"));
                //modelViewPdbBytes = File.ReadAllBytes(Path.Combine(Define.CodeDir, "Unity.ModelView.pdb.bytes"));

                //HybridCLR的IL2CPP的处理方式
                if (Define.EnableIL2CPP)
                {
                    foreach (var kv in this.aotDlls)
                    {
                        TextAsset textAsset = kv.Value;
                        //补充元数据,使用超集的模式(在允许使用裁剪后的Dll的情况下,还允许使用原始Dll进行补充)
                        RuntimeApi.LoadMetadataForAOTAssembly(textAsset.bytes, HomologousImageMode.SuperSet);
                    }
                }
                //加载model的程序集
                this.modelAssembly = Assembly.Load(modelAssBytes, modelPdbBytes);
                //加载modelView的程序集
                this.modelViewAssembly = Assembly.Load(modelViewAssBytes, modelViewPdbBytes);
            }
            else
            {
                //编辑器模式下,如果启动Dll的模式
                if (this.enableDll)
                {
                    //直接从文件读取后加载,无需补充元数据
                    byte[] modelAssBytes = File.ReadAllBytes(Path.Combine(Define.CodeDir, "Unity.Model.dll.bytes"));
                    byte[] modelPdbBytes = File.ReadAllBytes(Path.Combine(Define.CodeDir, "Unity.Model.pdb.bytes"));
                    byte[] modelViewAssBytes = File.ReadAllBytes(Path.Combine(Define.CodeDir, "Unity.ModelView.dll.bytes"));
                    byte[] modelViewPdbBytes = File.ReadAllBytes(Path.Combine(Define.CodeDir, "Unity.ModelView.pdb.bytes"));
                    this.modelAssembly = Assembly.Load(modelAssBytes, modelPdbBytes);
                    this.modelViewAssembly = Assembly.Load(modelViewAssBytes, modelViewPdbBytes);
                }
                else
                {
                    //反射加载
                    Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
                    foreach (Assembly ass in assemblies)
                    {
                        string name = ass.GetName().Name;
                        if (name == "Unity.Model")
                        {
                            this.modelAssembly = ass;
                        }
                        else if (name == "Unity.ModelView")
                        {
                            this.modelViewAssembly = ass;
                        }

                        if (this.modelAssembly != null && this.modelViewAssembly != null)
                        {
                            break;
                        }
                    }
                }
            }
            
            //加载热更代码(元组?),与本方法类似
            (Assembly hotfixAssembly, Assembly hotfixViewAssembly) = this.LoadHotfix();
            
            //添加CodeTypes的单例并Awake
            World.Instance.AddSingleton<CodeTypes, Assembly[]>(new[]
            {
                //之前加载完毕的程序集
                typeof (World).Assembly, typeof (Init).Assembly, this.modelAssembly, this.modelViewAssembly, hotfixAssembly,hotfixViewAssembly
            });

            //热更代码结束,进入正式逻辑,反射执行ET.Entry类的Start方法
            IStaticMethod start = new StaticMethod(this.modelAssembly, "ET.Entry", "Start");
            start.Run();
        }

        /// <summary>
        /// 加载热更程序集
        /// </summary>
        private (Assembly, Assembly) LoadHotfix()
        {
            byte[] hotfixAssBytes;
            byte[] hotfixPdbBytes;
            byte[] hotfixViewAssBytes;
            byte[] hotfixViewPdbBytes;
            Assembly hotfixAssembly = null;
            Assembly hotfixViewAssembly = null;
            if (!Define.IsEditor)
            {
                hotfixAssBytes = this.dlls["Unity.Hotfix.dll"].bytes;
                hotfixPdbBytes = this.dlls["Unity.Hotfix.pdb"].bytes;
                hotfixViewAssBytes = this.dlls["Unity.HotfixView.dll"].bytes;
                hotfixViewPdbBytes = this.dlls["Unity.HotfixView.pdb"].bytes;
                // 如果需要测试,可替换成下面注释的代码直接加载Assets/Bundles/Code/Hotfix.dll.bytes,但真正打包时必须使用上面的代码
                //hotfixAssBytes = File.ReadAllBytes(Path.Combine(Define.CodeDir, "Unity.Hotfix.dll.bytes"));
                //hotfixPdbBytes = File.ReadAllBytes(Path.Combine(Define.CodeDir, "Unity.Hotfix.pdb.bytes"));
                //hotfixViewAssBytes = File.ReadAllBytes(Path.Combine(Define.CodeDir, "Unity.HotfixView.dll.bytes"));
                //hotfixViewPdbBytes = File.ReadAllBytes(Path.Combine(Define.CodeDir, "Unity.HotfixView.pdb.bytes"));
                hotfixAssembly = Assembly.Load(hotfixAssBytes, hotfixPdbBytes);
                hotfixViewAssembly = Assembly.Load(hotfixViewAssBytes, hotfixViewPdbBytes);
            }
            else
            {
                if (this.enableDll)
                {
                    hotfixAssBytes = File.ReadAllBytes(Path.Combine(Define.CodeDir, "Unity.Hotfix.dll.bytes"));
                    hotfixPdbBytes = File.ReadAllBytes(Path.Combine(Define.CodeDir, "Unity.Hotfix.pdb.bytes"));
                    hotfixViewAssBytes = File.ReadAllBytes(Path.Combine(Define.CodeDir, "Unity.HotfixView.dll.bytes"));
                    hotfixViewPdbBytes = File.ReadAllBytes(Path.Combine(Define.CodeDir, "Unity.HotfixView.pdb.bytes"));
                    hotfixAssembly = Assembly.Load(hotfixAssBytes, hotfixPdbBytes);
                    hotfixViewAssembly = Assembly.Load(hotfixViewAssBytes, hotfixViewPdbBytes);
                }
                else
                {
                    Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
                    foreach (Assembly ass in assemblies)
                    {
                        string name = ass.GetName().Name;
                        if (name == "Unity.Hotfix")
                        {
                            hotfixAssembly = ass;
                        }
                        else if (name == "Unity.HotfixView")
                        {
                            hotfixViewAssembly = ass;
                        }

                        if (hotfixAssembly != null && hotfixViewAssembly != null)
                        {
                            break;
                        }
                    }
                }
            }
            
            return (hotfixAssembly, hotfixViewAssembly);
        }

        public void Reload()
        {
            (Assembly hotfixAssembly, Assembly hotfixViewAssembly) = this.LoadHotfix();

            CodeTypes codeTypes = World.Instance.AddSingleton<CodeTypes, Assembly[]>(new[]
            {
                typeof (World).Assembly, typeof (Init).Assembly, this.modelAssembly, this.modelViewAssembly, hotfixAssembly,
                hotfixViewAssembly
            });
            codeTypes.CreateCode();

            Log.Info($"reload dll finish!");
        }
    }
}
//CodeTypes.cs的代码
using System.Collections.Generic;
using System.Reflection;
using System;

namespace ET
{
    /// <summary>
    /// 代码类型集合
    /// </summary>
    public class CodeTypes: Singleton<CodeTypes>, ISingletonAwake<Assembly[]>
    {
        private readonly Dictionary<string, Type> allTypes = new();
        private readonly UnOrderMultiMapSet<Type, Type> types = new();
        
        public void Awake(Assembly[] assemblies)
        {
            //获取Dll的全部类型
            Dictionary<string, Type> addTypes = AssemblyHelper.GetAssemblyTypes(assemblies);
            foreach ((string fullName, Type type) in addTypes)
            {
                //赋值
                this.allTypes[fullName] = type;
                
                //抽象类型
                if (type.IsAbstract)
                {
                    continue;
                }
                
                // 记录所有的有BaseAttribute标记的的类型(例如[EventSystem]这种类型)
                object[] objects = type.GetCustomAttributes(typeof(BaseAttribute), true);
                foreach (object o in objects)
                {
                    this.types.Add(o.GetType(), type);
                }
            }
        }

        public HashSet<Type> GetTypes(Type systemAttributeType)
        {
            if (!this.types.ContainsKey(systemAttributeType))
            {
                return new HashSet<Type>();
            }

            return this.types[systemAttributeType];
        }

        public Dictionary<string, Type> GetTypes()
        {
            return allTypes;
        }

        public Type GetType(string typeName)
        {
            return this.allTypes[typeName];
        }
        
        public void CreateCode()
        {
            var hashSet = this.GetTypes(typeof (CodeAttribute));
            foreach (Type type in hashSet)
            {
                object obj = Activator.CreateInstance(type);
                ((ISingletonAwake)obj).Awake();
                World.Instance.AddSingleton((ASingleton)obj);
            }
        }
    }
}
//Entry.cs的代码
using MemoryPack;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Serializers;

namespace ET
{
    public struct EntryEvent1
    {
    }   
    
    public struct EntryEvent2
    {
    } 
    
    public struct EntryEvent3
    {
    }
    
    public static class Entry
    {
        public static void Init()
        {
            
        }
        
        public static void Start()
        {
            StartAsync().Coroutine();
        }
        
        private static async ETTask StartAsync()
        {
            //根据平台不同初始化时间精度
            WinPeriod.Init();

            // 注册Mongo type
            MongoRegister.Init();
            // 注册Entity序列化器
            EntitySerializeRegister.Init();
            //创建无需热重载的通用组件
            World.Instance.AddSingleton<IdGenerater>();
            World.Instance.AddSingleton<OpcodeType>();
            World.Instance.AddSingleton<ObjectPool>();
            World.Instance.AddSingleton<MessageQueue>();
            World.Instance.AddSingleton<NetServices>();
            World.Instance.AddSingleton<NavmeshComponent>();
            World.Instance.AddSingleton<LogMsg>();
            
            //创建需要reload的各种code单例(各种消息分发组件/EventSystem)
            CodeTypes.Instance.CreateCode();
            
            //添加配置加载单例,加载全部标记[Config]标签头的类
            await World.Instance.AddSingleton<ConfigLoader>().LoadAsync();

            //因为上面初始化了EventSystem,所以这里创建纤程后会分发到对应的事件管线
            //检查SceneType.Main的引用后,发现该事件的Invoke为AInvokeHandler类
            await FiberManager.Instance.Create(SchedulerType.Main, ConstFiberId.Main, 0, SceneType.Main, "");
        }
    }
}

最后,在创建完纤程后抛出的事件里,会创建一个根实体SceneFiber下面,而后面又会给这个根实体Scene挂载很多的组件实体,也就形成了如下的树状结构图。

注:Scene并非只允许挂载在Fiber下,而是任意实体下都可。

整个框架树状结构图

最后修改:2024 年 05 月 16 日
如果觉得我的文章对你有用,请随意赞赏