Unity筆記(七)——四元數、延遲函數、協同程序

寫在前面:

寫本系列(自用)的目的是回顧已經學過的知識、記錄新學習的知識或是記錄心得理解,方便自己以后快速復習,減少遺忘。主要是C#代碼部分。

六、四元數

歐拉角具有旋轉約定,也就是說,無論你調整角度的順序是什么,最終物體旋轉的順序都會是約定的旋轉序列。最常見用約定是Y-X-Z約定,也就是物體按Y-X-Z的方向進行旋轉。

無論你如何更改角度,系統都會重新從初始狀態按順序Y-X-Z進行調整,這和萬向鎖相似。也就是說,先轉動的軸會帶動后轉動的軸轉動,但后轉動的軸不能影響先轉動的軸。歐拉角會帶來萬向節死鎖的問題,導致在特定情況下丟失一個自由度。

此外,同樣的角度能使用不同的歐拉角角度來描述,因此,引入四元數來解決歐拉角的這兩個問題。

1、四元數

一個四元數包含一個標量和一個3D向量

[w, v],w為標量,v為3D向量,也就是[w, (x, y, z)],對于給定的任意一個四元數,表示3D空間中的一個旋轉量。

(1)四元數的公式

假設繞著n軸旋轉β度,n軸為(x,y,z),那么可以構成四元數為:

四元數Q = [cos(β/2),sin(β/2)n] = [cos(β/2),sin(β/2)x,sin(β/2)y,sin(β/2)z]

四元數Q表示繞著n軸,旋轉β度的旋轉量。

2、Unity中的四元數

可以有兩種初始化方法:

1、Quaternion q = new Quaternion(sin(β/2)x,sin(β/2)y,sin(β/2)z, cos(β/2));

2、?Quaternion q2 = Quaternion.AngleAxis(旋轉度數, 旋轉軸);

一般使用第二種初始化方法:

void Start()
{//繞著x軸轉60度Quaternion q = new Quaternion(Mathf.Sin(Mathf.Deg2Rad), 0, 0, Mathf.Cos(30*Mathf.Deg2Rad));GameObject obj = GameObject.CreatePrimitive(PrimitiveType.Cube);obj.transform.rotation = q;Quaternion q2 = Quaternion.AngleAxis(60, Vector3.right);
}

(1)四元數和歐拉角轉換

歐拉角轉為四元數使用的API是:Quaternion.Euler(),括號內傳入需要轉化的歐拉角,比如Quaternion.Euler(60, 0, 0),轉化出來的四元數是繞x軸轉60度的四元數。

四元數轉歐拉角使用的API是:.eulerAngles

void Start()
{//歐拉角轉四元數,這個就是繞著x軸轉60度Quaternion q3 = Quaternion.Euler(60, 0, 0);//四元數轉歐拉角print(q2.eulerAngles);
}

最后,需要注意的是,四元數的角度區間是-180~180,因此四元數表示的角度是唯一的。

3、四元數的常用方法

(1)單位四元數

單位四元數表示沒有旋轉量(角位移),當角度為0或者360度時,對于給定軸都會得到單位四元數。

例如[1,(0, 0, 0)]和[-1, (0, 0, 0)]都是單位四元數,表示沒有旋轉量。原因是,由四元數Q = [cos(β/2),sin(β/2)n] = [cos(β/2),sin(β/2)x,sin(β/2)y,sin(β/2)z],角度為0時,cos0 = 1, sin0均為0;或是cos180度等于-1,sin180都等于0。

在Unity中提供了表示單位四元數的API:Quaternion.identity

void Start()
{print(Quaternion.identity);testObj.rotation = Quaternion.identity;
}

單位四元數可用于初始化。初始化在0,0,0點,方向為0, 0, 0的物體:

Instantiate(testObj, Vector3.zero, Quaternion.identity);

(2)四元數插值運算

四元數中的Lerp和Slerp只有一些細微差別,由于算法不同,Slerp的效果會好一些,Lerp的效果相比Slerp更快,但當旋轉范圍較大時,效果不太好,所以建議使用Slerp進行插值運算。與之前的插值運算區別不大,只不過這里插值的是角度,同樣演示了兩種方法:

public Transform target;
public Transform A;
public Transform B;private Quaternion start;
private float time = 0;void Start()
{start = B.rotation;
}void Update()
{A.rotation = Quaternion.Slerp(A.rotation, target.rotation, Time.deltaTime);time+= Time.deltaTime;B.rotation = Quaternion.Slerp(start,target.rotation, time);
}

(3)向量指向轉四元數

向量指向轉四元數的API是:Quaternion.LookRotation(向量)

這個方法可以將傳入的面朝向量轉換為對應四元數角度信息。例如,物體A想要看向物體B,就可以將向量AB傳入。這個方法會返回轉為AB方向的四元數,這時候將物體A的角度信息改為該四元數即可實現A看向B。

public Transform LookA;
public Transform LookB;void Start()
{Quaternion q = Quaternion.LookRotation(LookB.position - LookA.position);LookA.rotation = q;
}

4、四元數計算

(1)四元數相乘

兩個四元數相乘得到一個新的四元數,代表兩個旋轉量的疊加。旋轉相對的坐標系是物體自身的坐標系。

 void Start(){Quaternion q = Quaternion.AngleAxis(20, Vector3.up);this.transform.rotation *= q;this.transform.rotation *= q;}

(2)四元數乘向量

四元數乘向量返回一個新向量,相當于將這個向量旋轉了對應四元數的旋轉量。

 void Start(){Vector3 v = Vector3.forward;print(v);//四元數×向量順序不能改變v = Quaternion.AngleAxis(45, Vector3.up) * v;print(v);}

七、延遲函數

延遲函數就是會延時執行的函數,我們可以自己設定延時要執行的函數和具體延時時間。

1、延遲函數

延遲函數使用的API是:Invoke(),里面傳入兩個參數,第一個參數是需要延遲執行的函數的名字的字符串,第二個參數是延遲幾秒鐘執行。

void Start()
{Invoke("DelayDoSomething", 5);
}private void DelayDoSomething()
{print("執行");
}

延遲函數無法調用有參數的函數,需要進行包裹。函數名必須是該腳本上聲明的函數。只能和有參函數一樣進行包裹。例如下例,只有這樣才能執行有參數的延遲函數。

void Start()
{Invoke("DelayDoSomething", 5);}private void DelayDoSomething()
{print("執行");TestFunc(2);
}private void TestFunc(int i)
{print("執行" + i);
}

2、延遲重復執行函數

延遲函數可以一直重復執行,使用的API是:InvokeRepeating(),括號內傳入三個參數,第一個參數是重復執行的函數名的字符串,第二個參數是第一次執行延遲多少秒,第三個參數是之后執行延遲多少秒。如下例函數,會在第5秒時執行一次,之后每隔1秒執行一次。

void Start()
{InvokeRepeating("DelayRe", 5, 1);
}private void DelayRe()
{print("重復執行");
}

3、取消延遲函數

(1)取消所有

使用CancelInvoke()后會取消所有延遲函數。

void Start()
{//取消所有CancelInvoke();
}

(2)指定函數名取消

也可以通過傳入函數名的字符串指定取消延遲函數。只要取消了指定延遲,不管之前函數開啟了多少次,延遲執行都會統一取消。此外,如果沒有該延遲函數也不會報錯。

void Start()
{CancelInvoke("DelayRe");
}

4、判斷是否有延遲函數

使用IsInvoking()可以檢查是否存在延遲函數,同樣的,也可以傳入參數判斷是否有指定延遲函數。

void Start()
{if(IsInvoking()){print("存在延遲函數");}if(IsInvoking("DelayDoSomething")){print("存在延遲函數DelayDoSomething");}
}

5、延遲函數受對象失活的影響

腳本掛載的對象失活或是腳本失活,延遲函數只要開啟了,就依然可以執行。腳本或者掛載的對象刪除了或是腳本銷毀了,延遲函數就會失效。

八、協同程序

1、Unity的多線程

Unity是支持多線程的,需要引用命名空間using System.Threading。創建一個新線程使用的是:Thread t = new Thread(),需要注意的是,括號內需要傳入一個委托函數,該函數會在新線程中執行。傳入函數后,t.Start()即可開啟線程。

線程與編輯器的生命周期一樣長,要記得手動關閉多線程。可以在對象銷毀時運行的生命周期函數OnDestroy()中關閉線程。線程的關閉使用t.Abort()即可。

Thread t;void Start()
{t = new Thread(Test);t.Start();
}private void Test()
{while (true){Thread.Sleep(1000);print("新開線程");}
}private void OnDestroy()
{t.Abort();t = null;
}

此外,需要注意的是,新開線程無法訪問Unity相關對象的內容,也就是無法控制場景中的對象,這些對象的邏輯只能在主線程中控制。

2、協同程序

(1)概念

協同程序簡稱協程,它是假的多線程。它將代碼分時執行,不卡主線程,也就是它把可能會讓主線程卡頓的耗時的邏輯分時分布執行。主要使用的場景有:異步加載文件,異步下載文件,場景異步加載,批量創建時防止卡頓。

(2)協程和線程的區別

新開一個線程是獨立的一個管道,和主線程并行執行;新開協程是在原線程之上開啟,進行邏輯分時分步執行。

3、協程的使用

(1)聲明

協同程序函數的返回值必須是 IEnumerator或者繼承了它的類型。協程函數當中必須使用yield return進行返回。例如:

IEnumerator MyCoroutine(int i, string str)
{print(i);yield return new WaitForSeconds(5f);print(str);
}

該協程函數中?yield return new WaitForSeconds(5f);指的是第二部分延遲5s執行。也就是說在print(i)過后會延遲5s再執行print(str)。

此外,yield return可以寫多個,可以把代碼分成幾個部分來執行。

IEnumerator MyCoroutine(int i, string str)
{print(i);yield return new WaitForSeconds(5f);print(str);yield return new WaitForSeconds(1f);print("end");
}

(2)開啟協程

協程函數不能直接調用。有兩種調用方式,第一種先創建一個?IEnumerator變量接收返回值,再將返回值傳入函數StartCoroutine(返回值)。第二種,直接使用StartCoroutine(函數名)。

void Start()
{IEnumerator ie = MyCoroutine(1, "123");StartCoroutine(ie);StartCoroutine(MyCoroutine(1, "123"));
}

(3)關閉協程

有兩種關閉協程的方式:第一種方式為關閉所有協程StopAllCoroutines(),它會關閉你開啟的所有協程。第二種方式為關閉指定協程,可以創建Coroutine c1來存儲協程返回的變量,而后使用StopCoroutine(c1);關閉指定協程。

 void Start(){Coroutine c1 = StartCoroutine(MyCoroutine(1, "123"));Coroutine c2 = StartCoroutine(MyCoroutine(1, "123"));Coroutine c3 = StartCoroutine(MyCoroutine(1, "123"));StopCoroutine(c1);StopAllCoroutines();}

4、yield return

1、下一幀執行:yield return 數字、yield return null。過了一幀后,下一次執行會在Update和LateUpdate之間執行。

2、等待指定秒后執行:yield return new WaitForSeconds(秒)。等待指定秒數后,下一次執行會在Update和LateUpdate之間執行。

3、等待下一個固定物理幀更新時執行:yield return new WaitForFixedUpdate()。等待固定物理幀后,會在FixedUpdate和碰撞檢測相關函數之后執行。

4、等待攝像機和GUI渲染完成后執行:yield return newWaitForEndOfFrame()。在LateUpdate之后的渲染相關處理完畢之后執行。

5、一些特殊類型的對象,比如異步加載相關函數返回的對象,后續介紹。一般在Update和LateUpdate之間執行

6、跳出協程 yield break;

5、協程受對象和組件失活銷毀的影響

協程開啟后,組件和物體銷毀,協程不執行;物體失活協程不執行,組件失活協程執行。

6、協同程序原理

(1)協程的本質

協程可以分成兩部分,協程函數本體和協程函數調度器。協程函數本體是一個能夠中間暫停返回的函數,協程調度器是Unity內部實現的,會在對應時機 幫助我們繼續執行協程函數。

(2)協程函數本體

創建一個迭代器IEnumerator ie來接受協程函數,利用迭代器的ie.MoveNext()方法就可以依次調度到下一個yield return為止的函數。利用ie.Current可以獲得yield return中傳入的值。如下:

public class TestClass
{public int time;public TestClass(int time){this.time = time;  }
}IEnumerator Test()
{print("第一次執行");yield return 1;print("第二次執行");yield return 2;print("第三次執行");yield return "123";print("第四次執行");yield return new TestClass(10);
}void Start()
{IEnumerator ie = Test();ie.MoveNext();print(ie.Current);ie.MoveNext();print(ie.Current);ie.MoveNext();print(ie.Current);ie.MoveNext();TestClass ts = ie.Current as TestClass;print(ts.time);
}

可見,協程的本質就是一個迭代器,傳入Unity的協程調度器后,Unity自動執行協程。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/920243.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/920243.shtml
英文地址,請注明出處:http://en.pswp.cn/news/920243.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

用大語言模型提升語音翻譯:一種全新的端到端方法

用大語言模型提升語音翻譯:一種全新的端到端方法 在語音翻譯領域,如何將說話內容快速準確地轉化為另一種語言,一直是研究者們關注的焦點。隨著大語言模型(LLM)的興起,我們迎來了一個全新的機遇:利用LLM的強大能力,來提升語音翻譯系統的性能。最近,一項名為“End-to-E…

freeModbus TCP收發數據一段時間后,出現掉線情況(time out問題)

話說這個是真難找啊。我僅僅發表我找到的問題。我在接收幾十到幾百次數據的時候,會出現連接超時,也就是time out。而且ping也ping不通。也就是說明lwip出了問題。首先我先介紹modbus的這個流程。首先是函數eMBTCPInit( MB_TCP_PORT_USE_DEFAULT )我們進入…

Linux Web環境一鍵安裝腳本集合(非docker)

?重磅!盹貓的個人小站正式上線啦~誠邀各位技術大佬前來探秘!? —— 專為開發者打造的寶藏基地,等你來探索! 這里有: 🔥 硬核技術干貨:編程技巧、開發經驗、踩坑指南,帶…

原生安卓#基于Android的愛好者分享論壇的設計與實現/基于Android在線論壇系統app/基于Android的論壇系統的設計與實現的設計與實現

原生安卓#基于Android的愛好者分享論壇的設計與實現/基于Android在線論壇系統app/基于Android的論壇系統的設計與實現的設計與實現

基于Android的超市購物系統的設計與實現、基于android的在線商城app/基于android的在線銷售系統app#android

基于Android的超市購物系統的設計與實現、基于android的在線商城app/基于android的在線銷售系統app#android

C++14 到 C++20 全面解析:語言新特性、標準庫演進與實戰案例

一、前言C 作為一門歷史悠久且不斷演進的編程語言,在 C11 之后進入了“現代化”的快車道。C11 被稱為 C 的第二次誕生,引入了 lambda 表達式、智能指針、右值引用、并發支持等革命性特性。然而,C 的標準化進程并沒有止步于此。C14、C17 和 C2…

HarvardX TinyML小筆記2(番外1:TFLite)

1 原理 tflite就是Tensorflow的輕量化模型,核心處理就是量化和剪枝。不過這部分目前是在Tensorflow中封裝了,所以這里也不會去看細節,主要就是看看原理和使用方法。 量化Quantization,其實就是把原來的float32換成int8。這樣一個…

向量庫Qdrant vs Milvus 系統詳細對比

Qdrant vs Milvus 系統詳細對比 一、它們是什么(定位) 兩者都是專門做向量相似搜索的數據庫:支持ANN(近似最近鄰)檢索、向量結構化過濾、REST/gRPC 接口與官方SDK;Milvus 官方也定位為"面向GenAI、可…

適配歐拉操作系統

背景 客戶指定服務器環境歐拉操作系統,版本:6.6.0-72.0.0.76.oe2403sp1.x86_64 需要把Java 應用以及各種中間件部署在歐拉操作系統上。 問題適配MySQL 1.1 編譯報錯 mysql-5.7.40-el7-x86_64.tar.gz版本在CentOS7環境安裝正常 當前歐拉環境直接使用CentO…

學習spring Bean的生命周期

完整項目結構 ├── pom.xml └── src/├── main/│ ├── java/│ │ └── com/│ │ └── zhang/│ │ ├── bean/│ │ │ ├── Address.java│ │ │ ├── MyBeanPostProcessor.java│ │ …

elasticsearch 7.17.23 使用spring data es實現高亮分頁,scroll查詢分頁查詢

一 介紹 1.1 工程結構 1.2 啟動elasticsearch服務 1.3 高亮分頁 DeepSeek 代碼 效果: 1.4 scroll分頁 代碼 2.效果 后臺日志 1.5 完整代碼 https://gitee.com/jurf-liu/es-2.17.x-demo.git

onlyoffice整合springboot+vue實現文檔在線編輯保存

項目上需要用到在線word、excel文檔編輯功能,通過游覽器在線打開一個遠程的word文檔編輯保存,這里記錄下整合思路。 onlyoffice簡介 ONLYOFFICE 是一款開源的辦公套件,提供了一系列在線文檔編輯和協作工具,適用于團隊和個人使用…

Linux筆記10——shell編程基礎-4

補充$#——取參數個數“$n”,有值取值,無值取空字符,一般都會加引號,在某些情況下避免報語法錯誤一、read接收鍵盤輸入[rootlocalhost ~]# cat demo.sh #!/bin/bash echo -n "請輸入你的姓名:" read nameecho "你…

(Redis)過期刪除策略

1. 背景Redis 支持為 Key 設置過期時間(TTL),讓數據在一定時間后自動失效。 例如:SET session:1001 "userA" EX 60 # 60 秒后過期但是問題來了:Key 到期后,Redis 什么時候、如何刪除它&#xf…

nodejs 集成mongodb實現增刪改查

初始化項目: npm init -y npm install mongoose -save 安裝mongoose 插件 mongoose 鏈接數據庫語法: mongodb://[username:password]host1[:poert1],host2[:port2]…/[databsase]?[options…] userame: 用戶名 passwrod: 密碼 host1:port1,host2:port…

音視頻學習(五十八):STAP-A模式

什么是 STAP-A? STAP-A 是一種特殊的 RTP 封裝機制,專為 H.264 和 H.265 這類視頻編碼協議設計。它的核心目的只有一個:將多個小的 NALU(網絡抽象層單元)打包進一個 RTP 包中,以此來減少網絡開銷&#xff0…

管理型交換機通過VLAN劃分實現不同IP跨網段通信配置方法

管理型交換機應用場景豐富,如果要實現不同IP跨網段通信(比如172.22.106.X和192.168.100.X實現通信),通過VLAN劃分是可以滿足,下面分享基于弱三層交換機RTL9301方案核心模塊SW-24G4F-301EM配置方法! 1. 一般結合交換機的應用場景&a…

什么是高防服務器?如何進行防御?

高防服務器是指能為用戶提供防御網絡攻擊,是主要針對DDOS等流量型攻擊能力的服務器,通過部署專業的硬件設備與軟件系統,具備高帶寬、大流量清洗能力,能有效抵御各類惡意流量沖擊,確保服務器穩定運行,保障網…

SW - 增加導出STL數據中的三角面數,增加別人逆向建模的難度

文章目錄SW - 增加導出STL數據中的三角面數,增加別人逆向建模的難度概述筆記SW版本導出時,選擇STL的導出選項默認導出(精細)導出粗糙自定義導出 - 將誤差和角度改為最大自定義導出 - 將誤差,角度,三角面數改為最大備注這幾天的感想關于我不參考人家零件&…

四十一、【高級特性篇】API 文檔驅動:OpenAPI/Swagger 一鍵導入測試用例

四十一、【高級特性篇】API 文檔驅動:OpenAPI/Swagger 一鍵導入測試用例 前言 準備工作 第一部分:后端實現 - OpenAPI 解析與批量創建 API 1. 創建 OpenAPI 解析服務 2. 創建批量用例導入 API 3. 注冊新 API 路由 第二部分:前端實現 - OpenAPI 導入界面 1. 更新 `api/testca…