目录
前言:
整个流程:
一、配置路径
二、打包
三、初始化资源
四、测试打包
如果使用了GameFrameWork框架的话,你会发现你点击Build And Run按钮打包运行大概是运行不起来的。本篇就讲了怎么打包游戏运行。
我觉得我对于资源加载这块儿理解也不够深,所以本篇主要记录方法流程,不讲原理。想探究原理的可以先去看看官方手册,看看GF官网,然后看看其它GF大佬的B站视频啊,博客啊啥的(多看看,每一篇都有值得吸收的碎片,然后把这些碎片再拼成自己脑中的体系图景)。
废话不多说,开干!
如过没有配置过路径的话,打开GameFramework的资源工具Resource Editor是这样的:
也就是最右边一列的资产列表是空的。
(关于这个打包工具官网有相关介绍可以先看看)
所以这里需要自己手动添加一个配置文件,告诉这个打包器需要打包的资源路径。
在Assets/GameMain/Configs下手动创建文件ResourceEditor.xml:
内容为:
Assets/GameMain
t:Scene t:Prefab t:Shader t:Model t:Material t:Texture t:AudioClip t:AnimationClip t:AnimatorController t:Font t:TextAsset t:ScriptableObject
l:ResourceInclusive
t:Script
l:ResourceExclusive
Path
(配置文件的内容看英文就明白个大概了,如果想弄懂的话)
配置文件创建了,然后要告诉框架这个配置文件的路径,需要使用一个叫“ResourceEditorConfigPath”的属性来配置。下面来搞。
在Assets/GameMain/Scripts路径下建立Editor文件夹:
文件夹里面手动创建GameFrameworkConfigs.cs(名字实际上应该无所谓):
里面配置一下刚刚创建的配置文件的路径:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using GameFramework;
using UnityGameFramework.Editor.ResourceTools;
using System.IO;
namespace ShadowU
{
public static class GameFrameworkConfigs
{
[ResourceEditorConfigPath]
public static string ResourceEditorConfig = Utility.Path.GetRegularPath(Path.Combine(Application.dataPath, "GameMain/Configs/ResourceEditor.xml"));
}
}
如果不懂为啥可以去恶补一下c#的Attribute的相关知识。
保存。
然后再打开打包的资源编辑器:
这样在右边一列就能够看到自己的资源了。
然后打包的任务通俗的说就是把右边的资源放到左边。这个界面的操作说明官网有(官网为数不多的教程就有讲这个的。。。)
然后关于打AB包怎么组合比较好可以参考Unity的官方手册:为 AssetBundle 准备资源 - Unity 手册 (unity3d.com)
这里为了演示就不一个包一个包打了直接一把梭:
然后左边就是选择打包的文件:
细节不管了,先跑起来再说。
点击右下角save(默认会在Assets/GameFramework/Configs路径下生成ResourceCollection.xml):
然后打开工具的builder:
自己设置好你的Output Directory。
(这里的Internal Resource Version每打一次包就会自增,所以我这里是10.。。。)
save是保存这个界面填写的配置(默认会在Assets/GameFramework/Configs路径下生成ResourceBuilder.xml)。
按Start Build Resources开始打包!
打包后,到自己设置的输出文件夹里:
各个文件夹的介绍官网都有写:使用 AssetBundle 构建工具 | Game Framework
然后我们只需要把 Package(单机模式)或 Packed(可更新模式)中对应版本(如 0_1_0_1)、对应平台的内容(如 windows),完整拷贝至 Unity 工程的 Assets/StreamingAssets 中,即可Build了。(StreamingAssets文件夹自己创建):
然后打包之前可以先在编译器运行一下非编译器资源模式。
把这个取消勾选:
然后运行。。。。。
你会发现报错,运行不了。
因为还有一步。
虽然已经把游戏所需要的资源打包放好了,但是要让程序使用它们还得通过框架对这些资源进行初始化。
不过初始化很简单,就一个语句:
GameEntry.Resource.InitResources(OnInitResourcesComplete);
其中传入的OnInitResourcesComplete是初始化完成的回调函数。
用一个标记来判断是否初始化完成:
m_InitResourcesComplete = false;
回调函数就这么写:
private void OnInitResourcesComplete()
{
m_InitResourcesComplete = true;
Log.Info("Init resources complete.");
}
因为我流程写得相对于StarForce的精简了很多,所以我初始化资源、加载资源、启动游戏等逻辑都放在了ProcedureLaunch.cs里面,所以我加了一点处理来保证初始化资源-->初始化完成-->加载资源-->加载资源完成-->启动游戏这些操作按顺序执行。所以代码如下:
(如果觉得乱直接去学习StarForce的代码即可)
using GameFramework.Fsm;
using GameFramework.Event;
using UnityGameFramework.Runtime;
using System.Collections.Generic;
using UnityEngine;
namespace ShadowU
{
public class ProcedureLaunch : ProcedureBase
{
public override bool UseNativeDialog
{
get
{
return true;
}
}
private Dictionary m_LoadedFlag = new Dictionary();//用来标记是否已经加载
private bool m_InitResourcesComplete = false;
private bool temp = false;
public static readonly string[] DataTableNames = new string[]
{
"Scene",
"Character",
"Entity",
"Enemy"
};
protected override void OnEnter(IFsm procedureOwner)
{
base.OnEnter(procedureOwner);
Debug.Log("launch!");
//初始化资源标记
m_InitResourcesComplete = false;
temp = false;
// 注意:使用单机模式并初始化资源前,需要先构建 AssetBundle 并复制到 StreamingAssets 中,否则会产生 HTTP 404 错误
GameEntry.Resource.InitResources(OnInitResourcesComplete);
//注册事件
GameEntry.Event.Subscribe(LoadConfigSuccessEventArgs.EventId, OnLoadConfigSuccess);
GameEntry.Event.Subscribe(LoadConfigFailureEventArgs.EventId, OnLoadConfigFailure);
GameEntry.Event.Subscribe(LoadDataTableSuccessEventArgs.EventId, OnLoadDataTableSuccess);
GameEntry.Event.Subscribe(LoadDataTableFailureEventArgs.EventId, OnLoadDataTableFailure);
}
protected override void OnLeave(IFsm procedureOwner, bool isShutdown)
{
base.OnLeave(procedureOwner, isShutdown);
//注销事件
GameEntry.Event.Unsubscribe(LoadConfigSuccessEventArgs.EventId, OnLoadConfigSuccess);
GameEntry.Event.Unsubscribe(LoadConfigFailureEventArgs.EventId, OnLoadConfigFailure);
GameEntry.Event.Unsubscribe(LoadDataTableSuccessEventArgs.EventId, OnLoadDataTableSuccess);
GameEntry.Event.Unsubscribe(LoadDataTableFailureEventArgs.EventId, OnLoadDataTableFailure);
}
protected override void OnUpdate(IFsm procedureOwner, float elapseSeconds, float realElapseSeconds)
{
base.OnUpdate(procedureOwner, elapseSeconds, realElapseSeconds);
if (!m_InitResourcesComplete)
{
// 初始化资源未完成则继续等待
return;
}
if (!temp)//temp用于保证这里的代码只执行一次
{
m_LoadedFlag.Clear();
//预加载配置
PreloadResources();
//加载数据表
foreach (string dataTableName in DataTableNames)
{
LoadDataTable(dataTableName);
}
temp = true;
}
foreach (KeyValuePair loadedFlag in m_LoadedFlag)
{
if (!loadedFlag.Value)
{
return;//说明有资源没有加载成功
}
}
//通过加载的配置表来设置下一个转换场景的ID
procedureOwner.SetData("NextSceneId",GameEntry.Config.GetInt("Scene.Menu"));
//一帧后直接转到菜单流程
ChangeState(procedureOwner);
}
private void PreloadResources()
{
LoadConfig("DefaultConfig");
}
private void LoadConfig(string configName)
{
string configAssetName = AssetUtility.GetConfigAsset(configName, false);
m_LoadedFlag.Add(configAssetName, false);
GameEntry.Config.ReadData(configAssetName, this);
}
private void LoadDataTable(string dataTableName)
{
string dataTableAssetName = AssetUtility.GetDataTableAsset(dataTableName, false);
m_LoadedFlag.Add(dataTableAssetName, false);
GameEntry.DataTable.LoadDataTable(dataTableName, dataTableAssetName, this);
}
private void OnLoadConfigSuccess(object sender, GameEventArgs e)
{
LoadConfigSuccessEventArgs ne = (LoadConfigSuccessEventArgs)e;
if (ne.UserData != this)
{
return;
}
m_LoadedFlag[ne.ConfigAssetName] = true; //资源加载成功了则设置一下标记为true
Log.Info("Load config '{0}' OK.", ne.ConfigAssetName);
}
private void OnLoadConfigFailure(object sender, GameEventArgs e)
{
LoadConfigFailureEventArgs ne = (LoadConfigFailureEventArgs)e;
if (ne.UserData != this)
{
return;
}
Log.Error("Can not load config '{0}' from '{1}' with error message '{2}'.", ne.ConfigAssetName, ne.ConfigAssetName, ne.ErrorMessage);
}
private void OnLoadDataTableSuccess(object sender, GameEventArgs e)
{
LoadDataTableSuccessEventArgs ne = (LoadDataTableSuccessEventArgs)e;
if (ne.UserData != this)
{
return;
}
m_LoadedFlag[ne.DataTableAssetName] = true;
Log.Info("Load data table '{0}' OK.", ne.DataTableAssetName);
}
private void OnLoadDataTableFailure(object sender, GameEventArgs e)
{
LoadDataTableFailureEventArgs ne = (LoadDataTableFailureEventArgs)e;
if (ne.UserData != this)
{
return;
}
Log.Error("Can not load data table '{0}' from '{1}' with error message '{2}'.", ne.DataTableAssetName, ne.DataTableAssetName, ne.ErrorMessage);
}
private void OnInitResourcesComplete()
{
m_InitResourcesComplete = true;
Log.Info("Init resources complete.");
}
}
}
搞定。
再在编译器运行一下非编译器资源模式:
可以启动,点击开始后也能正常运行。
那下面就可以打包了:
Build And Run,选择好文件夹,打包,运行!
搞定!
同时StreamingAssets文件夹的内容会同时拷贝到打包好的工程的StreamingAssets文件夹: