【unity】將場景導出XML或JSON或二進制而且解析還原場景(補充)

補充html

最近在作客戶端與服務器的交互,使用JSON 和XML會感受數據量太大,影響效率。最後使用二進制的方式來完成。以下圖所示,使用二進制能夠把空間節省到803K ,是否是很不錯呢? 下面咱們開始學習如何製做吧。git

 

wKioL1RIrPrwcWOYAADCN5RQ0_o116.jpg

導出場景時增長導出二進制文件選項,代碼以下。數組

C#服務器

1編輯器

2ide

3oop

4學習

5ui

6spa

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

[MenuItem ("GameObject/BINARY")]

static void XMLJSONTOBinary ()

{

string filepath = Application.dataPath + @"/StreamingAssets/binary.txt";

if(File.Exists (filepath))

{

File.Delete(filepath);

}

FileStream  fs = new FileStream(filepath, FileMode.Create);

BinaryWriter bw = new BinaryWriter(fs);

foreach (UnityEditor.EditorBuildSettingsScene S in UnityEditor.EditorBuildSettings.scenes)

        {

            if (S.enabled)

            {

string name = S.path;

EditorApplication.OpenScene(name);

 

foreach (GameObject obj in Object.FindObjectsOfType(typeof(GameObject)))

{

     if (obj.transform.parent == null)

     {

//註解 直接寫入字符串

bw.Write(name);

bw.Write(obj.name);

 

short posx = (short)(obj.transform.position.x * 100);

C#

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

bw.Write(posx);

bw.Write((short)(obj.transform.position.y * 100.0f));

bw.Write((short)(obj.transform.position.z * 100.0f));

bw.Write((short)(obj.transform.rotation.eulerAngles.x * 100.0f));

bw.Write((short)(obj.transform.rotation.eulerAngles.y * 100.0f));

bw.Write((short)(obj.transform.rotation.eulerAngles.z * 100.0f));

bw.Write((short)(obj.transform.localScale.x * 100.0f));

bw.Write((short)(obj.transform.localScale.y * 100.0f));

bw.Write((short)(obj.transform.localScale.z * 100.0f));

 

}

}

 

}

}

 

bw.Flush();

bw.Close();

fs.Close();

}

註解

在寫入二進制數據時用到的核心類就是BinaryWriter ,Binary是二進制的意思 ,可見操做二進制寫入就用BinaryWriter了。 經常使用的數據類型會分配固定的字節數量,假設BinaryWriter 寫入一個short 那麼就佔2字節,寫一個 int 就佔4字節,若是是數組的話須要數組類型字節長度在乘以數組長度。

byte:一個字節(8位)
short:兩個字節(16位)
int:四個字節(32位)(一個字長)
long:八個字節(64位)
float:四個字節(32位)
double:八個字節(64位)

而後在說說string,字符串它並非標準的數據類型,它是一個對象 object 那麼它的字節長度就是可變的。開始我也在string 上糾結了一小會兒。還有BinaryWriter 在寫入string 的時候會現將字符串的長度以byte的形式儲存,而後在儲存字符串的字節長度。那麼在解析字符串的時候須要先解析字符串長度,而後在根據長度取得後面對應長度的字節數組,再把這個字節數組轉換成string就行啦。還有,上面我用的是short x 100 其實上爲了節省長度, 由於short是2字節,float是4字節。我在解析的時候用short 在除以100 就能夠 換算成float拉。

而後咱們在看看解析的代碼,寫入的時候咱們用的是BinaryWriter 那麼讀取的時候應該是 BinaryReader。

Binary.cs

C#

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

using UnityEngine;

using System.Collections;

using System.IO;

using System.Text;

using System;

public class Binary : MonoBehaviour

{

 

void Start ()

{

string filepath = Application.dataPath + @"/StreamingAssets/binary.txt";

 

if(File.Exists (filepath))

{

FileStream fs = new FileStream (filepath,FileMode.Open);

BinaryReader br = new BinaryReader(fs);

 

int index = 0;

    //將二進制字節流所有讀取在這個byte數組當中

    //ReadBytes傳遞的參數是一個長度,也就是流的長度

byte[] tempall = br.ReadBytes((int)fs.Length);

 

//開始解析這個字節數組

while(true)

{

//當超過流長度,跳出循環

if(index >= tempall.Length)

{

break;

}

 

//獲得第一個byte 也就是獲得字符串的長度

int scenelength = tempall[index];

byte []sceneName = new byte [scenelength];

index += 1;

//根據長度拷貝出對應長度的字節數組

System.Array.Copy(tempall,index,sceneName,0,sceneName.Length);

//而後把字節數組對應轉換成字符串

string sname = System.Text.Encoding.Default.GetString(sceneName);

 

    //這裏和上面原理同樣就不贅述

int objectLength = tempall[index + sceneName.Length];

byte []objectName = new byte [objectLength];

 

index += sceneName.Length + 1;

System.Array.Copy(tempall,index,objectName,0,objectName.Length);

string oname = System.Text.Encoding.Default.GetString(objectName);

 

//下面就是拿short 每個short的長度是2字節。

 

     index += objectName.Length;

byte[] posx = new byte[2];

System.Array.Copy(tempall,index,posx,0,posx.Length);

//取得對應的數值 而後 除以100 就是float拉。

float x = System.BitConverter.ToInt16(posx,0) /100.0f;

 

    //下面都差很少

index += posx.Length;

byte[] posy = new byte[2];

System.Array.Copy(tempall,index,posy,0,posy.Length);

float y = System.BitConverter.ToInt16(posy,0) /100.0f;

 

index += posy.Length;

byte[] posz = new byte[2];

System.Array.Copy(tempall,index,posz,0,posz.Length);

float z = System.BitConverter.ToInt16(posz,0) /100.0f;

 

index += posz.Length;

byte[] rotx = new byte[2];

System.Array.Copy(tempall,index,rotx,0,rotx.Length);

float rx = System.BitConverter.ToInt16(rotx,0) /100.0f;

 

index += rotx.Length;

byte[] roty = new byte[2];

System.Array.Copy(tempall,index,roty,0,roty.Length);

float ry = System.BitConverter.ToInt16(roty,0) /100.0f;

 

index += roty.Length;

byte[] rotz = new byte[2];

System.Array.Copy(tempall,index,rotz,0,rotz.Length);

float rz = System.BitConverter.ToInt16(rotz,0) /100.0f;

 

index += rotz.Length;

byte[] scax = new byte[2];

System.Array.Copy(tempall,index,scax,0,scax.Length);

float sx = System.BitConverter.ToInt16(scax,0) /100.0f;

 

index += scax.Length;

byte[] scay = new byte[2];

System.Array.Copy(tempall,index,scay,0,scay.Length);

float sy = System.BitConverter.ToInt16(scay,0) /100.0f;

 

index += scay.Length;

byte[] scaz = new byte[2];

System.Array.Copy(tempall,index,scaz,0,scaz.Length);

float sz = System.BitConverter.ToInt16(scaz,0) /100.0f;

 

index+=scaz.Length;

 

if(sname.Equals("Assets/StarTrooper.unity"))

{

//最後在這裏把場景生成出來

string asset = "Prefab/" + oname;

Vector3 pos = new Vector3 (x,y,z);

Vector3 rot = new Vector3(rx,ry,rz);

Vector3 sca = new Vector3(sx,sy,sz);

GameObject ob = (GameObject)Instantiate(Resources.Load(asset),pos,Quaternion.Euler(rot));

ob.transform.localScale = sca;

}

 

}

}

}

 

// Update is called once per frame

void Update ()

{

 

}

}

運行一下,場景依然生成的很是完美,在處理二進制解析的時候須要特別注意的就是字節對齊,由於你的全部數據其實就是一個byte[]字節數組,須要有理有序的把字節數組拆分,而後在轉換成對應的數據,因此必定要對齊否則確定會出錯的。

 

wKioL1RIrQHwGETxAAJ12WmYfAg285.jpg

最後把代碼放出來,晚安 Good Ngith 哇咔咔。

下載地址 :http://vdisk.weibo.com/s/la_QE 

留言中恰好有人討論到這塊。另外還有一種方式也能夠實現動態增長創建場景,使用.unity 來實現場景的加載,我以爲這種方式可能會更好一些。我在網上已經發現有人寫了,那就轉載過來吧。

原文地址:http://blog.csdn.net/cony100/article/details/8842919

在Unity3d中,場景(scene)多半經過在build settings中點擊add current或者把場景拖進面板實現,假如不這麼作,你的場景便不會被加載,哪怕你制定了絕對路徑。

就是說,一個遊戲裏要加載多少場景多半都是固定的。

這樣的方法會有不少不便,不容易動態加載場景。因此咱們今天要說的,是一種動態加載場景的方法。

首先,你須要一個編輯器文件,放在editor文件夾下。注意,這個文件不能夠繼承自monobehaviour

C#

1

2

3

4

5

6

7

public class BuildSceneEditor{  

    [@MenuItem("build/BuildWebplayerStreamed")]  

    static void Build(){  

        string[] levels = new string[]{"Assets/Level1.unity","Assets/Level2.unity"};  

        BuildPipeline.BuildStreamedSceneAssetBundle(levels,"streamed.unity3d",BuildTarget.WebPlayer);  

    }  

}

這樣,在你的unity編輯器上出現了一個按鈕,你執行這個按鈕,則會在你的Assets同級目錄下出現你build好的streamed.unity3d文件,你把這個文件放在服務器上,下面一步就是下載這個文件並build了。

C#

1

2

3

WWW download = WWW.LoadFromCacheOrDownload("http://xxx/streamed.unity3d",0);  

yield return download;  

Application.LoadLevel("Level1");

你們注意到了嗎。下載好之後就能夠直接loadlevel了,不須要手動進行add current的操做了。

這裏還有一篇聖典翻譯的文章 http://game.ceeger.com/Script/BuildPipeline/BuildPipeline.BuildStreamedSceneAssetBundle.html

     最後我在補充一下使用.unity3d確實方便不少,由於它不只會把場景打包進去,而且還會把場景中對應的資源文件打包進去。舉個例子,你將美工作好的模型文件放在Project視圖中,而後在將模型放在Hierarchy視圖中的 100,100,100座標點中,最後把該場景打包成.unity3d文件。此時你在新建一個工程只需下載剛剛打包的場景文件,他會自動把模型放在 100,100,100座標點中。

      這說明場景文件,包含了該場景中所用到的全部模型,而且還包含了模型資源與Hierarchy視圖的關係。它會帶來一個弊端,好比你有N個場景,每一個場景中都有相同的模型文件,這樣每一個場景都須要重複下載這些相同的模型文件,因此我以爲最好仍是使用assetbundle來對同類的資源文件進行分包處理。

相關文章
相關標籤/搜索