跳至主要内容

ScriptableObject 序列化

  ScriptableObject是一个类,它允许你存储大量用于共享的数据独立脚本实例,不要迷惑这个类同样可以叫做 SerializableObject,可以理解成是一个Unity串行化工具。这是一个编辑器类并且你可以在Inspector面板中编辑数据。例如: 如果你有一个存储了一百万数据的 int[],这个数组占用4MB内存,放在Prefab上,那么当你每次实例化Prefab,你都会得到这个数组的一个副本。如果你实例化10个这个 Prefab,那就会占用40MB内存。

可序列化的类型

Unity的serializes(序列化)支持所有原生的类型,也支持strings,arrays,lists还有Unity的Vector3等都支持,而且还支持自定义的类,但需要有一个串行的属性。

使用情景

预期使用情况,使用ScriptableObject是减少内存使用量,避免Values的副本。但是你也可以用它来定义可插拨的数据集。这方面的 一个例子是:想像RPG游戏中的NPC商店,你可以创建自定义ShopContens ScriptableObject,每个资产定义了一组可用于购买物品。在这样一个场景,游戏中有三个区域,每个区域都可以提供不同层级的项目。你的店铺 脚本将引用ShopContents对象,定义哪些项目可供选择。

Tips

当在检查ScriptableObject引用时,你可以在Inspector双击这个参考字段。
你可以创建一个自定义的Editor来查看不同的类似在Inspector,以帮助你管理它所表示的数据。

游戏关卡数据序列化

策划需求

一个游戏中的配置表数据,这些数据一般都是由策划在Excel等工具上配置,要运用到游戏中时,一般需要做一个转换,比如类似转换。这时可以使用 ScriptableObject,将数据预先处理成程序需要访问的数据结构,存储在ScriptableObject中,然后打包成一个外部文件,这样 在游戏运行的时候就不用做解析和组装了,这个功能对大批量的公用数据尤其有用!!

思路分析

策划在Art工程拼接好关卡信息之后,客户端根据关卡的中元素的位置,大小等信息生成出关卡,这中间有一个存储关卡信息的过程。我们的做法是把关卡 的信息存储在ScriptableObject上,然后在Editor里,把当前关卡信息存储在Product目录下做为一个文件。Client直接读取 这个MapObject文件

MapSetting.cs

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

//地图信息打包成外部文件,供Client读取
public class CMapSetting : ScriptableObject
{
    public List<string> TexturesDependencies;//依赖的贴图
    public string MapObjectPath;//MapRoot

    public List Battles;//战役信息
}

导出关卡数据代码

    public void ExportCurrentScene()
    {
        SceneName = EditorApplication.currentScene.Substring(EditorApplication.currentScene.LastIndexOf('/') + 1);
        SceneName = SceneName.Substring(0, SceneName.LastIndexOf('.'));
        string exportPath = string.Format("Scene/{0}{1}", SceneName, GameDef.ASSET_BUNDLE_FILE_EXT);

        string mapObjectPath = string.Format("Scene/{0}_MapObject{1}", SceneName, GameDef.ASSET_BUNDLE_FILE_EXT);

        CSimBattle[] battles = GameObject.FindObjectsOfType();//获取所有的Battle

        List battleList = new List(battles);
        battleList.Sort((CSimBattle x, CSimBattle y) =>
        {
            return x.transform.position.y.CompareTo(y.transform.position.y);
        });//根据Battle的Y进行排序

        CSimActor[] actors = GameObject.FindObjectsOfType();//获取所有的Actor
        List actorList = new List(actors);
        int count = 0;//测试一个Battle中多少只怪
        List LevelBattleSettings = new List();//有多少关卡

        for (int b = 0; b < battleList.Count; b++)//遍历Battle
        {
            CSimBattle simBattle = battleList[b];
            CLevelBattleSetting levelBatSetting = new CLevelBattleSetting();//创建一个新的BattleSetting实例
            levelBatSetting.MapActorSettings = new List();//Battle中的Actor设置信息
            levelBatSetting.Num = simBattle.KillNum;
            levelBatSetting.Time = simBattle.BattleTime;
            levelBatSetting.StoryOnStart = simBattle.StoryOnStart;
            levelBatSetting.StoryOnEnd = simBattle.StoryOnEnd;
            levelBatSetting.CurPosition = simBattle.transform.position;
            //CBase.Log("CSimBattle.CurPosition:{0}", levelBatSetting.CurPosition);

            float battlePosY = simBattle.transform.position.y;//Battle的Y值
            for (int a = (actorList.Count - 1); a >= 0; a--)//遍历怪
            {
                float actorPosY = actorList[a].transform.position.y;//Actor的Y值
                if (actorPosY <= battlePosY)//判断这个Actor是否在这个Battle内
                {
                    CMapActorSetting actorSetting = new CMapActorSetting();
                    CSimActor simActor = actorList[a];
                    actorSetting.NpcId = simActor.NPC编号;
                    actorSetting.NpcLevel = simActor.NPC等级;
                    actorSetting.NpcPosition = simActor.transform.position;
                    actorSetting.IsEnemy = simActor.是否敌人;

                    levelBatSetting.MapActorSettings.Add(actorSetting);//把怪添加到关卡中
                    actorList.Remove(simActor);//移除已经添加的Actor
                    count += 1;
                }
            }

            LevelBattleSettings.Add(levelBatSetting);//把battle添加进关卡地图中
            CBase.Log("Battle :{0}  有怪物 {1} 只" ,b, count);//打印一个Battle中有多少怪
            count = 0;
        }
        //CBase.Log("当前关卡共有 {1} 个Battle", LevelBattleSettings.Count);

        GameObject mapRoot = GameObject.Find("MapRoot");
        if (mapRoot != null)
        {
            List<string> textureList = new List<string>();//存放打包好图片的路径
            Renderer[] renderers = mapRoot.GetComponentsInChildren();
            foreach (Renderer child in renderers)
            {
                Texture _texture = child.sharedMaterial.mainTexture;
                string _path = AssetDatabase.GetAssetPath(_texture);
                _path = XBuildTools.GetUniquepath(_path);
                if (!textureList.Contains(_path))
                {
                    textureList.Add(_path);
                    XBuildTools.PushAssetBundle(_texture, string.Format("Textures/{0}{1}", _path, GameDef.ASSET_BUNDLE_FILE_EXT));
                    CBase.Log("Texture导出成功! =>{0}", _path);
                }
            }

            XBuildTools.PushAssetBundle(mapRoot, mapObjectPath);//打包 mapRoot
            Debug.Log("地图导出成功!" + SceneName);
            XBuildTools.PopAssetBundle();
            //XBuildTools.PopAllAssetBundle();

            CMapSetting mapSetting = ScriptableObject.CreateInstance();
            mapSetting.TexturesDependencies = textureList;
            mapSetting.MapObjectPath = mapObjectPath;
            mapSetting.Battles = LevelBattleSettings;
            XBuildTools.BuildScriptableObject(mapSetting, exportPath);//序列化 MapSetting的位置啊,路径信息。
        }
    }

打包出的关卡文件

image

文档资料

Unity Manual:http://docs.unity3d.com/Manual/class-ScriptableObject.html
Scripting API:http://docs.unity3d.com/ScriptReference/ScriptableObject.html

评论

此博客中的热门博文

[3D跑酷] AudioManager

Unity音频管理 游戏中的声音管理最常用的组件莫过于AudioSource和AudioClip,我的做法是建立是一个AudioManager类(单例类)管理各个音频,谈一下我的经验: 函数列表 Start函数:设置音频整体参数; 编辑器面板 拖拽文件赋值 AudioSource文档 逻辑实现代码 public void playSoundEffect(SoundEffects soundEffect) { AudioClip clip = null ; float pitch = 1; switch (soundEffect) { case SoundEffects.ObstacleCollisionSoundEffect: clip = obstacleCollision; break ; case SoundEffects.CoinSoundEffect: clip = coinCollection; pitch = 1.5f; break ; case SoundEffects.PowerUpSoundEffect: clip = powerUpCollection; break ; case SoundEffects.GameOverSoundEffect: clip = gameOver; break ; case SoundEffects.GUITapSoundEffect: clip = guiTap; break ; } soundEffectsAudio.pitch = pitch; //音调 so...

[3D跑酷] GUIManager UI管理

UI元素更新及界面跳转 继上篇日志《Unity开发之 GUIClickEventReceiver》,再谈一下我们如何管理游戏中的UI元素更新及界面跳转 UI绑定 图一:Inspector面板 Public GameObjectName与GameObject一一对应 UI结构及命名规范 图二:Hierarchy面板 UI父子结构及组件命名规范 UI枚举种类 图三:enum GUIState UI绑定代码 图四:public UI控件定义 与Hierarchy命名规范 UI主要方法及逻辑 图五:主要方法及逻辑 主要方法 1、隐藏Transform及子Transform #if !UNITY_3_5 private void activeRecursively(Transform obj, bool active) { foreach (Transform child in obj) { activeRecursively(child, active); } obj.gameObject.SetActive(active); } #endif private GameObject panelFromState(GUIState state) { switch (state) { case GUIState.MainMenu: return mainMenuPanel; case GUIState.InGame: return inGamePanel; case GUIState.EndGame: return endGamePanel; case GUIState.Store: return storePanel; case GUIState.Stats: return statsPanel; case GUISt...

谷歌Adsense广告代码异步加载解决谷歌联盟广告老卡的问题

最近一段时间,谷歌中国的服务器从香港撤走,导致很多朋友连谷歌也无法访问了,而像部落这样,在网站中投放了谷歌Adsense广告的,也经常会出 现网站打开,加载到谷歌广告位置时,网站就卡在那里了,估计一些网速慢的用户,可能就永远卡那里了。对于这种情况,我们可以利用异步加载的广告代码来解决 这个问题。 Google广告会因为各种原因经常打不开,如果不用异步加载的方法,会导致在您的网站中,google广告后面的代码显示被阻塞,这样的话,很影响用户体验。经常网页不能完全打开的话,必然会导致用户的在面积流失。 上图是部落的网站首页,在没有使用广告代码异步加载前,经常会出现这样的现像,首页右边的一个300X300的谷歌广告位无法正常显示了。 当然,使用谷歌Adsense广告代码异步加载,您的网站加载速度会明显提升,特别是对一些网速慢的用户来说,但有一点和我们站长相矛盾的,那就是上次谷歌承诺广告发布商的广告被有效浏览才收费,很明显,异步加载有可能会降低您站内广告的浏览量。 使用谷歌Adsense广告代码异步加载 其实使用方法很简单,我们进入自己的谷歌联盟后台,在“广告单元”中找到需要异步加载的广告位,点击“获取代码”,如下图: 我们在代码类型中选择“异步”,就能获得异步加载的广告位代码,将您之前在网站中投放的广告代码替换就行了。 之前传统的google广告异步加载代码 不知您有没有发现,之前传统的google广告异步加载代码之这次部落所说的有很大的不同了,例如部落之前同样是采用的广告代码同步加载,代码内容如下: --> 显然,以上代码会影响您的网站加载速度。而之前的异步加载代码,则是在后面加上了一段JS代码。内容就不放出了。 部落在谷歌官方找到一些异步加载说明:google广告在google服务器正常的情况下会及时的正常显示,google服务器不正常的情况下也不会影响后面页面的加载。 而自己正式体验后,才发现,谷歌联盟的广告加载速度真是超快,只是加载的内容,匹配性没之前那么好了,究其原因,部落猜想应该是在调用本地电脑数据的cookis时,就直接调用了其中的广告数据,只有这样,加载速度才会达到最快。当然,这纯粹只是个人猜想而已。