補充html
最近在作客戶端與服務器的交互,使用JSON 和XML會感受數據量太大,影響效率。最後使用二進制的方式來完成。以下圖所示,使用二進制能夠把空間節省到803K ,是否是很不錯呢? 下面咱們開始學習如何製做吧。git
導出場景時增長導出二進制文件選項,代碼以下。數組
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字節,若是是數組的話須要數組類型字節長度在乘以數組長度。
而後在說說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[]字節數組,須要有理有序的把字節數組拆分,而後在轉換成對應的數據,因此必定要對齊否則確定會出錯的。
最後把代碼放出來,晚安 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來對同類的資源文件進行分包處理。
轉載請註明: 雨鬆MOMO
2012年12月07日
於 雨鬆MOMO程序研究院 發表