Unity框架YouYouFramework学习第2篇:启动框架
1. YouYou框架内置模块
在最新的 YouYou Framework 中, 包含以下内置模块:
数据表 (DataTable) - 将游戏数据以表格(Excel)的形式进行配置后,可以使用此模块读取数据表。数据表的格式是支持自定义的。
有限状态机 (FSM) - 提供创建、使用和销毁有限状态机的功能。方便管理多个状态机. 适合系统开发, 可以直接切状态, 相对简单.
资源 (Loader) - 支持同步加载和UniTask异步加载, 使用了一套完整的加载资源体系。像数据表、Shader、场景、界面、角色等任何资源,都可使用Loader进行加载, 并自动管理资源引用计数。
Web 请求 (Http) - 提供使用短连接的功能,可以用 Get 或者 Post 方法向服务器发送请求并获取响应数据,可指定允许几个 Web 请求器进行同时请求。
数据模型 (Model) - 基于MVC思想, 管理运行时的所有Model, 以便统一管理与释放运行时数据。
本地数据存档 (PlayerPrefs) - 基于Unity官方PlayerPrefs的本地数据存档, 支持存Object对象,拥有更好的性能。
流程 (Procedure) - 是贯穿游戏运行时整个生命周期的有限状态机。通过流程,将不同的游戏状态进行解耦将是一个非常好的习惯。对于网络游戏,你可能需要如检查资源流程、更新资源流程、检查服务器列表流程、选择服务器流程、登录服务器流程、创建角色流程等流程,而对于单机游戏,你可能需要在游戏选择菜单流程和游戏实际玩法流程之间做切换。如果想增加流程,只要派生自 ProcedureBase 类并实现自己的流程类即可使用。
场景 (Scene) - 提供场景管理的功能,可以同时加载多个场景,也可以随时卸载任何一个场景,从而很容易地实现场景的分部加载。
任务 (Task) - 提供了Action委托的分组管理, 支持"顺序链式调用", 在一定程度上解决了委托套娃的现象
界面 (UI) - 提供管理界面和界面组的功能,如 激活界面、改变界面层级 界面反切(返回上一个界面)等。界面使用结束后可以不立刻销毁(UI池),从而等待下一次重新使用。
2. 框架启动GameEntry
在第1篇详细介绍了,通过脚本MainEntry的start函数启动游戏框架。
private void Start(){//开始检查更新CheckVersionCtrl.Instance.CheckVersionChange(async () =>{//检查更新完成, 加载Hotfix代码(HybridCLR)await HotfixCtrl.Instance.LoadHotifx();//启动YouYouFramework框架入口GameObject gameEntryAsset = await Addressables.LoadAssetAsync<GameObject>("Assets/Game/Download/Prefab/GameEntry.prefab");Instantiate(gameEntryAsset);});}
详细介绍GameEntry脚本
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Audio;
using UnityEngine.UI;namespace YouYouFramework
{/// <summary>/// 游戏框架入口, 几乎所有框架模块都在这里调用/// </summary>public class GameEntry : MonoBehaviour{//管理器属性public static FsmManager Fsm { get; private set; }public static ProcedureManager Procedure { get; private set; }public static DataTableManager DataTable { get; private set; }public static ModelManager Model { get; private set; }public static HttpManager Http { get; private set; }public static PoolManager Pool { get; private set; }public static YouYouSceneManager Scene { get; private set; }public static LoaderManager Loader { get; private set; }public static UIManager UI { get; private set; }public static AudioManager Audio { get; private set; }public static PlayerPrefsManager PlayerPrefs { get; private set; }public static TaskManager Task { get; private set; }public static GameEntry Instance { get; private set; }private void Awake(){Log(LogCategory.Procedure, "GameEntry.OnAwake()");Instance = this;}private void Start(){Log(LogCategory.Procedure, "GameEntry.OnStart()");//在new的构造函数中, 构造自身, 模块之间不可互相调用, 因为其他模块可能是nullFsm = new FsmManager();Procedure = new ProcedureManager();DataTable = new DataTableManager();Model = new ModelManager();Http = new HttpManager();Pool = new PoolManager();Scene = new YouYouSceneManager();Loader = new LoaderManager();UI = new UIManager();Audio = new AudioManager();PlayerPrefs = new PlayerPrefsManager();Task = new TaskManager();//在Init中, 模块之间可互相调用Log(LogCategory.Procedure, "Procedure.Init Before");Procedure.Init();Log(LogCategory.Procedure, "Procedure.Init After");Audio.Init();Log(LogCategory.Procedure, "Audio.Init After");//进入第一个流程Procedure.ChangeState(ProcedureManager.EState.Launch);}void Update(){Procedure.OnUpdate();Pool.OnUpdate();Scene.OnUpdate();UI.OnUpdate();Audio.OnUpdate();Task.OnUpdate();}}
}
在Start函数中初始化各模块:
状态机Fsm, 流程Procedure, 数据表DataTable, 数据层Mode, 网络请求Http, 对象池Pool, 场景Scene, 资源加载Loader, 界面模块UI, 音频Audio, 本地数据存储PlayerPrefs, 任务Task.
最后进入第一个流程:ProcedureLaunch
3. 详细介绍启动流程Procedure
/// <summary>/// 流程状态/// </summary>public enum EState{/// <summary>/// 初始化/// </summary>Launch,/// <summary>/// 预加载/// </summary>Preload,/// <summary>/// 登录/// </summary>Login,/// <summary>/// 游戏主流程/// </summary>Main}
ProcedureLaunch 游戏启动流程
ProcedurePreload 游戏资源预加载流程
ProcedureLogin 游戏登录流程
ProcedureMain 游戏主流程
启动流程(Launch) => 预加载流程(Preload) => 登录流程(Login) => 游戏主流程(Main)=> 进入游戏主场景
a. 启动流程(ProcedureLaunch)
/// <summary>
/// 启动流程
/// </summary>
public class ProcedureLaunch : ProcedureBase
{private string[] permissions = new string[]{"android.permission.WRITE_EXTERNAL_STORAGE"};public override void OnEnter(int lastState){base.OnEnter(lastState);//获取安卓权限permissions.ToList().ForEach(s =>{//if (!Permission.HasUserAuthorizedPermission(s)) Permission.RequestUserPermission(s);});GameEntry.Procedure.ChangeState(ProcedureManager.EState.Preload);}
}
在进入启动流程OnEnter, 默认只有安卓权限的检查。 完成检查后启动预加载流程。
GameEntry.Procedure.ChangeState(ProcedureManager.EState.Preload);
b. 预加载流程(ProcedurePreload)
/// <summary>
/// 预加载流程
/// </summary>
public class ProcedurePreload : ProcedureBase
{/// <summary>/// 目标进度(实际进度)/// </summary>private float m_TargetProgress;/// <summary>/// 当前进度(模拟进度)/// </summary>private float m_CurrProgress;public override void OnEnter(int lastState){base.OnEnter(lastState);MainEntry.Instance.ActionPreloadBegin?.Invoke();m_CurrProgress = 0;BeginTask();}public override void OnUpdate(float elapseSeconds){base.OnUpdate(elapseSeconds);//模拟加载进度条if (m_CurrProgress < m_TargetProgress){//根据实际情况调节速度, 加载已完成和未完成, 模拟进度增值速度分开计算!if (m_TargetProgress < 1){m_CurrProgress += Time.deltaTime * 0.5f;}else{m_CurrProgress += Time.deltaTime * 0.8f;}m_CurrProgress = Mathf.Min(m_CurrProgress, m_TargetProgress);MainEntry.Instance.ActionPreloadUpdate?.Invoke(m_CurrProgress);}if (m_CurrProgress >= 1){MainEntry.Instance.ActionPreloadComplete?.Invoke();//进入到业务流程GameEntry.Procedure.ChangeState(ProcedureManager.EState.Login);}}/// <summary>/// 开始任务/// </summary>private void BeginTask(){TaskGroup taskGroup = GameEntry.Task.CreateTaskGroup();//加载自定义Shader//taskGroup.AddTask(async (taskRoutine) =>//{// AssetBundle bundle = await GameEntry.Loader.LoadAssetBundleAsync(YFConstDefine.CusShadersAssetBundlePath);// bundle.LoadAllAssets();// //Shader.WarmupAllShaders();// taskRoutine.TaskComplete();//});//加载ExceltaskGroup.AddTask(async (taskRoutine) =>{await GameEntry.DataTable.LoadDataAllTable();taskRoutine.TaskComplete();});taskGroup.OnCompleteOne = () =>{m_TargetProgress = taskGroup.CurrCount / (float)taskGroup.TotalCount;};taskGroup.Run();}}
m_CurrProgress 预加载资源当前进度
m_TargetProgress 预加载资源的目标进度
在进入流程OnEnter逻辑:
(1) 调用MainEntry回调预加载开始事件 ActionPreloadBegin
(2) 预加载进度初始化 m_CurrProgress = 0;
(3) 启动Task预加载,默认只有加载数据表 await GameEntry.DataTable.LoadDataAllTable();
OnUpdate函数中更新加载进度,当完成预加载后,启动登录流程:
GameEntry.Procedure.ChangeState(ProcedureManager.EState.Login);
c. 登录流程ProcedureLogin
/// <summary>
/// 登录流程
/// </summary>
public class ProcedureLogin : ProcedureBase
{public override void OnEnter(int lastState){base.OnEnter(lastState);GameEntry.Procedure.ChangeState(ProcedureManager.EState.Main);}internal override void OnDestroy(){base.OnDestroy();}
}
默认登录流程没有实际逻辑,只是进入的时候跳转主流程Main, 实际开发中接入游戏服务器登录,完成登录后进入游戏主流程:
GameEntry.Procedure.ChangeState(ProcedureManager.EState.Main);
d. 主流程ProcedureMain
/// <summary>
/// 游戏主流程
/// </summary>
public class ProcedureMain : ProcedureBase
{public override void OnEnter(int lastState){base.OnEnter(lastState);GameEntry.UI.OpenUIForm<LoadingForm>();GameEntry.Scene.LoadSceneAction(SceneGroupName.Main);}public override void OnLeave(int newState){base.OnLeave(newState);//退出登录时, 清空业务数据GameEntry.Model.Clear();}
}
进入游戏主流程,基本进入游戏逻辑,默认打开加载弹窗LoadingForm, 进入游戏的主场景Main.