天天看點

在Unity3D的網絡遊戲中實作資源動态加載

用unity3d制作基于web的網絡遊戲,不可避免的會用到一個技術-資源動态加載。比如想加載一個大場景的資源,不應該在遊戲的開始讓使用者長時間等待全部資源的加載完畢。應該優先加載使用者附近的場景資源,在遊戲的過程中,不影響操作的情況下,背景加載剩餘的資源,直到所有加載完畢。 

本文包含一些代碼片段講述實作這個技術的一種方法。本方法不一定是最好的,希望能抛磚引玉。代碼是c#寫的,用到了json,還有c#的事件機制。 

在講述代碼之前,先想象這樣一個網絡遊戲的開發流程。首先美工制作場景資源的3d模組化,遊戲設計人員把3d模組化導進unity3d,托托拽拽編輯場景,完成後把每個gameobject導出成xxx.unity3d格式的資源檔案(參看buildpipeline),并且把整個場景的資訊生成一個配置檔案,xml或者json格式(本文使用json)。最後還要把資源檔案和場景配置檔案上傳到伺服器,最好使用cms管理。用戶端運作遊戲時,先讀取伺服器的場景配置檔案,再根據玩家的位置從伺服器下載下傳相應的資源檔案并加載,然後開始遊戲,注意這裡并不是下載下傳所有的場景資源。在遊戲的過程中,背景繼續加載資源直到所有加載完畢。 

一個簡單的場景配置檔案的例子: 

mydemosence.txt 

json代碼 

"assetlist" : [{ 

"name" : "chair 1", 

"source" : "prefabs/chair001.unity3d", 

"position" : [2,0,-5], 

"rotation" : [0.0,60.0,0.0] 

}, 

"name" : "chair 2", 

"position" : [1,0,-5], 

"rotation" : [0.0,0.0,0.0] 

"name" : "vanity", 

"source" : "prefabs/vanity001.unity3d", 

"position" : [0,0,-4], 

"name" : "writing table", 

"source" : "prefabs/writingtable001.unity3d", 

"position" : [0,0,-7], 

"rotation" : [0.0,0.0,0.0], 

"name" : "lamp", 

"source" : "prefabs/lamp001.unity3d", 

"position" : [-0.5,0.7,-7], 

}] 

assetlist:場景中資源的清單,每一個資源都對應一個unity3d的gameobject 

name:gameobject的名字,一個場景中不應該重名 

source:資源的實體路徑及檔案名 

position:gameobject的坐标 

rotation:gameobject的旋轉角度 

你會注意到writing table裡面包含了lamp,這兩個對象是父子的關系。配置檔案應該是由程式生成的,手工也可以修改。另外在遊戲上線後,用戶端接收到的配置檔案應該是加密并壓縮過的。 

主程式: 

c#代碼 

。。。 

public class mainmonobehavior : monobehaviour { 

public delegate void maineventhandler(gameobject dispatcher); 

public event maineventhandler startevent; 

public event maineventhandler updateevent; 

public void start() { 

resourcemanager.getinstance().loadsence("scenes/mydemosence.txt"); 

if(startevent != null){ 

startevent(this.gameobject); 

public void update() { 

if (updateevent != null) { 

updateevent(this.gameobject); 

這裡面用到了c#的事件機制,大家可以看看我以前翻譯過的國外一個牛人的文章。c# 事件和unity3d 

在start方法裡調用resourcemanager,先加載配置檔案。每一次調用update方法,mainmonobehavior會把update事件分發給resourcemanager,因為resourcemanager注冊了mainmonobehavior的update事件。 

resourcemanager.cs 

private mainmonobehavior mainmonobehavior; 

private string mresourcepath; 

private scene mscene; 

private asset msceneasset; 

private resourcemanager() { 

mainmonobehavior = gameobject.find("main camera").getcomponent<mainmonobehavior>(); 

mresourcepath = pathutil.getresourcepath(); 

public void loadsence(string filename) { 

msceneasset = new asset(); 

msceneasset.type = asset.type_json; 

msceneasset.source = filename; 

mainmonobehavior.updateevent += onupdate; 

在loadsence方法裡先建立一個asset的對象,這個對象是對應于配置檔案的,設定type是json,source是傳進來的“scenes/mydemosence.txt”。然後注冊mainmonobehavior的update事件。 

public void onupdate(gameobject dispatcher) { 

if (msceneasset != null) { 

loadasset(msceneasset); 

if (!msceneasset.isloadfinished) { 

return; 

//clear mscene and msceneasset for next loadsence call 

mscene = null; 

msceneasset = null; 

mainmonobehavior.updateevent -= onupdate; 

onupdate方法裡調用loadasset加載配置檔案對象及所有資源對象。每一幀都要判斷是否加載結束,如果結束清空mscene和msceneasset對象為下一次加載做準備,并且取消update事件的注冊。 

最核心的loadasset方法: 

private asset loadasset(asset asset) { 

string fullfilename = mresourcepath + "/" + asset.source; 

//if www resource is new, set into www cache 

if (!wwwcachemap.containskey(fullfilename)) { 

if (asset.www == null) { 

asset.www = new www(fullfilename); 

return null; 

if (!asset.www.isdone) { 

wwwcachemap.add(fullfilename, asset.www); 

繼續loadasset方法: 

if (asset.type == asset.type_json) { //json 

if (mscene == null) { 

string jsontxt = msceneasset.www.text; 

mscene = jsonmapper.toobject<scene>(jsontxt); 

//load scene 

foreach (asset sceneasset in mscene.assetlist) { 

if (sceneasset.isloadfinished) { 

continue; 

} else { 

loadasset(sceneasset); 

if (!sceneasset.isloadfinished) { 

代碼能夠運作到這裡,說明資源都已經下載下傳完畢了。現在開始加載處理資源了。第一次肯定是先加載配置檔案,因為是json格式,用jsonmapper類把它轉換成c#對象,我用的是litjson開源類庫。然後循環遞歸處理場景中的每一個資源。如果沒有完成,傳回null,等待下一幀處理。 

else if (asset.type == asset.type_gameobject) { //gameobject 

if (asset.gameobject == null) { 

wwwcachemap[fullfilename].assetbundle.loadall(); 

gameobject go = (gameobject)gameobject.instantiate(wwwcachemap[fullfilename].assetbundle.mainasset); 

updategameobject(go, asset); 

asset.gameobject = go; 

if (asset.assetlist != null) { 

foreach (asset assetchild in asset.assetlist) { 

if (assetchild.isloadfinished) { 

asset assetret = loadasset(assetchild); 

if (assetret != null) { 

assetret.gameobject.transform.parent = asset.gameobject.transform; 

asset.isloadfinished = true; 

return asset; 

終于開始處理真正的資源了,從緩存中找到www對象,調用instantiate方法執行個體化成unity3d的gameobject。updategameobject方法設定gameobject各個屬性,如位置和旋轉角度。然後又是一個循環遞歸為了加載子對象,處理gameobject的父子關系。注意如果loadasset傳回null,說明www沒有下載下傳完畢,等到下一幀處理。最後設定加載完成标志傳回asset對象。 

updategameobject方法: 

private void updategameobject(gameobject go, asset asset) { 

//name 

go.name = asset.name; 

//position 

vector3 vector3 = new vector3((float)asset.position[0], (float)asset.position[1], (float)asset.position[2]); 

go.transform.position = vector3; 

//rotation 

vector3 = new vector3((float)asset.rotation[0], (float)asset.rotation[1], (float)asset.rotation[2]); 

go.transform.eulerangles = vector3; 

這裡隻設定了gameobject的3個屬性,眼力好的同學一定會發現這些對象都是“死的”,因為少了腳本屬性,它們不會和玩家互動。設定腳本屬性要複雜的多,編譯好的腳本随着主程式下載下傳到本地,它們也應該通過配置檔案加載,再通過c#的反射建立腳本對象,賦給相應的gameobject。 

最後是scene和asset代碼: 

public class scene { 

public list<asset> assetlist { 

get; 

set; 

public class asset { 

public const byte type_json = 1; 

public const byte type_gameobject = 2; 

public asset() { 

//default type is gameobject for json load 

type = type_gameobject; 

public byte type { 

public string name { 

public string source { 

public double[] bounds { 

public double[] position { 

public double[] rotation { 

public bool isloadfinished { 

public www www { 

public gameobject gameobject { 

代碼就講完了,在我實際測試中,會看到gameobject一個個加載并顯示在螢幕中,并不會影響到遊戲操作。代碼還需要進一步完善适合更多的資源類型,如動畫資源,文本,字型,圖檔和聲音資源。 

動态加載資源除了網絡遊戲必需,對于大公司的遊戲開發也是必須的。它可以讓遊戲策劃(負責場景設計),美工和程式3個角色獨立出來,極大提高開發效率。試想如果策劃改變了什麼npc的位置,美工改變了某個動畫,或者改變了某個程式,大家都要重新倒入一遍資源是多麼低效和麻煩的一件事。

<a href="http://www.web3d.com.cn/new/teach/unity3d/2012/3/27/37137809.html">http://www.web3d.com.cn/new/teach/unity3d/2012/3/27/37137809.html</a>

簡介:09年入行,喜歡遊戲和程式設計,對3d遊戲和引擎尤其感興趣。 

版權聲明:本文版權歸作者和部落格園共有,歡迎轉載。轉載必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。

轉載:http://www.cnblogs.com/geniusalex/archive/2013/02/22/2922975.html