原帖地址:

http://purdyjotut.blogspot.com/2013/10/using-protobuf-in-unity3d.html

先转过来,等时间合适了,再来收拾

Using Protobuf In Unity3D

 
Recently in our projects at LoneWolfPack Interactive, we looked for faster and more efficient ways to store data. In the past we had fiddled with XML and .Net's binary formatter, but so far we weren't very satisfied.

XML is  nice. You can store practically any data, and then edit it afterwards once the file is stored. The problem with it is that the readability of the files can make it extremely bloated with certain data sets.. You could go from small manageable files to massive uncontrollable files depending on the amount of data you need to store. The speed of serialization and deserialization in the XMLSerializer is also fairly slow, so you can get small chugs here and there when you need to use it.

Binary formatter was the temporary solution. It stored small files and was relatively quick. There are a few problems with the binary formatter though, such as the lack of flexibility. If you save data using the binary formatter and then change the saved class, you come across issues and you have to scrap the data that was there. The other problem is that binary formatter isn't supported on iOS. Binary formatter requires the ability to JIT compile (Just In Time), where Apple requires AOT compilation (Ahead Of Time).

Looking for another solution I hit the net, and came across protobuf-net which was developed by Marc Gravell of Stack Overflow. If you are a geek and like stats you can look up the performance info here. Basically protobuf solved all of the issues that we were having with binary formatter. It was even faster than binary formatter, it was flexible, it was supported on iOS and we could change around our classes all we wanted and it wouldn't matter, things would still load.

 

There is a little bit of set up with protobuf though, and for that we are going to use Visual Studio. The only reason I am using Visual Studio here instead of MonoDevelop is because VS handles a few things automatically for us and the whole process is quicker. Plus I like Visual Studio, and I miss using it. If you don't have Visual Studio, you can grab it here. In this tutorial I am using VS 2010, but everything should be similar if you grab VS express 2012 .

The first thing you want to do is download protobuf from Marc's Google Code site. As of the date of this post, the version is r668, but unless Marc goes crazy and restructures everything, future versions should work with this tutorial as well.

Once the zip file containing protobuf is downloaded, you are going to want to grab the protobuf-net.dll file located under Full/unity/. and put it somewhere where you will remember so that you can reference it in future projects.

 
 

Once we have the dll stored on our computer in an accessible place, let's open Visual Studio, and create a brand new Class Library project. This project is used for creating a dll, and this will allow us to access the classes and structures we want to save within our Unity project. So since we are saving game data, let's call this project GameSaveData.

Since we are using this in Unity, we want to set a few things up beforehand. The first thing we want to do is make sure that the project is compiled under .Net Framework 2. In order to do this, in the menu go to Project -> GameSaveData properties, and set the target framework to 2.0. You will get a warning saying that Visual Studio needs to reopen to make the changes. Hit yes and you can continue on.

Next we want to reference protobuf in our project. In order to do this, right click on your references folder in the Solution Explorer and hit Add Reference. From here you can browse to where you stored the protobuf and select it.

Now that our project is set up, here is a sample version of what your GameSaveData might look like. This sample uses a basic item class, a few enums and a character customization class:

using System;

//we need to use specific attributes so that ProtoBuf knows what references what, so we add a using statement for the ProtoBuf namespace.
using ProtoBuf;

namespace GameSaveData
{
//when creating a class use the ProtoContract attribute. While not completely necessary if you look through the documentation you can see that these attributes can control what goes on when things are serialized and deserialized
    [ProtoContract]
    public class Item
    {
//Protobuf also needs to associate data with keys so that it can identify data on serialization and deserialization. We do this with the ProtoMember attribute, and in that attribute be assign an integer value
        [ProtoMember(1)]
        public string name;
        [ProtoMember(2)]
        public int price;
        [ProtoMember(3)]
        public EntityType applicableEntities;
        [ProtoMember(4)]
        public ItemType itemType;

//then we can just make the class as normal
        public Item()
        {
            this.name = "Item";
            this.price = 10;
            this.applicableEntities = EntityType.ALL;
            this.itemType = ItemType.Hat;
        }

public Item(string name, int price,EntityType applicableEntities, ItemType currentItemType)
        {
            this.name = name;
            this.price = price;
            this.applicableEntities = applicableEntities;
            this.itemType = currentItemType;
        }
    }

[ProtoContract]
    public enum EntityType
    {
        NONE = 0,
        ROCK = 1,
        PAPER = 1 << 1,
        SCISSORS = 1 << 2,
        NEUTRAL = 1 << 3,
        ALL = ROCK | PAPER | SCISSORS | NEUTRAL
    }

[ProtoContract]
    public enum ItemType
    {
        Hat = 0,
        Cape = 1,
        Weapon = 2,
        Shoe = 3,
        Arm_Armour = 4,
        Leg_Armour = 5,
        Shirt = 6,
    }

[ProtoContract]
    public class CharacterCustomizationData
    {
        [ProtoMember(1)]
        public Item hat;
        [ProtoMember(2)]
        public Item cape;
        [ProtoMember(3)]
        public Item shoes;
        [ProtoMember(4)]
        public Item weapon;
        [ProtoMember(5)]
        public Item armArmour;
        [ProtoMember(6)]
        public Item legArmour;
        [ProtoMember(7)]
        public Item shirt;

public CharacterCustomizationData()
        {
            this.hat = this.cape = this.shoes = this.weapon = this.armArmour = this.legArmour = this.shirt = null;
        }
    }

//just a very basic trophy implementation for those who would put trophies in their game and need a way to store the data
    [ProtoContract]
    public class Trophy
    {
        [ProtoMember(1)]
        public string trophyName;
        [ProtoMember(2)]
        public string trophyDescription;

public Trophy()
        {
            trophyName = "Trophy1";
            trophyDescription = "Description";
        }

public Trophy(string trophyName, string trophyDescription)
        {
            this.trophyName = trophyName;
            this.trophyDescription = trophyDescription;
        }
    }
}

 

From here, just build the project and you are done with GameSaveData.

Now, if you are deploying on a platform that handles JIT compilation, this is all you need. From here you can go to the bin folder in your GameSaveData project and drag the GameSaveData.dll and Protobut-net.dll into your Unity Project under the plugins folder.

From here, using Protobuf in your project is really simple. If you are trying to read data from a while:

using (FileStream f = new FileStream("testpath", FileMode.OpenOrCreate))
{
     mySerializedObject = ProtoBuf.Serializer.Deserialize<ObjectType>(f);
}

and if you are saving to a file:

using (FileStream f = new FileStream("testpath", FileMode.OpenOrCreate))
{
  ProtoBuf.Serializer.Serialize<ObjectType>(f, mySerializedObject);
}

Now, if you are deploying to a platform that does not support JIT, you have a little bit more work to do. In order for you do use protobuf in an AOT environment, you need to pre-compile the serializer that will be handling all of your data. Thankfully this is a very simple process, and takes about the same time to complete as our first Visual Studio project.

So what we want to do is create another new Visual Studio project, and this time it is going to be a console application. We will want to call this SaveDataSerializer, since that is what we are serializing.

We want to set the project up very similarly to our first one, so once again we are going to change the target framework to .Net 2.0 in  Project -> SaveDataSerializer properties.

We also want to add references to the project again, but this time, we want to add references to the GameSaveData.dll and protobuf-net.dll that were compiled in our last project. They should be in the bin/Debug or bin/Release folder, depending on the settings that you had the project in.

Now that our project is set up, we can create our program that will compile our serializer for us. Using the sample, our serializer code would look something like this:

using System;

//we need to reference the content of our old project so that we can add the data to the serializer
using GameSaveData;
//we need to reference the ProtoBuf.Meta library so that we can create a TypeModel that will tell protobuf exactly how the data is serialized
using ProtoBuf.Meta;

namespace GameDataSerializer
{
    class Program
    {
        static void Main(string[] args)
        {
            var model = TypeModel.Create();

model.Add(typeof(EntityType), true);
            model.Add(typeof(ItemType), true);
            model.Add(typeof(Item), true);
            model.Add(typeof(CharacterCustomizationData), true);
            model.Add(typeof(Trophy), true);

model.AllowParseableTypes = true;
            model.AutoAddMissingTypes = true;

model.Compile("GameDataSerializer", "GameDataSerializer.dll");
        }
    }
}

From here, you can use Protobuf in any of your projects, whether it is iOS, Android, PC, whichever. If you need to modify your classes, just open up your Visual Studio projects, make the modifications and go through the tutorial again. That is the nice thing about Protobuf, is that it is so easy to manipulate and change, and all of your data is safe.

Now normally I would say that I am done, but there is one more thing I want to show you, and that is a wrapper class that I created for protobuf. This class allows you to use protobuf anywhere in your project, is much more manageable, and much more convenient to use. So what I want you to do is create a new c# script in your Unity project, and call it ProtoLoader.

using UnityEngine;
using System.IO;

//this is a static class. We create this because we dont need an instance of it to make anything work, everything works independently.
public static class ProtoLoader
{
//We need to make a reference to the GameDataSerializer that wil be handling all of the data
  private static GameDataSerializer m_serializer = new GameDataSerializer();

//our first function will load an object from resources. This is useful for if you had an editor script that created data that would be stored for later usage in the game, and you had saved it with the .bytes extension
  public static T loadObjectFromResources<T>(string resourcePath)
  {
     TextAsset objectAsset = Resources.Load(resourcePath, typeof(TextAsset)) as TextAsset;
     if(objectAsset == null)
     {
        return default(T);
      }

T deserializedObject = default(T);

//basically we just load the bytes of the text asset into a memory stream, and the deserialize it from there
     using(MemoryStream m = new MemoryStream(objectAsset.bytes))
     {
        deserializedObject = (T)m_serializer.Deserialize(m, null, typeof(T));
     }

return deserializedObject;
  }

//our next function is used for loading an object from a path. Basically for when you are loading progress and other sorts of stuffs
  public static T loadObjectFromPath<T>(string path)
  {
//if the file doesn't exist we just return null. Since we are using a templated function the best we can do is default(T)
     if(!File.Exists(path))
     {
        return default(T);
     }

T deserializedObject = default(T);

using(FileStream f = new FileStream(path, FileMode.Open))
     {
        deserializedObject = (T)m_serializer.Deserialize(f, null, typeof(T));
     }

return deserializedObject;
  }

//Our third function will save the object to a file path. This is basically for saving game options, progress, whatever you can think of that you would need saved
  public static void saveObjectToPath<T>(string objPath, string fileName, T serializedObject)
  {
     if(!Directory.Exists(objPath))
     {
        Directory.CreateDirectory(objPath);
     }

//after checking if the directory is there we serialize the object into a filestream to save it.

using(FileStream f = new FileStream(objPath + fileName, FileMode.OpenOrCreate))
     {
        m_serializer.Serialize(f, serializedObject);
     }
  }

//This function is actually useful for sending objects over an RPC function. Unity allows you to send a byte[]  through an RPC even though its not documented, so here you can serialize object data, send it over the network and then deserialize the object on the other end.
  public static byte[] serializeProtoObject<T>(T obj)
  {
     using(MemoryStream m = new MemoryStream())
     {
        m_serializer.Serialize(m, obj);
        return m.ToArray();
     }
  }

//this is the opposite of the previous function. It allows you to send in a byte[] and return the object that it originally was. Very useful for networking
  public static T deserializeProtoObject<T>(byte[] bytes)
  {
     using(MemoryStream m = new MemoryStream(bytes))
     {
        return (T)m_serializer.Deserialize(m, null, typeof(T));
     }
  }
}

And that is it! You now have fast, efficient and easy to use serialization for your project!

Overall we are really happy with how well protobuf works, and it is definitely something for you to look into for data serialization. Now for those of you who may have had an issue here or there, or just want the projects, I have included alink here to a zip file that has both of the visual studio projects, and a unity package that has all of the classes and dlls created in this project, at your convenience, because I am super nice and awesome like that. LINK

Remember to share if you liked it, whether its on Twitter, Facebook, Google+ or to your technically savvy grandma!

转一篇关于如何在Unity里使用Protobuf的更多相关文章

  1. 从零开始搭建Java开发环境第二篇:如何在windows10里安装MySQL

    1 下载安装包 1.1 压缩包 https://dev.mysql.com/downloads/mysql/ [外链图片转存失败(img-oesO8K09-1566652568838)(data:im ...

  2. 如何在Unity中分别实现Flat Shading(平面着色)、Gouraud Shading(高洛德着色)、Phong Shading(冯氏着色)

    写在前面: 先说一下为什么决定写这篇文章,我也是这两年开始学习3D物体的光照还有着色方式的,对这个特别感兴趣,在Wiki还有NVIDIA官网看了相关资料后,基本掌握了渲染物体时的渲染管道(The re ...

  3. 【Unity Shaders】Unity里的雾效模拟

    写在前面 熟悉Unity的都知道,Unity可以进行基本的雾效模拟.所谓雾效,就是在远离我们视角的方向上,物体看起来像被蒙上了某种颜色(通常是灰色).这种技术的实现实际上非常简单,就是根据物体距离摄像 ...

  4. [转]如何在 Git 里撤销(几乎)任何操作

    任何版本控制系统的一个最有的用特性就是“撤销 (undo)”你的错误操作的能力.在 Git 里,“撤销” 蕴含了不少略有差别的功能. 当你进行一次新的提交的时候,Git 会保存你代码库在那个特定时间点 ...

  5. Maven:如何在eclipse里新建一个Maven的java项目和web项目

    如何在eclipse里新建一个Maven的java项目和web项目: 一:java项目 New-->Other-->Maven 右击项目-->properties,修改以下文件: ① ...

  6. 解剖SQLSERVER 第六篇 对OrcaMDF的系统测试里避免regressions(译)

    解剖SQLSERVER 第六篇  对OrcaMDF的系统测试里避免regressions (译) http://improve.dk/avoiding-regressions-in-orcamdf-b ...

  7. 走向DBA[MSSQL篇] 积跬步行千里

    原文:走向DBA[MSSQL篇] 积跬步行千里 不知道大家对SQL系列的感不感兴趣 先在这里探个路 本文针对的读者为SQL菜鸟 欢迎大牛驳论或者补充 既然是探路篇 就先说下数据过滤中的偏门匹配 希望能 ...

  8. 如何在BIOS里设置定时关机?

    如何在BIOS里设置定时关机? 通过CMOS设置实现定时开机的设置过程如下: 首先进入"CMOS SETUP"程序(大多数主板是在计算机启动时按DEL键进入): 然后将光条移到&q ...

  9. 如何在IDEA里给大数据项目导入该项目的相关源码(博主推荐)(类似eclipse里同一个workspace下单个子项目存在)(图文详解)

    不多说,直接上干货! 如果在一个界面里,可以是单个项目 注意:本文是以gradle项目的方式来做的! 如何在IDEA里正确导入从Github上下载的Gradle项目(含相关源码)(博主推荐)(图文详解 ...

随机推荐

  1. getaddrinfo

    gethostbyname和gethostbyaddr这两个函数仅仅支持IPv4,getaddrinfo函数能够处理名字到地址以及服务到端口这两 种转换,返回的是一个sockaddr结构的链表而不是一 ...

  2. 003.同时Ping多个IP(select实现IO复用,信号计时),ping程序升级版

    写这个的目的主要是为了以后的方便: 1.信号计时函数的使用 2.ip头的构建和icmp头的构建 3.selec函数t的用法 代码实现: /src/ping.h /* * ping.h * * Crea ...

  3. zookeeper 相关学习资料

    zookeeper的配置:http://www.cnblogs.com/yuyijq/p/3438829.html zookeeper运维:http://blog.csdn.net/hengyunab ...

  4. 使用Bootstrap v3.3.4注意细节box-sizing

    一.bootstrap样式 在Bootstrap v3.3.4中有下面一条重置样式: * { -webkit-box-sizing: border-box; -moz-box-sizing: bord ...

  5. Javascript笔记--Objects

     Javascript的简单数据类型包括: 数字,字符串,true/false,null 和undefined. 其他所有值都是对象. 数组是对象,方法也是对象.属性值是除开undefined值以外的 ...

  6. Java对象序列化文件追加对象的问题,以及Java的读取多个对象的问题解决方法。

    这几天做一个小的聊天项目用到对象序列化的知识,发现对象序列化不能像普通文件一样直接追加对象.每次写入对象都会被覆盖.弄了2个多小时终于解决了.Java默认的对象序列化是每次写入对象都会写入一点头ace ...

  7. POJ 1696 Space Ant 【极角排序】

    题意:平面上有n个点,一只蚂蚁从最左下角的点出发,只能往逆时针方向走,走过的路线不能交叉,问最多能经过多少个点. 思路:每次都尽量往最外边走,每选取一个点后对剩余的点进行极角排序.(n个点必定能走完, ...

  8. POJ 1066 Treasure Hunt【线段相交】

    思路:枚举四边墙的门的中点,与终点连成一条线段,判断与其相交的线段的个数.最小的加一即为答案. 我是傻逼,一个数组越界调了两个小时. #include<stdio.h> #include& ...

  9. 在JAVA中ArrayList如何保证线程安全

    [b]保证线程安全的三种方法:[/b]不要跨线程访问共享变量使共享变量是final类型的将共享变量的操作加上同步一开始就将类设计成线程安全的, 比在后期重新修复它,更容易.编写多线程程序, 首先保证它 ...

  10. javascript中使用循环链表实现约瑟夫环问题

    1.问题 传说在公元1 世纪的犹太战争中,犹太历史学家弗拉维奥·约瑟夫斯和他的40 个同胞被罗马士兵包围.犹太士兵决定宁可自杀也不做俘虏,于是商量出了一个自杀方案.他们围成一个圈,从一个人开始,数到第 ...