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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈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…