二、Mono中的重要內容
1、延遲函數
(1)延遲函數定義
延遲執行的函數,可以設定要延遲執行的函數和具體延遲的時間
(2)延遲函數的使用
#region 1、延遲函數//函數:Invoke(函數名/字符串,延遲時間/秒)//注意點//1.1 延遲函數第一個參數傳入的是函數名字符串//1.2 延遲函數沒有辦法傳入參數 #endregion#region 2、延遲重復執行函數//函數:InvokeRepeating(函數名,第一次執行的延遲時間,之后每次執行的間隔時間)#endregion#region 3、取消延遲函數//3.1 取消該腳本上的所有延遲函數//CancelInvoke()//3.2 指定函數名取消//CancelInvoke(函數名)#endregion#region 4、判斷是否有延遲函數//IsInvoking()#endregion
(3)延遲函數受對象失活銷毀影響
腳本依附對象失活 延遲函數可以繼續執行
腳本依附對象銷毀或腳本刪除 延遲對象無法繼續執行
2、協同程序
(1)Unity是否支持多線程
Unity中支持多線程,不過新開的線程無法訪問Unity場景中對象的內容
Unity中的多線程,即使沒有點擊運行,它也會一直執行,所以要記得關閉
多線程一般用于一些復雜計算,將輸出的結果存在公共變量中,供Unity主程序使用
(2)協同程序
(a)定義
協同程序是假的多線程,不是多線程
(b)作用
將代碼分時分步執行,不卡主線程
可以設置什么時候執行哪個步驟,多久時間執行下一步
(c)主要使用場景
異步加載文件
異步下載文件
場景異步加載
批量創建時防止卡頓
(3)協程的使用
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Lesson9 : MonoBehaviour
{// Start is called before the first frame updatevoid Start(){//第二步 開啟協程函數Coroutine c1=StartCoroutine(MyFunc(1,"123"));Coroutine c2= StartCoroutine(MyFunc(1, "123"));//第三步 關閉協程//關閉所有StopAllCoroutines();//關閉指定協程StopCoroutine(c1);}IEnumerator MyFunc(int i,string str){//第一步 聲明協程函數//1.1 返回值為IEnumerator類型及其子類//1.2 函數通過yield return 返回值 進行返回//執行第一部分print(i);yield return new WaitForSeconds(5f);//執行第二部分print(str);yield return new WaitForSeconds(5f);//執行第三部分print("123456");//協程內允許寫死循環while (true){print("死循環,等3秒打印");yield return new WaitForSeconds(3f);}}
}
(4)yield return 不同內容的含義
(5)協程受對象和組件失活銷毀的影響
協程開啟后
組件和物體銷毀 協程不執行
物體失活協程不執行,組件(腳本)失活協程執行
3、協同程序原理
(1)協程的本質
協程的本質是一個C#的迭代器方法,主要分為 協程函數本體 和 協程調度器 兩個部分
(a)協程函數本體
協程函數本體是一個能夠中間暫停返回的函數
(b)協程調度器
協程調度器是Unity內部實現的,會在對應的時機幫助我們繼續執行協程函數
(2)總結
協程的本質,就是利用C#迭代器函數分布執行的特點,加上協程調度邏輯實現的一套分時執行函數的規則
三、Resources資源動態加載
1、Resources同步加載
(1)Resources資源動態加載的作用
通過代碼動態加載Resources文件夾下指定路徑資源
避免繁瑣的拖曳操作
一個工程中可以有多個Resources文件夾,通過API加載時,會自動去尋找同名文件
(2)常用資源類型
預設體對象——GameObject 注意:預設體對象加載需要實例化,其他資源可以直接用
音效文件——AudioClip
文本文件——TextAsset 注意:支持的文本資源格式 txt xml bytes json html csv
圖片文件——Texture
其他類型——需要什么類型用什么類型
(3)資源同步加載 普通方法
Object obj= Resources.Load("資源所在Resources文件夾下的路徑");//加載指定類型的資源
Resources.Load("資源名",typeof(資源類型));
//加載指定名字的所有資源
Object[] objs= Resources.LoadAll("資源名");
(4)資源同步加載 泛型方法
Resources.Load<資源類型>("資源名");
2、Resources異步加載
(1)異步加載介紹
當加載過大的資源會造成程序卡頓,Resources異步加載就是內部新開一個線程進行資源加載,不會造成主線程卡頓
(2)異步加載方法
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Lesson11 : MonoBehaviour
{// Start is called before the first frame updatevoid Start(){print("開始加載,當前幀為"+Time.frameCount);//異步加載不能馬上得到加載的資源 至少需要等一幀//1.通過異步加載中的完成時間監聽 使用加載的資源//這句代碼表示 Unity開了一個線程進行資源加載ResourceRequest rq = Resources.LoadAsync<GameObject>("Cube");//為 資源下載結束時 添加的一個監聽事件rq.completed += LoadOver;//2.通過協程使用加載的資源StartCoroutine(Load());}private void LoadOver(AsyncOperation rq){print("結束加載,當前幀為" + Time.frameCount);//加載結束 才能對資源進行使用Instantiate((rq as ResourceRequest).asset);}IEnumerator Load(){ResourceRequest rq = Resources.LoadAsync<GameObject>("Sphere");//打印加載進度while (!rq.isDone){print("當前加載進度:" + rq.progress);yield return null;}//第一部分yield return rq;//Unity會自己判斷 資源是否加載完畢 加載完畢后才會繼續執行后面的代碼//實例化預制體Instantiate(rq.asset);//yield return null;第二部分//yield return new WaitForSeconds(3);第三部分}
}
(3)總結
1.完成事件監聽異步加載 ”線性加載“
優點:寫法簡單
缺點:只能在資源加載結束后進行處理
2.協程異步加載 ”并行加載“
好處:可以在協程中處理復雜邏輯,比如同時加載多個資源,加載進度條更新等
壞處:寫法較為復雜
3、Resources卸載資源
(1)Resources重復加載資源會浪費內存嗎
Resources加載一次資源后,該資源一直放在內存中作為緩存,二次加載時發現內存中存在該資源就會直接取出使用
所有多次加載資源不會浪費內存,但是會浪費性能
(2)如何手動釋放掉緩存中的資源
//卸載資源//1.卸載指定資源Resources.UnloadAsset(資源名稱);//注意:該方法 不能釋放 GameObject對象 因為它會用于實例化對象//它只能用于一些 不需要實例化的內容 比如 圖片 音效 文本等//一般情況很少單獨使用//2.卸載未使用的資源Resources.UnloadUnusedAssets();//該方法一般在過場景和GC一起使用
四、場景異步切換
1、場景同步切換
SceneManager.LoadScene(“場景名稱”);
缺點:
使用同步切換回刪除當前場景上所有對象,并去加載下一個場景的相關信息。如果當前場景或者下一個場景內容過多,就會導致同步切換過程耗時很久
2、場景異步切換
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;public class Lesson12 : MonoBehaviour
{// Start is called before the first frame updatevoid Start(){//同步場景切換SceneManager.LoadScene("Lesson12Test");//異步場景切換//1.通過事件回調函數 異步加載//AsyncOperation ao= SceneManager.LoadSceneAsync("Lesson12Test");//ao.completed += LoadSceneOver;//2.通過協程異步加載//注意:加載場景會把當前場景上沒有特別處理的對象都刪除了,所以協程中的部分邏輯可能是執行不了的//解決思路:讓處理場景加載的腳本依附的對象,過場景時不被移除DontDestroyOnLoad(this.gameObject);StartCoroutine(LoadScene("Lesson12Test")); }private void LoadSceneOver(AsyncOperation ao){print("場景加載結束");}IEnumerator LoadScene(string name){//異步加載AsyncOperation ao = SceneManager.LoadSceneAsync(name);//等待場景加載完成//可以在加載過程中,做別的事情print("異步加載過程中,打印的信息");yield return ao;//場景加載完 再執行下面的邏輯print("異步加載結束后,打印的信息");}
}
五、輔助功能Linerender
1、Linerender介紹
Linerender是Unity提供的一個用于畫線的組件
用途:
繪制攻擊范圍
武器紅外線
輔助功能
其他畫線功能
2、Linerender參數相關
3、Linerender代碼相關
//動態添加一個線段GameObject line = new GameObject();line.name = "Line";LineRenderer lineRenderer= line.AddComponent<LineRenderer>();//首尾相連lineRenderer.loop = true;//開始結束寬lineRenderer.startWidth = 0.02f;lineRenderer.endWidth = 1f;//開始結束顏色lineRenderer.startColor = Color.white;lineRenderer.endColor = Color.black;//設置材質lineRenderer.material = material;//設置點//要先設置點的個數lineRenderer.positionCount = 4;//設置對應每個點的位置lineRenderer.SetPositions(new Vector3[] {new Vector3(0,0,0),new Vector3(0,0,5),new Vector3(5,0,5),new Vector3(0,5,5)});//指定設置某個點lineRenderer.SetPosition(3, new Vector3(1, 5, 6));//設置是否使用世界坐標系//決定了 是否隨對象移動而移動 false——會跟隨對象移動,true——不會跟隨對象移動lineRenderer.useWorldSpace = false;//讓線段受光影響 會接受光數據 進行著色器計算lineRenderer.generateLightingData = true;
六、核心系統
1、物理系統之范圍檢測
(1)物理系統之碰撞檢測
碰撞產生必要條件:至少一個物體有剛體,兩個物體都必須有碰撞器
碰撞和觸發:碰撞會產生實際的物理效果,觸發看起來不產生碰撞但是可以通過函數監聽觸發
碰撞檢測主要用于實體物體之間產生物理效果時使用
(2)范圍檢測介紹
在指定位置 進行 范圍判斷 我們可以得到處于指定范圍內的 對象
目標是對 范圍內的對象進行處理 比如,受傷,掉血等
例如植物大戰僵尸的食人花
(3)如何進行范圍檢測
//范圍檢測//必備條件:想要被范圍檢測到的對象,必須具備碰撞器//注意點://1.范圍檢測相關API 只有當執行該句代碼時 進行一次范圍檢測 它是瞬時的//2.范圍檢測相關API 并不會真正產生一個碰撞器 只是碰撞判斷計算而已//1.盒裝范圍檢測//返回在該范圍內的觸發器 = Physics.OverlapBox(立方體中心點,立方體三邊大小,立方體角度,// 檢測指定層級(不填檢測所有層),是否忽略觸發器(UseGlobal-使用全局設置,可以在Project Setting-PyPhysics中進行設置,// Collide-檢測觸發器 Ignore-忽略觸發器 不填使用UseGlobal))Collider[] colliders =Physics.OverlapBox(Vector3.zero, Vector3.one, Quaternion.AngleAxis(45, Vector3.up), 1 << LayerMask.NameToLayer("UI"));//重點知識//關于層級//通過名字得到層級編號 LayerMask.NameToLayer//我們需要通過編號左移構建二進制數//這樣每一個編號的層級 都是 對應位為1的2進制數//我們通過位運算可以選擇想要的檢測層級//好處 一個int 就可以表示所有想要檢測的層級信息//另一個API//返回值:碰撞到的碰撞器數量//參數:傳入一個數組進行存儲Physics.OverlapBoxNonAlloc(Vector3.zero, Vector3.one, colliders);//2.球形范圍檢測// Physics.OverlapSphere(中心點,球形半徑,層級)Collider[] colliders1=Physics.OverlapSphere(Vector3.zero, 5f, 1 << LayerMask.NameToLayer("UI"));//另一個APIPhysics.OverlapSphereNonAlloc(Vector3.zero, 5f, colliders1);//3.膠囊范圍檢測//Physics.OverlapCapsule(上半圓中心點位置,下半圓中心點位置,圓的半徑,層級)Collider[] colliders2 = Physics.OverlapCapsule(Vector3.zero, Vector3.up, 1, 1 << LayerMask.NameToLayer("UI"));//另一個APIPhysics.OverlapCapsuleNonAlloc(Vector3.zero, Vector3.up, 1, colliders2);
2、物理系統之射線檢測
(1)射線檢測介紹
射線檢測可以在指定點發射一個指定方向的射線,判斷該射線與哪一些碰撞器相交,得到對應對象
(2)射線對象
//1.聲明一個射線對象//Ray(起點,方向)Ray ray = new Ray(Vector3.right, Vector3.forward);print(ray.origin);//起點print(ray.direction);//方向//2.攝像機發射出的射線//模擬鼠標點擊發出射線Ray ray1 = Camera.main.ScreenPointToRay(Input.mousePosition);
(3)碰撞器檢測函數
//碰撞檢測函數//射線檢測也是瞬時的,執行代碼時進行一次射線檢測//1.最原始的射線檢測Ray ray = new Ray(Vector3.zero, Vector3.forward);//返回一個布爾值 = Physics.Raycast(射線對象,最遠距離/米,層級,是否忽略發射器)bool isColl =Physics.Raycast(ray, 1000, 1 << LayerMask.NameToLayer("UI"));//2.獲取相交物體的信息//物體信息類 RaycastHitRaycastHit hitInfo;//Physics.Raycast(射線對象,物體信息類,最遠距離,層級)if (Physics.Raycast(ray,out hitInfo,1000, 1 << LayerMask.NameToLayer("UI"))){print(hitInfo.collider);//碰撞器信息print(hitInfo.point);//碰撞到的點print(hitInfo.normal);//法線信息print(hitInfo.transform.position);//碰到對象的位置信息print(hitInfo.distance);//碰撞對象與發射對象的距離}//3.獲取相交的多個對象RaycastHit[] raycastHits = Physics.RaycastAll(ray, 1000, 1 << LayerMask.NameToLayer("UI"));int count = Physics.RaycastNonAlloc(ray, raycastHits, 1000, 1 << LayerMask.NameToLayer("UI"));
(4)使用時注意的問題
距離和層級 都是int類型
當我們傳入參數時,一定要明確傳入的參數代表的是距離還是層數