当前位置: 首页 > news >正文

Unity基础学习

目录

  • 基础知识点
    • 3D数学——基础
      • Mathf
      • 三角函数
      • 坐标系
    • 3D数学——向量
      • 向量模长和单位向量
      • 向量的加减乘除
      • 向量点乘
      • 向量叉乘
      • 向量插值运算
    • 3D数学——四元数
      • 为何使用四元数
      • 四元数是什么
      • 四元数常用方法
      • 四元数计算
    • MonoBehavior中的重要内容
      • 延迟函数
      • 协同程序
      • 协同程序原理
    • Resources资源动态加载
      • 特殊文件夹
      • Resources资源同步加载
      • Resources资源异步加载
      • Resources资源加载
    • 场景异步切换
      • 场景异步加载
    • LineRenderer
    • 物理系统
      • 范围检测
      • 射线检测

基础知识点

3D数学——基础

Mathf

Mathf和Math
Math是C#中封装好的用于数学计算的工具类—位于System命名空间中。
Mahtf是Unity中封装好的用于数学计算的工具结构体----位于UnityEngine命名空间中。两者都是提供用于进行数学相关计算的。
区别
Mathf和Math中的相关方法几乎一样,Math是C#自带的工具类,主要就提供一些数学相关计算方法。Mathf是unity专门封装的,不仅包含Math中的方法,还多了一些适用于游戏开发的方法,所以我们在进行Unity游戏开发时,使用Mathf中的方法用于数学计算即可。
Mathf中常用的方法----一般计算一次
1、Π——pi

Mathf.PI;

2、取绝对值——Abs

Mathf.Abs(-10);

3、向上取整——CeilToInt

Mathf.CeilToInt(1.3f);

4、向下取整——FloorToInt

Mathf.FloorToInt(9.6f);

5、钳制函数——Clamp
比小的还小取最小
比大的还大取最大
在中间的取自己

Mathf.Clamp(10,11,20);

6、获取最大值——Max

Mathf.Max(1,2,3,4,5,6,7,8);

7、获取最小值——Min

Mathf.Min(1,2,3,4,5,6,7,8);

8、一个数的n次幂——Pow

Mathf.Pow(4,2);

9、四舍五入——RoundToInt

Mathf.RoundToInt(1.3f);

10、返回一个数的平方根——Sqrt

Mathf.Sqrt(4);

11、判断一个数是否是2的n次方——IsPowerOfTwo

Mathf.isPowerOfTwo(4);

12、判断正负——Sign

Mathf.Sign(0);

Mathf中的常用方法——一般不停计算
插值运算——Lerp

Lerp函数公式
result = Mathf.Lerp(start,end,t);
t为插值系数,取值范围0~1
result = start + (end - start)*t

插值运算用法一:
每帧改变start的值,变化速度先快后慢,位置无限接近,但是不会得到end的位置

float start = 0;
start = Mathf.Lerp(start,10,Time.deltaTime);

插值运算用法二:
每帧改变t的值,变化速度均匀,位置每帧接近,当t>=1时,得到结果

float start = 0;
float result = 0;
float time = 0;
time += Time.deltaTime;
result = Mathf.Lerp(start,10,time);

三角函数

角度和弧度
角度和弧度都是度量角的单位:
角度:1°
弧度:1 radian
圆一周的角度:360°
圆一周的弧度:2Π radian
角度和弧度的转换关系
在这里插入图片描述
弧度转角度

float rad = 1;
float anger = rad * Mathf.Rad2Deg;

角度转弧度

anger = 1;
rad = anger * Mathf.Deg2Rad; 

三角函数
Mathf中的三角函数相关函数,传入的参数需要是弧度值
例如:

Mathf.Sin(30 * Mathf.Deg2Rad);
Mathf.Cos(60 * Mathf.Deg2Rad);

反三角函数
反三角函数得到的结果是正弦或者余弦值对应的弧度

rad = Mathf.Asin(0.5f);
rad * Mathf.Rad2Deg;
rad = Mathf.Acos(0.5f);
rad * Mathf.Rad2Deg;

坐标系

世界坐标系
原点:世界的中心点
轴向:世界坐标系的三个轴向是固定的

this.transform.position;
this.transform.ratation;
this.transform.eulerAngles;
this.transform.lossyScale;

物体坐标系
原点:物体的中心点(建模时决定)
轴向:
物体右方为x轴正方向
物体上方为y轴正方向
物体前方为z轴正方向

//相对父对象的物体坐标系的位置 本地坐标 相对坐标
this.transform.localPosition;
this.transform.localEulerAngles;
this.transform.localRotation;
this.transform.localScale;

屏幕坐标系
原点:屏幕左下角
轴向:
向右为x轴正方向
向上为y轴正方向
最大宽高:
Screen.width
Screen.height

Input.mousePosition
Screen.width
Screen.height

视口坐标系
摄像机上的视口范围。
原点:屏幕左下角
轴向:
向右为x轴正方向
向上为y轴正方向
特点:
左下角为(0,0)
右上角为(1,1)
和屏幕坐标类似,将坐标单位化
坐标转换相关
1、世界转本地

this.transform.InverseTransformDirection;
this.transform.InverseTransformPoint;
this.transform.InverseTransformVector;

2、本地转世界

this.transform.TransformDirection;
this.transform.TransformPoint;
this.transform.TransformVector;

3、世界转屏幕

Camera.main.WorldToScreenPoint;

4、屏幕转世界

Camera.main.ScreenToWorldPoint;

5、世界转视口

Camera.main.WorldToViewportPoint;

6、视口转世界

Camera.main.ViewportToWorldPoint;

7、视口转屏幕

Camera.main.ViewportToScreenPoint;

8、屏幕转视口

Camera.main.ScreenToViewportPoint;

3D数学——向量

向量模长和单位向量

向量
三维向量——Vector3
1、位置——代表一个点

this.transform.position;

2、方向——代表一个方向

this.transform.forward;
this.transform.up;

两点决定——向量
在这里插入图片描述

Vector3 A = new Vector3(1,2,3);
Vector3 B = new Vector3(5,1,5);
Vector3 AB = B - A;
Vector3 BA = A - B;

零向量

Vector3.zero;

负向量

-Vector3.forward;

向量的模长
模长公式:
A向量为(x,y,z)

在这里插入图片描述

//两种
AB.magnitude;
AB.Distance(A,B);

单位向量
在这里插入图片描述

AB.normalized;

向量的加减乘除

向量加法

this.transform.Translate(Vector3.forward * 5);

1、位置+位置
无意义
2、向量+向量
向量 + 向量 = 向量
3、位置+向量
位置 + 向量 = 位置

向量减法

this.transform.Translate(-Vector3.forward * 5);

1、位置-位置
位置 - 位置 = 向量
2、向量-向量
向量 - 向量 = 向量
3、位置-向量
位置 + (-向量) = 位置
4、向量-位置
无意义
向量乘除

this.transform.localScale *= 2;
this.transform.localScale /= 2;

向量只会和标量进行乘除法运算
在这里插入图片描述

向量点乘

向量 * 向量 = 标量
在这里插入图片描述
调试画线

//画线段
//起点 终点
Debug.DrawLine(this.transform.position,this.transform.position + this.transform.forward,Color.red);
//画射线
//起点 方向
Debug.DrawRay(this.transform.position,this.transform.forward,Color.white);

通过点乘判断对象方位

public Transform target;
Debug.DrawRay(this.transform.position,this.transform.forward,Color.red);
Debug.DrawRay(this.transform.position,target.position - this.transform.position,Color.red);
//得到两个向量的点乘结果
Vector3.Dot(this.transform.forward,target.position - this.transform.position);

通过点乘推导公式算出夹角
在这里插入图片描述
1、用单位向量算出点乘结果

dotResult = Vector3.Dot(this.transfrom.forward,(target.position - this.transform.position).normalized);

2、用反三角函数得出角度

Mathf.Acos(dotResult) * Mathf.Rad2Deg;

向量叉乘

在这里插入图片描述
叉乘计算

public Transform A;
public Transform B;
Vector3.Cros(A.position,B.position);

1、A×B得到的向量同时垂直A和B
2、A×B向量垂直于A和B组成的平面
3、A×B = -(B×A)
叉乘几何意义
假设向量A和B都在XZ平面上,向量A×B
1、y > 0:B在A右侧
2、y < 0:B在A左侧

Vector3 C = Vector3.Cross(A.position,B.position);
if(C.y > 0)
{print("B在A右侧");
}
else
{print("B在A左侧");
}

向量插值运算

线性插值
在这里插入图片描述
1、每帧改变start的值(先快后慢)
2、每帧改变t的值(匀速)

public Transform target;
public Transform A;
public Transform B;
//result = start + (end - start) * t;
//1、先快后慢,每帧改变start位置,位置无限接近,但不会得到end位置
A.position = Vector3.Lerp(A.position,target.position,Time.deltaTime);private Vector3 startPos = B.position;
private float time;
//2、匀速 每帧改变时间,当t>=1时,得到结果
time += Time.deltaTime;
B.position = Vector3.Lerp(startPos,target.position,time);

第二种匀速运动,当time>=1时,我改变了目标位置后,物体会直接瞬移到目标位置,导致看不出匀速运动,解决方法如下:

public Transform target;
public Transform A;
public Transform B;
private Vector3 startPos = B.position;
private float time;
private Vector3 nowTarget;
if(nowTarget != target.position)//若当前位置与目标位置不同
{nowTarget = target.position;//将当前位置更新为目标位置time = 0;//time清0startPos = B.position;//更新物体开始位置
}
time += Time.deltaTime;
B.position = Vector3.Lerp(startPos,nowTarget,time);

球形插值
线性插值和球形插值的区别
线性:直接平移,直线轨迹
球形:直接旋转,弧形轨迹

public Transform C;
C.position = Vector3.Slerp(Vector3.right * 10,Vector3.forward * 10,time * 0.1);

3D数学——四元数

为何使用四元数

欧拉角
由三个角度(x,y,z)组成,在特定坐标系下用于描述物体的旋转量。空间中的任意旋转都可以分解成绕三个互相垂直轴的三个旋转角组成的序列。
1、旋转约定
heading-pitch-bank(Y-X-Z)是一种最常用的旋转序列约定。
2、Unity中的欧拉角
Inspector窗口中调节的Rotation就是欧拉角,this.transform.eulerAngles得到的就是欧拉角角度。
3、优缺点
优点:直观、易理解,存储空间小(三个数表示)、可以进行从一个方向到另一个方向旋转大于180度的角度。
缺点:同一旋转的表示不唯一、万向节死锁。
4、万向节死锁
当某个特定轴达到某个特殊值时,绕一个轴旋转可能会覆盖住另一个轴的旋转,从而失去一维自由度,Unity中x轴达到90度时,会产生万向节死锁。

四元数是什么

概念: 四元数时简单的超复数,由实数加上三个虚数单位组成,主要用于在三维空间中表示旋转。
构成
一个四元数包含一个标量和一个3D向量。
[w,v]:w为标量,v为3D向量
[w,(x,y,z)]
对于给定的任意一个四元数:表示是3D空间中的一个旋转量。
轴-角对
在3D空间中,任意旋转都可以表示绕着某个轴旋转一个角得到。该轴并不是空间中的x,y,z轴,而是任意一个轴。
在这里插入图片描述
Unity中的四元数
Quaternion:是Unity中表示四元数的结构体
1、计算原理
在这里插入图片描述

Quaternino q = new Quaternino(Mathf.Sin(30 * Mathf.Deg2Rad),0,0,Mathf.Cos(30 * Mathf.Deg2Rad));

2、轴角对初始化四元数的方法
在这里插入图片描述

Quaternion q2 = Quaternion.AngleAxis(60,Vector3.right);

四元数和欧拉角转换
1、欧拉角转四元数
在这里插入图片描述
2、四元数转欧拉角
在这里插入图片描述
四元数弥补的欧拉角缺点
1、同一旋转的表示不唯一
2、万向节死锁

四元数常用方法

单位四元数表示没有旋转量(角位移),当角度为0或者360度时,对于给定轴都会得到单位四元数。
例如:[1,(0,0,0)][-1,(0,0,0)]
单位四元数

Quaternion.identity;

插值运算
四元数中同样提供如同Vector3的插值运算Lerp和Slerp。在四元数中Lerp和Slerp只有一些细微差别,由于算法不同,Slerp的效果会好一些,Lerp的效果相比Slerp更快但是旋转范围较大效果较差,所以建议使用Slerp进行插值运算。

public Transform target;
public Transform A;
public Transform B;
private Quaternion start;
private float time;
//无限接近 先快后慢
A.transform.rotation = Quaternion.Slerp(A.transform.rotation,target.rotation,Time.deltaTime);
//匀速变化 time>=1到达目标
time += Time.deltaTime;
B.transform.rotation = Quaternion.Slerp(start,target.rotation,time);

向量指向转四元数
Quaternion.LookRotation(面朝向量):该方法可以将传入的面朝向量,转换为对应的四元数角度信息。
例如:当人物面朝向想要改变时,只需要把目标面朝向传入该函数,便可以得到目标四元数角度信息,之后将人物四元数角度信息改为得到的信息即可达到转向。

Quaternion q = Quaternion.LookRotation(lookB.position - lookA.position);
lookA.rotation = q;

四元数计算

四元数相乘
q3 = q1 * q2:两个四元数相乘得到一个新的四元数,代表两个旋转量的叠加,相当于旋转。旋转相对的坐标系,是物体自身坐标系。

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

四元数乘向量
v2 = q1 * v1:四元数乘向量返回一个新向量,可以将指定向量旋转对应四元数的旋转量,相当于旋转向量。

Vector3 v = Vector3.forward;
v = Quaternion.AngleAxis(45,Vector3.up) * v;//四元数写在前面,向量写在后面

MonoBehavior中的重要内容

延迟函数

会延时执行的函数,我们可以自己设定延时要执行的函数和具体延时的时间,是MonoBehaviour基类中实现好的方法。
使用
1、延迟函数
Invoke:
参数一:函数名 字符串
参数二:延迟时间 秒为单位

private void DelayDoSomething()
{
}
Invoke("DelayDoSomething",5);

注意:
①、延迟函数第一个参数传入的是函数名字符串
②、延迟函数没办法传入参数,只有包裹一层
③、函数名必须是该脚本上声明的函数

2、延迟重复执行函数
InvokeRepeating
参数一:函数名字符串
参数二:第一次执行的延迟时间
参数三:之后每次执行的间隔时间

InvokeRepeating("DelayRe",5,1);

3、取消延迟函数
取消该脚本上的所有延迟函数执行
①取消该脚本上的所有延迟函数执行

CancelInvoke();

②指定函数名取消

CancelInvoke("DelayDoSomething");

4、判断是否有延迟函数

if(IsInvoking())
{print("存在延迟函数");
}
if(IsInvoking("DelayDoSomething"))
{print("存在延迟函数");
}

延迟函数受对象失活销毁影响
脚本依附对象失活,延迟函数可以继续执行。
脚本依附对象销毁或者脚本移除,延迟函数无法继续执行。

协同程序

Unity是否支持多线程?
unity支持多线程,只是新开线程无法访问unity相关对象的内容。
注意:unity中的多线程,要记住关闭。

private void Test()
{
}
Thread t;
t = new Thread(Test);
//关闭
private void OnDestroy()
{t.Abort();t = null;
}

协同程序是什么?
协同程序简称协程,它是”假“的多线程,他不是多线程。
1、主要作用:将代码分时执行,不卡主线程,就是把可能会让主线程卡顿的耗时的逻辑分时分步执行。
2、主要使用场景:

  • 异步加载文件
  • 异步下载文件
  • 场景异步加载
  • 批量创建时防止卡顿

协同程序和线程的区别
1、新开一个线程是独立的一个管道,和主线程并行执行。
2、新开一个协程是在原线程之上开启,进行逻辑分时分步执行。
协程的使用
继承MonoBehavior的类,都可以开启协程函数。
1、声明协程函数
关键:
①返回值为IEnumerator类型及其子类
②函数中通过yield return 返回值进行返回

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

2、开启协程函数
常用开启方式

MyCoroutine(1,"123");//错误执行方式
StartCoroutine(MyCoroutine(1,"123"));
IEnumerator ie = MyCoroutine(1,"123");
StartCoroutine(ie);

3、关闭协程
①关闭所有协程

StopAllCoroutines();

②关闭指定协程

StopCoroutine(c1);

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 new WaitForEndOfFrame();
在LateUpdate之后的渲染相关处理完毕之后执行
5、一些特殊类型的对象,比如异步加载相关函数返回的对象
一般在Update和LateUpdate之间执行
6、跳出协程
yield break;
协程受对象和组件失活销毁的影响
协程开启后,
组件和物体销毁,协程不执行
物体失活协程不执行,组件失活协程执行

协同程序原理

协程本质
协程可分为两部分:
1、协程函数本体
2、协程调度器
协程本体就是一个能够中间暂停返回的函数,协程调度器是Unity内部实现的,会在对应的时机帮助我们继续执行协程函数。Unity只实现了协程调度部分,协程的本体本质上是一个C#的迭代器方法。
协程本体是迭代器方法的体现
1、协程函数本体
如果不通过开启协程方法执行协程,Unity的协程调度器是不会帮助我们管理协程函数的。

IEnumerator ie = Test();

自己执行迭代器函数的内容

ie.MoveNext();//执行函数中内容遇到yield return为止的逻辑
print(ie.Current);//得到yield return返回的内容

2、协程调度器
继承MonoBehavior后开启协程,相当于是把一个协程函数(迭代器)放入unity的协程调度器中帮助我们管理进行执行,具体的yield return后面的规则,也是unity定义的一些规则。

Resources资源动态加载

特殊文件夹

工程路径获取
该方式获取到的路径一般情况下只在编辑模式下使用,不会再实际发布游戏后,还使用该路径。游戏发布后,该路径就不存在了。

Application.dataPath;

Resources资源文件夹
路径获取:
一般不获取,只能使用Resources相关API进行加载,如果硬要获取,可以用工程路径拼接。该文件夹需要我们自己来创建。

print(Application.dataPath + "/Resources");

作用:
1、需要通过Resource相关API动态加载的资源需要放在其中
2、该文件夹下所有文件都会被打包出去
3、打包时Unity会对其压缩加密
4、该文件夹打包后只读,只能通过Resources相关API加载
StreamingAssets流动资源文件夹
该文件夹需要自己创建
路径获取:

print(Application.streamingAssetsPath);

作用:
1、打包出去不会被压缩加密,可以任由我们摆布
2、移动平台只读,pc平台可读可写
3、可以放入一些需要自定义动态加载的初始资源
persistentDataPath持久数据文件夹
不需要我们自己创建
路径获取:

print(Application.persistentDataPath);

作用:
固定数据文件夹
1、所有平台都可读可写
2、一般用于放置动态下载或者动态创建的文件,游戏中创建或者获取的文件都放在其中
Plugins插件文件夹
需要我们自己创建
路径获取:
一般不获取
作用:
不同平台的插件相关文件放在其中,例如:IOS和Android
Editor编辑器文件夹
需要自己创建
路径获取:
一般不获取,如果硬要获取,可以用工程路径拼接

print(Application.dataPath + "/Editor");

作用:
1、开发Unity编辑器时,编辑器相关脚本放在该文件夹中
2、该文件夹中内容不会被打包出去
默认资源文件夹Standard Asssets
需要我们自己创建
路径获取:
一般不获取
作用:
一般unity自带资源都放在这个文件夹下
代码和资源优先被编译

Resources资源同步加载

Resources资源动态加载的作用
1、通过代码动态加载Resources文件夹下指定路径资源
2、避免繁琐的拖曳操作
常用资源类型
1、预设体对象——GameObject
2、音效文件——AudioClip
3、文本文件——TextAsset
4、图片文件——Texture
5、其他类型
注意:预设体对象加载需要实例化,其他资源加载一般直接用
资源同步加载——普通方法
1、预设体对象 想要创建在场景上
①加载预设体的资源文件
本质上就是加载配置数据,在内存中。

Object obj = Resources.Load("Cube");

②实例化

Instantiate(obj);

2、音效资源

public AudioSource audios;
Object obj3 = Resources.Load("Music/BKMusic");//加载数据
audios.clip = obj3 as AudioClip;//使用数据
audios.Play();

3、文本资源
支持的格式:
.txt
.xml
.bytes
.json
.html
.csv

TextAsset ta = Resources.Load("Txt/Test") as TextAsset;
print(ta.text);//文本内容
print(ta.bytes);//字节数据组

4、图片

pirvate Texture tex;
tex = Resources.Load("Tex/TestJPG") as Texture;
private void OnGUI()
{GUI.DrawTexture(new Rect(0,0,100,100),tex);
}

5、资源同名怎么办?
Resources.Load加载同名资源时,无法准确加载出你想要的内容
可以使用另外的API:

  • 加载指定类型的资源
tex = Resources.Load("Tex/TestJPG",typeof(Texture)) as Texture;
  • 加载指定名字的所有资源
Object[] objs = Resources.LoadAll("Tex/TestJPG");
foreach(Object item in objs)
{}

资源同步加载——泛型方法

TextAsset ta2 = Resources.Load<TextAsset>("Tex/TestJPG");

Resources资源异步加载

Resources异步加载是什么
在同步加载中,如果加载过大的资源可能会造成程序卡顿,其原因是,从硬盘上把数据读取到内存中,是需要进行计算的,越大的资源耗时越长,就会造成掉帧卡顿。
Resources异步加载,就是内部新开一个线程进行资源加载,不会造成主线程卡顿。
Resouces异步加载方法
异步加载不能马上得到加载的资源,至少要等一帧
1、通过异步加载中的完成事件监听使用加载的资源

private Texture tex;
private void LoadOver(AsyncOperation rq)
{//asset是资源对象加载完毕后就可以得到tex = (rq as ResourceRequest).asset as Texture;
}
ResourceRequest rq = Resources.LoadAsync<Texture>("Tex/TestJPG");   
rq.completed += LoadOver;//事件函数监听

2、通过协程使用加载的资源

StartCoroutine(Load());
IEnumerator Load()
{ResourceRequest rq = Resources.LoadAsync<Texture>("Tex/TestJPG"); //Unity自己知道该返回值意味着在异步加载资源yield return rq;	//Unity会自己判断该资源是否加载完毕了,加载完毕以后才会继续执行后面的代码。tex = rq.asset as Texture;//判断资源是否加载结束while(!rq.isDone){//打印当前的加载进度,该进度不会特别准确,过渡也不是特别明显print(rq.priority);yield return null;}
}

总结:
①完成事件监听异步加载(线性加载)
优点:写法简单
缺点:只能在资源加载结束后进行处理
②协程异步加载(并行加载)
优点:可以在协程中处理复杂逻辑,比如同时加载多个资源,比如进度条更新。
缺点:写法稍麻烦

Resources资源加载

Resources重复加载资源会浪费内存吗?
其实Resources加载一次资源过后,该资源就一直存放在内存中作为缓存,第二次加载时发现缓存中存在该资源,会直接取出来进行使用,所以多次重复加载不会浪费内存,但是会浪费性能(每次加载都会去查找取出,始终伴随一些性能消耗)
如何手动释放掉缓存中的资源
1、卸载指定资源
Resources.UnloadAsset方法:
该方法不能释放GameObject对象,因为它会用于实例化对象,它只能用于一下不需要实例化的内容,比如:图片、音效、文本等等。一般情况下,我们很少单独使用它。
2、卸载未使用的资源
一般在过场景时和GC一起使用。

Resources.UnloadUnusedAssets();
GC.Collect();

场景异步切换

场景异步加载

回顾场景同步切换

SceneManager.LoadScene("Lesson20Test");

在切换场景时,Unity会删除当前场景上所有对象,并且去加载下一个场景的相关信息,如果当前场景对象过多或者下一个场景对象过多,这个过程会非常的耗时,会让玩家感受到卡顿。
所以使用异步切换解决该问题。
场景异步切换
场景异步切换和资源异步加载,几乎一致,有两种方式:
1、通过事件回调函数,异步加载

AsyncOperation ao = SceneManager.LoadSceneAsync("Lesson20Test");
//当场景异步加载结束后,就会自动调用该事件函数,
//我们如果希望在加载结束后做一些事情,那么就可以在该函数中写处理逻辑
ao.completed += (a) =>
{print("加载完毕");
}

2、通过协程异步加载
加载场景会把当前场景上,没有特别处理的对象都删除,所以协程中的部分逻辑可能是执行不了的。
解决思路:让处理场景加载的脚本依附的对象过场景时,不被移除。

//解决方法:该脚本依附的对象过场景时,不会被移除
DontDestroyOnLoad(this.gameObject);
StartCoroutine(LoadScene("Lesson20Test"));
IEnumerator LoadScene(string name)
{AsyncOperation ao = SceneManager.LoadSceneAsync(name);yield return ao;//unity内部的协程协调器,发现是异步加载类型的返回对象,就会等待//等待异步加载结束后,才会继续执行,迭代器函数后面的步骤。//场景加载完毕后的这部分逻辑无法执行,因为挂载到场景中的对象的该脚本被移除
}

在异步加载过程中,可以去更新进度条
①利用场景异步加载的进度去更新,但是不是特别准确,一般也不会直接用。

while(!ao.isDone)
{print(ao.progress);yield return null;
}

离开循环后,就会认为场景加载结束,可以把进度条顶满,然后隐藏进度条。
②根据游戏规则自己定义进度条变化的条件

yield return ao;
//场景加载结束更新20%进度
//接着去加载场景中的其他信息
//例如:动态加载怪物再更新20%进度
//动态加载场景模型进度条顶满
//隐藏进度条

总结:
1、事件回调函数
优点:写法简单,逻辑清晰
缺点:只能加载完场景做一些事情,不能在加载过程中处理逻辑
2、协程异步加载
优点:可以在加载过程中处理逻辑,比如进度条更新等。
缺点:写法较为麻烦,要通过协程。

LineRenderer

LineRenderer是什么?
LineRenderer是unity提供的一个用于画线的组件,使用它我们可以在场景中绘制线段。一般可以用于:
1、绘制攻击范围
2、武器红外线
3、辅助功能
4、其他画线功能
LineRenderer参数相关
1、Loop:是否终点起始自动相连
2、Positions:线段的点
3、Width:线段宽度曲线调整
4、Color:颜色变化
5、Corner Vertices(角顶点、圆角):此属性指示在一条线中绘制时使用了多少额外的顶点。增加此值,使线角看起来更圆。
6、End Cap Vertices(终端顶点,圆角):终点圆角
7、Alignment对齐方式:

  • View 视点:线段对着摄像机
  • Transform Z:线段面向其Z轴

8、Texture Mode 纹理模式:

  • Stretch 拉伸:沿整条线映射纹理一次
  • Tile 瓷砖平铺:不停的重复纹理
  • Distribute Per Segment 分配执行
  • Repeat Per Segment 重复显示

9、Shadow Bias 阴影偏移
10、Generate Lighting Data 生成光源数据
11、Use World Space 是否使用世界坐标系
12、Materials 线使用的材质球
13、Lighting 光照影响

  • Cast Shadows 是否开启阴影
  • Receive Shadowss 接收阴影

14、Probes 光照探针
Light Probes 光探测器模式

  • 不使用光探针
  • 使用内插光探针
  • 使用三维网格内插光探针
  • 自定义从材质决定

Reflection Probes 反射探测器模式

  • 不使用反射探针
  • 启用混合反射探针
  • 启用混合反射探针和天空盒混合
  • 启用普通探针,重叠式不混合

15、Additional Settings 附加设置

  • Motion Vectors 运动矢量
    ①使用相机运动来跟踪运动
    ②特定对象来跟踪运动
    ③不跟踪
  • Dynamic Occludee 动态遮挡剔除
  • Sorting Layer 排序图层
  • Order in Layer 此线段在排序图层中的顺序
    新编辑模式
    在这里插入图片描述
    LineRenderer代码相关
    1、动态添加一个线段
GameObject line = new GameObject();
line.name = "Line";
LineRenderer lineRenderer = line.AddComponent<LineRenderer>();

2、首尾相连

lineRenderer.loop = true;

3、开始结束宽

lineRenderer.startWith = 0.02f;
lineRenderer.endWith = 0.02f;

4、开始结束颜色

lineRenderer.startColor = Color.white;
lineRenderer.endColor = Color.white;

5、设置材质

private Material m = Resouces.Load<Material>("M");
lineRenderer.material = m;

6、设置点
要先设置点的个数

lineRenderer.positionCount = 4;
lineRenderer.SetPositions(new Vector3[]{new Vector3(0,0,0},new Vector3(0,0,5),new Vector3(5,0,5)});

7、是否使用世界坐标系
决定了是否随对象移动而移动

lineRenderer.useWorldSpace = false;

8、让线段受光影响
会接受光数据,进行着色器计算

lineRenderer.generateLightingData = true;

物理系统

范围检测

什么是范围检测
游戏中瞬时的攻击范围判断一般会使用范围检测
举例:
1、玩家在前方5m处释放一个地刺魔法,在此处范围内对象将受到地刺伤害。
2、玩家攻击,在前方1米圆形范围内对象都受到伤害
类似这种并没有实体物体,只想要检测在某一范围是否让敌方受到伤害时,便可以使用范围判断。简而言之,在指定位置进行范围判断,我们可以得到处于指定范围内的对象,目的是对对象进行处理,比如受伤 减血等等。
如何进行范围检测
必备条件:想要被范围检测到的对象,必须具备碰撞器
注意点:
1、范围检测相关API,只有当执行该句代码时,进行一次范围检测,它是瞬时的
2、范围检测相关API并不会真正产生一个碰撞器,只是碰撞判断计算而已。

范围检测API
1、盒状范围检测
参数一:立方体中心点
参数二:立方体三边大小
参数三:立方体角度
参数四:检测指定层级(不检测所有层)
参数五:是否忽略触发器 UseGlobal——使用全局设置 Collide——检测触发器 Ignore——忽略触发器 不填使用UseGlobal
返回值:在该范围内的触发器(得到了对象触发器就可以得到对象的所有信息)

Collider[] colliders = Physics.OverlapBox(Vector3.zero,Vector3.one,Quaternion.AngleAxis(45,Vector3.up),1 << LayerMask.NameToLayer("UI") | 1 << LayerMask.NameToLayer("Default"),QueryTriggerInteraction.UseGlobal);

重要知识——关于层级
通过名字得到层级编号 LayerMask.NameToLayer
我们需要通过编号左移构建二进制数,这样每一个编号的层级都是对应位为1的二进制数。
我们通过位运算,可以选择想要检测的层级
好处是一个int就可以表示所有想要检测的层级信息

另一个API:
返回值:碰撞到的碰撞器数量
参数:传入一个数组进行存储

Physics.OverlapBoxNonAlloc(Vector3.zero,Vector3.one,colliders);

2、球形范围检测
参数一:中心点
参数二:球半径
参数三:检测指定层级(不填检测所有层)
参数四:是否忽略触发器 UseGlobal——使用全局设置 Collide——检测触发器 Ignore——忽略触发器 不填使用UseGlobal
返回值:在该范围内的触发器(得到了对象触发器就可以得到对象的所有信息)

Physics.OverlapSphere(Vector3.zero,5,1 << LayerMask.NameToLayer("Default"));

另一个API:
返回值:碰撞到的碰撞器数量
参数:传入一个数组进行存储

Physics.OverlapSphereNonAlloc(Vector3.zero,5,colliders);

3、胶囊范围检测
参数一:半圆一中心点
参数二:半圆二中心点
参数三:半圆半径
参数四:检测指定层级(不检测所有层)
参数五:是否忽略触发器 UseGlobal——使用全局设置 Collide——检测触发器 Ignore——忽略触发器 不填使用UseGlobal
返回值:在该范围内的触发器(得到了对象触发器就可以得到对象的所有信息)

Physics.OverlapCapsule(Vector3.zero,Vector3.up,1,1 << LayerMask.NameToLayer("UI"),QueryTriggerInteraction.UseGlobal);

另一个API
返回值:碰撞到的碰撞器数量
参数:传入一个数组进行存储

Physics.OverlapCapsuleNonAlloc(Vector3.zero,Vector3.up,1,colliders);

射线检测

什么是射线检测
在指定点发射一个指定方向的射线,判断该射线与哪些碰撞器相交,得到对应对象。
射线对象
1、3D世界中的射线
假设有一条起点为坐标(1,0,0),方向为世界坐标z轴正方向的射线。
参数一:起点
参数二:方向
不是两点决定射线方向,第二个参数直接代表方向向量。

Ray r = new Ray(Vector3.right,Vector3.forward);

Ray中的参数

r.origin;//起点
r.direction;//方向

2、摄像机发射出的射线
得到一条从屏幕位置作为起点,摄像机视口方向为方向的射线

Ray r2 = Camera.main.ScreenPointToRay(Input.mousePosition);

碰撞检测函数
Physics类中提供了很多进行射线检测的静态函数,射线检测也是瞬时的,执行代码时进行一次射线检测。
1、最原始的射线检测
准备一条射线

Ray r3 = new Ray(Vector3.zero,Vector3.forward);

进行射线检测,如果碰撞到对象返回true
参数一:射线
参数二:检测的最大距离,超出这个距离不检测
参数三:检测指定层级(不填检测所有层)
参数四:是否忽略触发器 UseGlobal——使用全局设置 Collide——检测触发器 Ignore——忽略触发器 不填使用UseGlobal
返回值:bool 当碰撞到对象时返回true 没有返回false

Physics.Raycast(r3,1000,1 << LayerMask.NameToLayer("Monster"),QueryTriggerInteraction.UseGlobal);

另一种重载:不用传入射线直接传入起点和方向也可以用于判断,就是把第一个参数射线变成了射线的两个点,一个起点,一个方向。

Physics.Raycast(Vector3.zero,Vector3.forward,1000,1 << LayerMask.NameToLayer("Monster"),QueryTriggerInteraction.UseGlobal);

2、获取相交的单个物体信息
物体信息类 RaycastHit

RaycastHit hitInfo;

参数一:射线
参数二:RaycastHit是结构体 是值类型,Unity会通过out关键字在函数内部处理后得到碰撞数据后返回到该参数中。
参数三:距离
参数四:检测指定层级(不填检测所有层)
参数五:是否忽略触发器 UseGlobal——使用全局设置 Collide——检测触发器 Ignore——忽略触发器 不填使用UseGlobal

if(Physics.Raycast(r3,out hitInfo,1000,1 << LayerMask.NameToLayer("Monster"),QueryTriggerInteraction.UseGlobal))
{print(hitInfo.collider.gameObject.name);//碰撞器信息print(hitInfo.point);//碰撞到的点print(hitInfo.normal);//法线print(hitInfo.transform.position);//得到碰撞到的对象的位置print(hitInfo.distance);//得到碰撞到的对象离自己的距离
}

另一种重载:
不用传入射线,直接传入起点和方向,也可以用于判断

if(Physics.Raycast(Vector3.zero,Vector3.forward,out hitInfo,1000,1 << LayerMask.NameToLayer("Monster"),QueryTriggerInteraction.UseGlobal))
{
}

3、获取相交的多个物体
可以得到碰撞到的多个对象,如果没有就是容量为0的数组
参数一:射线
参数二:距离
参数三:检测指定层级(不填检测所有层)
参数四:是否忽略触发器 UseGlobal——使用全局设置 Collide——检测触发器 Ignore——忽略触发器 不填使用UseGlobal

RaycastHit[] hits = Physics.RaycastAll(r3,1000,1 << LayerMask.NameToLayer("Monster"),QueryTriggerInteraction.UseGlobal)

另一种重载:
不用传入射线,直接传入起点和方向,也可以用于判断

RaycastHit[] hits = Physics.RaycastAll(Vector3.zero,Vector3.forward,1000,1 << LayerMask.NameToLayer("Monster"),QueryTriggerInteraction.UseGlobal)

还有一种函数,返回的碰撞的数量,通过out得到数据

Physics.RaycastNonAlloc(r3,hits,1000,1 << LayerMask.NameToLayer("Monster"),QueryTriggerInteraction.UseGlobal);

使用时注意的问题
距离、层级两个参数都是int类型,当我们传入参数时,一定要明确传入的参数代表的是距离还是层级。

相关文章:

Unity基础学习

目录 基础知识点3D数学——基础Mathf三角函数坐标系 3D数学——向量向量模长和单位向量向量的加减乘除向量点乘向量叉乘向量插值运算 3D数学——四元数为何使用四元数四元数是什么四元数常用方法四元数计算 MonoBehavior中的重要内容延迟函数协同程序协同程序原理 Resources资源…...

Java并发编程学习笔记:AQS

Java并发编程学习笔记&#xff1a;AQS 一、底层原理核心功能同步状态管理CLH 队列和线程调度机制独占模式与共享模式模板方法设计模式自旋、阻塞与超时机制 运行流程 二、锁的公平性公平锁非公平锁 三、容器实现 JUC中的AQS&#xff08;AbstractQueuedSynchronizer&#xff09;…...

Github上哪些好用的工具

专注于web漏洞挖掘、内网渗透、免杀和代码审计&#xff0c;感谢各位师傅的关注&#xff01;网安之路漫长&#xff0c;与君共勉&#xff01; Qexo-爱写博客的师傅强烈推荐 漂亮的 Hexo 静态博客编辑器。该项目是基于 Django 的 Hexo 静态博客管理后台&#xff0c;支持文章管理、…...

如何确保面试流程标准化操作,避免人为因素影响**

一、背景 在招聘过程中,面试作为关键环节,其标准化操作至关重要。标准化不仅有助于提高面试效率和质量,还能减少人为因素的影响,确保公平、公正和客观。本文将从以下八个方面探讨如何确保面试流程的标准化操作。 二、明确面试标准 制定明确的面试标准和要求,确保所有面试…...

YOLOv7改进 | 更换主干网络之PP-LCNet

前言:Hello大家好,我是小哥谈。PP-LCNet是一个由百度团队针对Intel-CPU端加速而设计的轻量高性能网络。它是一种基于MKLDNN加速策略的轻量级卷积神经网络,适用于多任务,并具有提高模型准确率的方法。与之前预测速度相近的模型相比,PP-LCNet具有更高的准确性。此外,对于计…...

MySQL基础-----多表查询之子查询

目录 前言 子查询概述 1.概念 2.分类 一、标量子查询 二、列子查询 三、行子查询 四、表子查询 前言 上一期我们讲了内外连接查询以及自连接查询&#xff0c;那么本期我们就学习多表查询的子查询。本期会详细讲解什么是子查询&#xff0c;以及子查询的相关功能&#xf…...

nginx应用场景(附配置)

场景1&#xff1a;web服务器 server {listen 80;server_name example.com; # 替换为您的域名location / {root /data/wwwroot;index index.html index.htm;} }server {listen 443 ssl;server_name example.com; # 替换为您的域名ssl_certificate /path/to/certificate.crt;ssl…...

tvm android_rpc_test.py执行报错解决

执行 python3 tests/android_rpc_test.py 报错&#xff1a; Run CPU test ... Traceback (most recent call last): File "tests/android_rpc_test.py", line 129, in <module> test_rpc_module() File "tests/android_rpc_test.py", line …...

十、项目沟通管理

十、项目沟通管理 从马斯洛需求的各个层级上&#xff0c;都需要沟通的介入。如果缺乏沟通&#xff0c;甚至可能严重损伤身心健康。 沟通渠道 1、 规划沟通管理 ​ 规划沟通管理是基于每个相关方或相关方群体的信息需求、可用的组织资产&#xff0c;以及具体项目的需求&#x…...

SQL设计时增加说明列

后关闭sql Studio,然后打开注册表,注册表地址: 计算机\HKEY_CURRENT_USER\SOFTWARE\Microsoft\SQL Server Management Studio\18.0_IsoShell\DataProject 如有版本不同,红色内容有所变化,修改内容如下: SSVPropViewColumnsSQL70,SSVPropViewColumnsSQL80 全修改为 1,2,6,7…...

前端提高性能——使用Intersection Observer API对图片视频进行懒加载

前言 最近做了一个项目是类似于商城的&#xff0c;需要放很多图片&#xff0c;在用户选择一页五十条时&#xff0c;页面加载速度会比较慢。为了提高性能&#xff0c;选择用Intersection Observer API 实现图片懒加载。 实现步骤 一、html代码&#xff1a; <img class&qu…...

杂七杂八111

MQ 用处 一、异步。可提高性能和吞吐量 二、解耦 三、削峰 四、可靠。常用消息队列可以保证消息不丢失、不重复消费、消息顺序、消息幂等 选型 一Kafak:吞吐量最大&#xff0c;性能最好&#xff0c;集群高可用。缺点&#xff1a;会丢数据&#xff0c;功能较单一。 二Ra…...

微信小程序(一)

WebView app.是全局配置&#xff0c;app.json是全局配置文件&#xff0c;在页面的.json配置文件中的配置会覆盖我们全局的配置 快捷键&#xff1a; .box 敲回车 ----- <view class"box"></view> .row*8 敲回车&#xff1a; .row{$}*8 敲回车 案例1&…...

【DL经典回顾】激活函数大汇总(十二)(GLU ReGLU附代码和详细公式)

激活函数大汇总&#xff08;十二&#xff09;&#xff08;GLU & ReGLU附代码和详细公式&#xff09; 更多激活函数见激活函数大汇总列表 一、引言 欢迎来到我们深入探索神经网络核心组成部分——激活函数的系列博客。在人工智能的世界里&#xff0c;激活函数扮演着不可或…...

【C++】string类初步介绍

个人主页 &#xff1a; zxctscl 如有转载请先通知 文章目录 1. 为什么学习string类1.1 C语言中的字符串1.2 推荐学习网站 2. 标准库中的string类2.1 string类2.2 string类的常用接口说明2.2.1 constructor2.2.2 遍历string2.2.2.1 下标加[]遍历2.2.2.2 迭代器&#xff08;itera…...

HCIP --- BGP 综合实验

目录 实验拓扑图&#xff1a; 实验要求&#xff1a; 实验步骤&#xff1a; 1.划分IP地址 R1的配置 R2的配置 R3的配置 R4的配置 R5的配置 R6的配置 R7的配置 R8的配置 2.检测查询IP地址 3.OSPF 建邻 4.查询OSPF 建邻是否成功 5.AS 2 内部BGP 建邻 a. AS 2 内部 IBG…...

【优选算法】专题1 -- 双指针 -- 移动零

前言: &#x1f4da;为了提高算法思维&#xff0c;我会时常更新这个优选算法的系列&#xff0c;这个专题是关于双指针的练习 &#x1f3af;个人主页&#xff1a;Dream_Chaser&#xff5e;-CSDN博客 一.移动零&#xff08;easy&#xff09; 描述&#xff1a; 「数组分两块」是⾮…...

【计算机视觉】二、图像形成:2、几何基元和几何变换:2D变换

文章目录 一、向量和矩阵的基本运算二、几何基元和变换1、几何基元(Geometric Primitives)2、几何变换(Geometric Transformations)1. 各种变换的关系2. 变换公式3. 2D变换的层次4. python实现 一、向量和矩阵的基本运算 【计算机视觉】二、图像形成&#xff1a;1、向量和矩阵…...

蓝桥杯---棋盘(典型的二维差分问题)

题目链接&#xff1a;棋盘 这道题真的是非常典型的二维差分问题了&#xff08;在我个人看来&#xff09;&#xff0c;题目中的0和1&#xff0c;我们直接让差分数组&#xff0c;偶数就是0&#xff0c;奇数就是1.初始化是0&#xff0c;是白子&#xff08;偶数&#xff09;&#x…...

OpenHarmony教程指南—ArkTS时钟

简单时钟 介绍 本示例通过使用ohos.display 接口以及Canvas组件来实现一个简单的时钟应用。 效果预览 使用说明 1.界面通过setInterval实现周期性实时刷新时间&#xff0c;使用Canvas绘制时钟&#xff0c;指针旋转角度通过计算得出。 例如&#xff1a;"2 * Math.PI /…...

Docker 离线安装指南

参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性&#xff0c;不同版本的Docker对内核版本有不同要求。例如&#xff0c;Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本&#xff0c;Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...

设计模式和设计原则回顾

设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...

【OSG学习笔记】Day 18: 碰撞检测与物理交互

物理引擎&#xff08;Physics Engine&#xff09; 物理引擎 是一种通过计算机模拟物理规律&#xff08;如力学、碰撞、重力、流体动力学等&#xff09;的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互&#xff0c;广泛应用于 游戏开发、动画制作、虚…...

Debian系统简介

目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版&#xff…...

(二)TensorRT-LLM | 模型导出(v0.20.0rc3)

0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述&#xff0c;后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作&#xff0c;其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...

Golang dig框架与GraphQL的完美结合

将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用&#xff0c;可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器&#xff0c;能够帮助开发者更好地管理复杂的依赖关系&#xff0c;而 GraphQL 则是一种用于 API 的查询语言&#xff0c;能够提…...

macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用

文章目录 问题现象问题原因解决办法 问题现象 macOS启动台&#xff08;Launchpad&#xff09;多出来了&#xff1a;Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显&#xff0c;都是Google家的办公全家桶。这些应用并不是通过独立安装的…...

Java面试专项一-准备篇

一、企业简历筛选规则 一般企业的简历筛选流程&#xff1a;首先由HR先筛选一部分简历后&#xff0c;在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如&#xff1a;Boss直聘&#xff08;招聘方平台&#xff09; 直接按照条件进行筛选 例如&#xff1a…...

【网络安全】开源系统getshell漏洞挖掘

审计过程&#xff1a; 在入口文件admin/index.php中&#xff1a; 用户可以通过m,c,a等参数控制加载的文件和方法&#xff0c;在app/system/entrance.php中存在重点代码&#xff1a; 当M_TYPE system并且M_MODULE include时&#xff0c;会设置常量PATH_OWN_FILE为PATH_APP.M_T…...

Python常用模块:time、os、shutil与flask初探

一、Flask初探 & PyCharm终端配置 目的: 快速搭建小型Web服务器以提供数据。 工具: 第三方Web框架 Flask (需 pip install flask 安装)。 安装 Flask: 建议: 使用 PyCharm 内置的 Terminal (模拟命令行) 进行安装,避免频繁切换。 PyCharm Terminal 配置建议: 打开 Py…...