前言
仔细算来,其实很久很久没有耐心来写一点自己在技术或者是生活中的一些心得了,虽然时常有些点东西的冲动,却往往正要写一些东西,每每敲完几段话,感觉很难往下继续写,
就索性delete掉,似乎总有一种江郎才尽的感觉。
好吧,今天为了彻底打破这种感觉,以后每周都定期至少写一篇博文,一方面是对学习,工作的总结,一方面也希望分享一些东西。
unity3d中ScriptableObject是一个非常有用的类,它可以帮助开发者存储一些希望保存的在运行时用到类的实例数据到文件里面。
正文
风刀的项目是一款用unity3d引擎制作的3D页游,为了达到在光影效果和效率之间的平衡,我们使用了lightmap,事实上unity3d烘培出来的效果非常好。
关于烘培部分,U3D的文档讲的很详细,这里就不多用文字解释了。直接上干货。
我们的项目抛弃了unity3d的多场景的开发方式,在程序实现上,整个游戏就只有一个场景,目的是可以对资源管理可以控制到很细的一个粒度。关卡的组织我们也自己实现了一套机制。
lightmap的动态载入主要有两方面的问题,lightmap的assetbundle和载入恢复场景的lightmap。
关于lightmap的assetbundle,最原始的思路是将所有的lightmap贴图全部bundle成一个包(这里需要记录下所有贴图的名字),然后客户端下载好lightmap包后,将所有贴图加载到内存里,然后设置到LightmapData里面去。另外一种思路是直接讲lightmapdata里面的信息通过scriptableobject序列化成asset,然后再对该asset进行bundle.鉴于第一种方法略微复杂,风刀直接使用了第二种方式。
ScriptableObject序列化的类,要求类型本身支持序列化,风刀测试了,LightMapData是不支持序列化的,不过这不会成为阻碍,我们把纹理数据从LightMapData里面取出来,序列化就OK了。因此,用于序列化lightmapdata的类
public class LightMapAsset : ScriptableObject
{
public Texture2D[] lightmapFar;
public Texture2D[] lightmapNear;
}
// 制作Asset
LightMapAsset lightmapAsset = ScriptableObject.CreateInstance<LightMapAsset>();
int Count = LightmapSettings.lightmaps.Length;
lightmapAsset.lightmapFar = new Texture2D[Count];
lightmapAsset.lightmapNear = new Texture2D[iCount];
for(int i=0; i<iCount; ++i)
{
// 这里把直接把lightmap纹理存起来
lightmapAsset.lightmapFar[i] = LightmapSettings.lightmaps[i].lightmapFar;
lightmapAsset.lightmapNear[i] = LightmapSettings.lightmaps[i].lightmapNear;
}
AssetDatabase.CreateAsset(asset, “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());
// 删除临时文件
AssetName = SceneName + “_lightmap.asset”;
AssetDatabase.DeleteAsset(“Assets/” + AssetName);
// 在游戏运行时恢复Lightmap数据就非常简单了,下面是风刀的测试代码片段
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。当LightMap数据从外部加载完成后,要恢复物件lightmapIndex和lightmapTilingOffset。当Lightmap数据还没加载完成的时候,要把lightmapIndex设置为-1(不使用LightMap),否则会出现渲染错误。