天天看點

自己用的unity學習筆記(二)——unity進階協同程式(協同或者協程)自動尋路遊戲開發的MVC架構檔案操作Unity jsonUnity網絡AssetBundle學習

協同程式(協同或者協程)

線程、程序

一個應用程式就是一個程序,籠統的說就是一個.exe程式

一個程序中至少一個線程叫主線程。

一個或多個線程組合成了程序。

協程

在unity中為了在主線程中模拟多線程而出現的輔助工具。

協程不是多線程,還是單線程,是在作業系統中的空間中模拟多線程。

啟動協程:

StartCoroutine(傳回IEnumerator類型傳回值的方法名);//開啟協程,

注意:傳回IEnumerator類型傳回值的方法名可以有參數,但是不能帶有ref和out形式的參數

例如:

StartCoroutine(“Fuck”);

IEnumerator Fuck(){

yield return new WaitForSeconds(3); //每三秒執行一次本行之後的代碼

Debug.log(“1231233”);

}

IEnumerator是一個疊代器類型的對象,疊代器是用來疊代周遊用的,例如foreach。

一次又一次的循環将其他元素取出來付給同一個元素就叫疊代。

yield return 是方法傳回疊代器的方式,等同普通方法的return

開啟一個協程(遇到yield return之前),從上到下執行,和普通方法一樣

遇到yield return,程式挂起,傳回協程開啟的地方繼續執行主線程,從此跟主線程沒有任何關系。

yield return之後的代碼屬于協程,進行主線程的同時,疊代器會反複檢視(每幀)yield return後面的條件是否滿足,滿足後會執行協程中yield return後的代碼,此時代碼跟主線程無關(例如協程内死循環,跟主線程無關,但協程也不能死循環,因為死循環會導緻協程無法跳出,過多無法跳出會卡死電腦)。

協程如果需要死循環,可以在死循環中通過yield return null(等待一幀)的方式來避免卡死,這種方式相當于一幀隻執行一次,例如update,對電腦負荷小,不會卡死。

例如:

IEnumerator Fuck(){

yield return new WaitForSeconds(3); //每三秒執行一次本行之後的代碼

while(true){

Debug.log(“1231233”);

yield return null;

}

}

上述情況不會卡死是因為yield return null是等待一幀的意思,此時并不是跳出了死循環,而是每執行一次循環體,就等待一幀時間,這樣會一直每幀輸出一次,但不會卡死,不寫的話,相當于每幀執行無限次,是以才會卡死。

yield return 協程的方式來開協程,等待的是協程全部執行完畢後才傳回。

例如 yield return StartCoroutine(“You”)等待新協程you執行完畢後再繼續。

本質上來說,死循環的協程就是一個單獨的update

Yield return 可以等待多種:

yield return 時間/新協程/www(是unity的一個類)/秒

yield break;跳出協程,不能用return。還可以通過代碼Stop Coroutine(“You”)停止協程,但停止協程會在協程這一幀執行完才會停止。yield break;馬上跳出

停止協程

協程是和gameobject挂鈎的(與協程所在的腳本無關),但可以通過腳本來開啟協程,而不能通過遊戲物體來開啟協程(擷取腳本元件,通過.的方式開啟協程)

如果遊戲物體失活或者銷毀,協程都會終止,再激活也不會執行。

如果不能保證本遊戲對象不會被銷毀,那麼這個協程要開啟在一個不會被銷毀的物體身上,而代碼寫在自己的邏輯中,使用方式就是上面所說的腳本元件.開啟協程。

StopCoroutine()停止協程,如果協程使用字元串方式開啟的,能用字元串方式停止,但如果用方法名加()的方式開啟的,用字元串停不住,用方法名加()也停不住,隻能用StopAll Coroutine ()的方式停止,但StopAllCoroutine ()隻會停止目前類對象下開啟的協程,不能停止其他物體的協程。

想要停止其他物體的協程,必須使用腳本. StopAllCoroutine ().

誰開啟的協程,誰來停止,其他對象停止不了。

延遲函數

Invoke(方法名字元串,float 秒) 延遲多少秒執行函數。

CancelInvoke(參數方法名,可不寫);取消延遲函數。

不寫參數,取消本腳本内所有延遲函數,加參數,取消指定參數

延遲方法無論遊戲物體和腳本失活都會執行,除非關閉或銷毀,否則不能停下來。

1、 可以用其他的對象開啟延遲函數,但要保證誰開啟的延遲函數,對應的函數體就必須在對應遊戲物體身上。

2、 無論誰開啟的,隻要函數在該物體的内部,該物體就可以停止延遲函數。

Invoke隻能開啟一次,多次開啟使用invokeRepeating

invokeRepeating(方法名,第一次延遲秒數,第一次之後每隔多少秒執行一次);

invokeRepeating取消方式和invoke一樣

Invoke是一個委托,協程是一個疊代,unity中不可以用線程,因為多個線程幀同步會出問題。(網絡遊戲中接包可能需要使用)非要用就得做幀同步,把線程的間隔時間和主線程的幀進行同步(通過time.deltatime)。

自動尋路

NavMesh

Agent Radius : 邊緣的半徑,不是尋路區域的。是尋路區域到邊緣之間的非尋路區域的半徑。

Agent height: 烘焙人物的高度。決定可到路徑的高度

Max slope: 烘焙坡度

Step height:烘焙高度,由人物高度決定

Generated off mesh links:跳躍相關的内容,設定跳躍的距離。

烘焙完成後,還需要在對應角色身上加上尋路元件,navMeshAgent。

障礙必須是靜态的才能影響路徑,

空物體添加nav mesh obstacle元件也可以稱為障礙物,該元件類似碰撞體

不同的路徑可以添加Areas子產品,分别選中對應路徑進行烘焙。建立區域後,在Object中指定路徑選中烘焙區域。然後在navMeshAgent元件中再指定玩家的路線。

Off mesh link 跳躍障礙物的元件

插件屬性

代碼擷取該元件需要引用unityEngine.AI.

MeshAgent.SetDestination(目标點);

遊戲開發的MVC架構

Model 資料層  view 顯示層  controller 控制層

核心思想:将資料層和顯示層分開,做到高内聚低耦合。

玩家在顯示層進行輸入,顯示層會将輸入的内容通過控制層傳遞給資料層

檔案操作

Directory:對檔案夾的操作

File :對檔案的操作

FileStream :以位元組流對檔案操作

計算機最小機關是位,8位一位元組,但位太小,位元組是檔案的基本處理機關。

具體操作參考C#面向對象中檔案操作類。

Unity中不再使用C#的方法,unity自己提供了一個檔案類。

TextAsset類

建立pulibc TextAsset textasset;可用正常方式加載

textasset.text;讀取所有文本

Asset的路徑:Application.dataPath+@”/(必須加斜杠)路徑(必須加字尾名)”

Unity json

Unity自帶jsonUility類。

Json是一種輕量級的資料互動格式,易于機器解析和生成。

Json的格式:<名稱/值>

名稱要使用字元串的格式,值可以是多種類型。名稱對之間用逗号來分割。

{“id”:1,“name”:”ccc”}

類和類内的屬性字段如果不是public的,就無法序列化成json.

Sring json = JsonUtility.ToJson(對象);

對象 = JsonUtility.FromJson<類名>(json);//json名和轉化的類的字段名稱必須相同。

Unity的Json大括号之間不能加逗号{“id”:1,“name”:”ccc”},{“id”:1,“name”:”ccc”}

配合檔案讀取函數使用:

str = File.ReadAllText(Application.dataPath+@”/(必須加斜杠)路徑(必須加字尾名)”

);傳回字元串,讀取所有檔案内容為str

string[] strs = File.ReadAllLines(Application.dataPath+@”/(必須加斜杠)路徑(必須加字尾名)”

);傳回字元串數組,每一行為數組的一個元素,讀取檔案内容的每一行為string數組

Unity網絡

網絡模型五大結構:應用層(應用程式),傳輸層(主要用來實作端到端的通訊,在傳輸層定義了不同服務品質的協定(TCP傳輸控制協定,高品質傳輸,會檢查有沒有收到,沒收到會再發,例如HTTP請求。UDP資料報協定,不檢查有沒有收到,直接傳輸,例如直播))。

網絡層:用來決定網絡消息在發送過程中走哪條線。

資料鍊路層:确定實體位址,源位址等問題,短途傳輸,例如區域網路

實體層:光纖等實體媒介

協定:不同語言的網絡傳輸為了便于通訊,共同遵守的規則

IP,port(IP和端口):網絡發送的目标。

在綁定端口号時,端口号分為幾種:

1、 公認端口,0-1023,常用服務綁定端口,通常這些端口已經明确表明了一些服務協定。例如80http通訊協定,自己用時,不要用1023之前的。

2、 注冊端口:從1024到49151.建議大家在寫自己程式時使用此類

3、 動态或私有端口:49152到65535,理論上,不為服務配置設定此類端口

65535是計算機所能開啟的最大線程數量,但實際的最大限制于計算機性能。

Socket連結

每一個tcp/udp都有對應的ip位址,每一組ip位址和端口統稱為一個Socket(套接字),通常也叫做一個連接配接服務。一個Socket套接字能确定連接配接雙方的ip,端口。

通常把一個socket定義成一個點到點的通訊,socket在用戶端和服務端連接配接上之後,就可以發送多條消息,不會自動停止,在沒有消息發送時,也會連接配接,但此時會發送鍊路消息包,俗稱心跳包,否則伺服器會自動結束socket。是以行業中通常将socket當做長連接配接。

TCP連結:

TCP建立連結需要三次握手:

1、 第一次,源主機給伺服器發送一個同步标志位(SYN),同時會發送一個ISN作為初始序号(ISN是随時間變化的随機值)

2、 第二次握手,目标伺服器傳回一個确認資料段,會将SYN加1,并且ISN也加1,f傳回給源主機。

3、 源主機再發送一個資料段,同樣帶有遞增的發送序号和确定序号。

三次握手之後,才開始發送真正需要發送的内容(僅一次内容,想要再次發送必須要重新三次握手)。

發送完内容後,伺服器傳回資訊,資訊傳回後會自動斷掉連結。

是以httprequest通過TCP協定的連結又稱為短連接配接。

http是被動傳輸。隻有有用戶端發送,才能回。

http超文本傳輸協定

網際網路應用最廣泛的網絡傳輸協定。用戶端和服務端請求應答的标準(TCP)。一般來說用在webrequest,通過webhtml或者網絡爬蟲和web伺服器進行通訊。

http是被動傳輸。隻有有用戶端發送,才能回。

Unity使用(www類)

WebRequest是一個抽象類,使用時WebRequest.Create(url);就會傳回一個Webrequest的對象。

WebResponse response = request.GetResponse();

Stream stream = response.GetResponseStream();

StreamReader sr = new StreamReader(stream,system.text.Encoding.UTF-8);讀流的方式讀取網絡傳回的流

String str = Sr.ReadToEnd();//讀流讀到結束

IEnumerator start(){//将start改成一個範圍值為疊代的方法,unity内置機制會開啟一個協程。之是以要用協程,是因為内容可能沒有加載好,直接通路會報錯。

WWW www = new WWW(url);

Yield return www;

tu = www.texture;

}

Socket使用(用戶端連結)

Using system.Net.Sockets

Using system.Net

Using system.Threading;多線程的命名空間

Socket ss;

Bytes[] buffer = new byte[1024*1024];

Void start(){

//連接配接伺服器

Ss = new Socket(AddressFamily.Internetwork,SocketType.Stream,ProtocolType.Tcp);通過socket的構造函數設定socket類型,參數:協定族,傳輸方式,連結方式

IPAddress ip = IPAddtrss.Parse(“127.0.0.1”);//連結本機,如果是伺服器,就使用伺服器主機ip

ss.Connect(ip,12345);//ip和端口,此處端口必須是伺服器監聽的端口

Thread recive = new Thread(Recives);開啟一個線程去接收伺服器傳回的資訊,參數是入口函數名稱,該函數在下面自己定義

recive.Start();//開啟線程

}

Void Recives(){

線程運作完,會直接關閉,為了保持長連接配接,不能讓線程結束,是以要使用死循環

While(true){

If(ss==null){//如果連結斷了,就跳出死循環,結束線程

Break;

}

Int bufcount = ss.Receive(buffer);//該接收方法是堵塞性方法,接收不到會一直卡在這裡,buffer是接收傳回資訊的位元組流數組,方法傳回位元組數組的長度。

  String message = Encoding.UTF8.GetString(buffer,0,bufcount);//讀取位元組數組,注意編碼一定要和服務端傳回的一樣。

}

}

C#服務端

Using内容相同

using System.Net;

using System.Net.Sockets;

using System.Threading;

using System.Text;

public class fuwuqi : MonoBehaviour {

    Socket servers;//建立socket連結

    string ip = "192.168.2.2";

    int port = 5555;

void Start () {

        servers = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);//同用戶端設定

        IPEndPoint ends = new IPEndPoint(IPAddress.Parse(ip),port);//IPAddress.Parse(ip)将ip轉化成IP格式。通過ip端口組合成一個IPEndPoint結構

        servers.Bind(ends);//綁定ip位址和端口,用來接收對應端口的消息

        servers.Listen(10);//最多監聽10個連結

        Thread acceptThread = new Thread(Accepts);//開啟一個線程接收用戶端資訊

        acceptThread.Start();

}

    void Accepts() {

        Debug.Log("連接配接成功,等待用戶端!");

        while (true) {

            Socket client = servers.Accept();//監聽其他源主機的請求,如果有資料發送過來,就會從堵塞形态放開,傳回值就是連接配接進來的用戶端的socket

            if (client!=null) {

                Debug.Log("有一個用戶端進入連接配接!");

            }

            string message = "你好啊!";

            byte[] buffer = Encoding.UTF8.GetBytes(message);

            client.Send(buffer);//向用戶端發送資料

        }

    }

}

完整的網絡連結

AssetBundle學習

AssetBundles打包

1、設定打包内容

每個資源的右下角都有AssetBundle的打包選項,預設none不打包,否則就是打包的包名稱。

此處填寫包名稱(支援路徑)和包的字尾名(字尾名随意)。填寫路徑時,會自動在檔案夾下建立對應的路徑檔案夾。

2、依賴打包

在打包過程中,為了節省空間,減少包的大小,需要将多個物體共同使用的資源單獨來打包,然後讓對應物體都使用這一個包的資源,例如材質和貼圖。

具體操作方式:(以材質貼圖為例,其實不需要人為操作什麼,unity會自己處理)

1、将材質貼圖單獨打包。

2、其他物體打包時,unity會自動尋找該物體所用到的材質貼圖,如果發現材質貼圖已經被打包了,就會自動依賴貼圖材質的包。

3、打包代碼

[MenuItem("Assets/Bulid AssetBundles")]

将該靜态函數添加到Assets目錄下,作為可以直接執行的功能,但前提必須為靜态方法,并且該代碼必須在Editor檔案夾(編輯器擴充檔案夾)中。

 static void BulidAllAsset() {//定義靜态函數,必須是靜态的,否則不能在菜單欄中顯示

        string dir ="abs";

        if (!Directory.Exists(dir)) {//判斷是否存在這個路徑,不存在就建立一個,使用該函數必須using system.IO;

注意:路徑名不能和AssetBundle的打包名稱相同

            Directory.CreateDirectory(dir);

        }

//BuildPipeline類下的BuildAssetBundles(路徑,打包的參數,打包的目标平台);

        BuildPipeline.BuildAssetBundles(dir, BuildAssetBundleOptions.None,BuildTarget.StandaloneWindows64);

  }

4、BuildAssetBundleOptions參數

BuildAssetBundleOptions.None:使用LZMA算法壓縮,壓縮的包更小,但是加載時間更長。使用之前需要整體解壓。一旦被解壓,這個包會使用LZ4重新壓縮。使用資源的時候不需要整體解壓。在下載下傳的時候可以使用LZMA算法,一旦它被下載下傳了之後,它會使用LZ4算法儲存到本地上。( 相當于隻有第一次加載會比較慢,随後就會變成lz4壓縮。)

BuildAssetBundleOptions.UncompressedAssetBundle:不壓縮,包大,加載快

BuildAssetBundleOptions.ChunkBasedCompression:使用LZ4壓縮,壓縮率沒有LZMA高,但是我們可以加載指定資源而不用解壓全部。

注意使用LZ4壓縮,可以獲得可以跟不壓縮想媲美的加載速度,而且比不壓縮檔案要小。

AssetBundles讀取

1、本地讀取

AssetBundle all = AssetBundle.LoadFromFile("ABS/assetbundles.ab");//從檔案中同步加載資源,傳回值類型AssetBundle

 //方法1:

Object[] obj = all.LoadAllAssets();//加載包中所有的資源,傳回obj格式的

for (int i = 0;i < obj.Length;i++) {

       Debug.Log(obj[i].name);

}

//方法2:

GameObject g = all.LoadAsset<GameObject>("[email protected]");//擷取指定資源,填寫名稱

Instantiate<GameObject>(g);

//從記憶體中加載

AssetBundle all = AssetBundle.LoadFromMemory(File.ReadAllBytes("ABS/assetbundles.ab"));//從記憶體中同步加載資源包,參數必須為位元組流

all.LoadAsset<GameObject>("[email protected]");

//從記憶體中異步加載

IEnumerator Start () {

        AssetBundleCreateRequest all = AssetBundle.LoadFromMemoryAsync(File.ReadAllBytes("ABS/assetbundles.ab"));//從記憶體中異步加載資源包,參數必須為位元組流

        yield return all;

        //使用

        all.assetBundle.LoadAsset("ccc");

 }

說明:

如果存在依賴關系,必須加載所依賴的包(即使原本就有也不行,必須加載依賴包),加載的順序沒有影響,隻要在使用之前加載,加載出來即可,不需要做其他操作,系統會自動尋找依賴資源。

2、伺服器讀取方式

1、www加載

  IEnumerator Start () {

        //使用www加載資源,該方法已被棄用,不推薦使用。

        while (Caching.ready != true) {//因為www把内容下載下傳到緩存中,是以使用www之前需要先判斷一下緩存是否準備好

            yield return null;

        }

        WWW all = WWW.LoadFromCacheOrDownload(@"file://D:\unity\AssetBundle\abs\assetbundles.ab", 1);//路徑說明,www的路徑最好是網絡路徑,本地路徑必須加上file:// 或者 file:///,1是版本号

        if (!string.IsNullOrEmpty(all.error)) {//www如果出錯是不會報錯的,是以需要人為的進行錯誤判斷

            Debug.Log(all.error);

            yield break;

        }

        yield return all;

        //使用

        GameObject g = all.assetBundle.LoadAsset<GameObject>("[email protected]");

        Instantiate<GameObject>(g);

}

2、通過UnityWebRequest方式加載對應的資源

  IEnumerator Start() {

        //使用unityRequest方法加載遠端資源

        string uri = @"file://D:\unity\AssetBundle\abs\assetbundles.ab";

        UnityWebRequest request = UnityWebRequest.GetAssetBundle(uri);//建立連結對象

        yield return request.Send();//開始下載下傳,并等待下載下傳完成request.Send()傳回AsyncOperation類型,和正常的異步加載一樣。

        AssetBundle all = DownloadHandlerAssetBundle.GetContent(request);//從request中擷取對應assetbundle資源

        //使用

        GameObject g = all.LoadAsset<GameObject>("[email protected]");

        Instantiate<GameObject>(g);

}

通過Manifest檔案加載assetbundle檔案的依賴包

系統在打包assetbundle時會在對應目錄下生成一個和目錄同名的包,該包沒有對應資源,但是它的manifest檔案會記錄檔案夾下所有包的名稱和每個包的依賴檔案,是以可以通過該檔案擷取依賴包,也可以通過每個包的manifest檔案來擷取。

AssetBundle assetBundle = AssetBundle.LoadFromFile(manifestFilePath);

AssetBundleManifest manifest =

assetBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");

string[] dependencies = manifest.GetAllDependencies("assetBundle"); //Pass the name of the bundle you want the dependencies for.

foreach(string dependency in dependencies)

{

    AssetBundle.LoadFromFile(Path.Combine(assetBundlePath, dependency));

}

AssetBundle解除安裝

解除安裝有兩個方面

1,減少記憶體使用

2,有可能導緻丢失

是以什麼時候去解除安裝資源

AssetBundle.Unload(true)解除安裝所有資源,即使有資源被使用着

1,在關切切換、場景切換

2,資源沒被用的時候

AssetBundle.Unload(false)解除安裝所有沒用被使用的資源

個别資源怎麼解除安裝:

1,通過 Resources.UnloadUnusedAssets.

2,場景切換的時候

繼續閱讀