最新文章
Cocos2d-x游戏开发实例详解7:对象释放时机
03-25 13:59
Cocos2d-x游戏开发实例详解6:自动释放池
03-25 13:55
Cocos2d-x游戏开发实例详解5:神奇的自动释放
03-25 13:49
Cocos2d-x游戏开发实例详解4:游戏主循环
03-25 13:44
Cocos2d-x游戏开发实例详解3:无限滚动地图
03-25 13:37
Cocos2d-x游戏开发实例详解2:开始菜单续
03-25 13:32
unity3d LightmapSettings.lightmaps
在 Unity3D 中,ScriptableObject 是一个非常实用的类,它可以帮助开发者将运行时用到的类的实例数据存储到文件中。
项目背景
风刀的项目是一款使用 Unity3D 引擎制作的 3D 页游。为了在光影效果和效率之间取得平衡,项目采用了光照贴图(Lightmap)技术,且 Unity3D 烘焙出来的光影效果十分出色。关于烘焙部分,Unity3D 的官方文档已有详细说明,本文不再赘述,直接进入核心内容。
项目摒弃了 Unity3D 的多场景开发方式,在程序实现上,整个游戏仅使用一个场景,这样做的目的是能够对资源管理进行更精细的控制。同时,项目也自行实现了一套关卡组织机制。光照贴图的动态载入主要涉及两个问题:光照贴图的 AssetBundle 处理和场景光照贴图的载入恢复。
光照贴图的 AssetBundle 处理思路
原始思路
将所有光照贴图打包成一个 AssetBundle(需记录所有贴图的名称),客户端下载该包后,将所有贴图加载到内存中,再设置到 LightmapData 里。
采用的思路
鉴于第一种方法较为复杂,风刀项目直接采用了第二种方式:将 LightmapData 里的信息通过 ScriptableObject 序列化为 Asset,然后对该 Asset 进行打包。
由于要求 ScriptableObject 序列化的类本身支持序列化,经测试,LightMapData 不支持序列化。不过,我们可以将纹理数据从 LightMapData 中取出进行序列化。以下是用于序列化 lightmapdata 的类:
public class LightMapAsset : ScriptableObject
{
public Texture2D[] lightmapFar;
public Texture2D[] lightmapNear;
}
制作 Asset 的代码实现
// 创建 LightMapAsset 实例
LightMapAsset lightmapAsset = ScriptableObject.CreateInstance<LightMapAsset>();
int count = LightmapSettings.lightmaps.Length;
lightmapAsset.lightmapFar = new Texture2D[count];
lightmapAsset.lightmapNear = new Texture2D[count];
for (int i = 0; i < count; i++)
{
// 存储光照贴图纹理
lightmapAsset.lightmapFar[i] = LightmapSettings.lightmaps[i].lightmapFar;
lightmapAsset.lightmapNear[i] = LightmapSettings.lightmaps[i].lightmapNear;
}
打包 Asset 的代码实现
// 创建 Asset
AssetDatabase.CreateAsset(lightmapAsset, "Assets/" + AssetName);
UnityEngine.Object obj = AssetDatabase.LoadAssetAtPath("Assets/" + AssetName, typeof(LightMapAsset));
string exportTargetPath = Application.dataPath + "/" + targetPath;
if (!File.Exists(exportTargetPath))
{
Directory.CreateDirectory(exportTargetPath);
}
string lightMapBundleName = SceneName + "_lightmap.bundle";
BuildPipeline.BuildAssetBundle(obj, null, exportTargetPath + lightMapBundleName.ToLower());
// 删除临时文件
AssetDatabase.DeleteAsset("Assets/" + AssetName);
游戏运行时恢复光照贴图数据
在游戏运行时恢复光照贴图数据相对简单,以下是风刀项目的测试代码片段:
if (info.www.assetBundle.mainAsset is LightMapAsset)
{
LightMapAsset lightmapAsset = info.www.assetBundle.mainAsset as LightMapAsset;
int count = lightmapAsset.lightmapFar.Length;
LightmapData[] lightmapDatas = new LightmapData[count];
for (int i = 0; i < count; ++i)
{
LightmapData lightmap = new LightmapData();
lightmap.lightmapFar = lightmapAsset.lightmapFar[i];
lightmap.lightmapNear = lightmapAsset.lightmapNear[i];
lightmapDatas[i] = lightmap;
}
LightmapSettings.lightmaps = lightmapDatas;
}
注意事项
在记录场景物件的信息时,需要记录 GameObject 的 lightmapIndex 和 lightmapTilingOffset。当光照贴图数据从外部加载完成后,要恢复物件的 lightmapIndex 和 lightmapTilingOffset。当光照贴图数据尚未加载完成时,要将 lightmapIndex 设置为 -1(不使用光照贴图),否则会出现渲染错误。