2.3D数学-Mathf数学计算公共类
2.1 知识点
Mathf和Math
- Math是C#中封装好的用于数学计算的工具类,位于System命名空间中。
- Mathf是Unity中封装好的用于数学计算的工具结构体,位于UnityEngine命名空间中。
- 他们都是提供来用于进行数学相关计算的。
Mathf和Math的区别
- Mathf 和 Math中的相关方法几乎一样。
- Math - C#自带数学库,提供基本的数学计算方法。
- Mathf - Unity专门针对游戏开发增强的数学库,包含了Math中的方法,并添加了一些适用于游戏开发的方法。
Mathf中的常用方法——一般计算一次
PI常量 获取圆周率π
// 众所周知的“3.14159265358979...”值(只读)。
print(Mathf.PI);//3.141593
Abs静态方法 取绝对值
// 返回 f 的绝对值。
print(Mathf.Abs(-10));//10
print(Mathf.Abs(-20));//20
print(Mathf.Abs(1));//1
CeilToInt静态方法 向上取整
// 返回大于或等于 f 的最小整数。
float f = 1.3f;
int i = (int)f;
print(i);//1
print(Mathf.CeilToInt(f));//2
print(Mathf.CeilToInt(1.00001f));//2
FloorToInt静态方法 向下取整
// 返回小于或等于 f 的最大整数。
print(Mathf.FloorToInt(9.6f));//9
Clamp静态方法 钳制函数
// 在给定的最小浮点值和最大浮点值之间钳制给定值。如果在最小和最大范围内,则返回给定值。
// 比最小还小,就取最小,比最大还大,就取最大,两者之间,就取本身
print(Mathf.Clamp(10, 11, 20));//11
print(Mathf.Clamp(21, 11, 20));//20
print(Mathf.Clamp(15, 11, 20));//15
Max静态方法 获取最大值
// 返回两个或更多值中的最大值。
// 内部有一个可变长的参数
print(Mathf.Max(1, 2, 3, 4, 5, 6, 7, 8));//8
print(Mathf.Max(1, 2));//2
Min静态方法 获取最小值
// 返回两个或更多值中的最小值。
// 内部有一个可变长的参数
print(Mathf.Min(1, 2, 3, 4, 545, 6, 1123, 123));//1
print(Mathf.Min(1.1f, 0.4f));//0.4
Pow静态方法 一个数的n次幂
// 返回 f 的 p 次幂。
print("一个数的n次方" + Mathf.Pow(4, 2));//16
print("一个数的n次方" + Mathf.Pow(2, 3));//8
RoundToInt静态方法 四舍五入
// 返回舍入为最近整数的 / f /。
print("四舍五入" + Mathf.RoundToInt(1.3f));//1
print("四舍五入" + Mathf.RoundToInt(1.5f));//5
Sqrt静态方法 返回一个数的平方根
// 返回 f 的平方根。
print("返回一个数的平方根" + Mathf.Sqrt(4));//2
print("返回一个数的平方根" + Mathf.Sqrt(16));//4
print("返回一个数的平方根" + Mathf.Sqrt(64));//8
IsPowerOfTwo静态方法 判断一个数是否是2的n次方
// 如果值是 2 的幂,则返回 true。
print("判断一个数是否是2的n次方" + Mathf.IsPowerOfTwo(4));//true
print("判断一个数是否是2的n次方" + Mathf.IsPowerOfTwo(8));//true
print("判断一个数是否是2的n次方" + Mathf.IsPowerOfTwo(3));//false
print("判断一个数是否是2的n次方" + Mathf.IsPowerOfTwo(1));//true
Sign静态方法 判断正负数
// 返回 f 的符号。
// 正数和0就返回1,负数就返回-1
print("判断正负数" + Mathf.Sign(0));//1
print("判断正负数" + Mathf.Sign(10));//1
print("判断正负数" + Mathf.Sign(-10));//-1
print("判断正负数" + Mathf.Sign(3));//1
print("判断正负数" + Mathf.Sign(-2));//-1
Mathf中的常用方法——一般不停计算
Lerp静态方法 插值运算
用于执行线性插值运算。线性插值是一种基本的数学运算,用于在两个值之间进行平滑的过渡。在游戏开发中,它通常用于在两个状态之间进行平滑过渡,例如在动画中对象的位置、旋转或颜色之间进行插值以实现平滑的动画效果。
public static float Lerp(float a, float b, float t);
a
:起始值。b
:目标值。t
:插值参数,通常介于0和1之间。当t
为0时,返回起始值a
;当t
为1时,返回目标值b
;当t
在0和1之间时,返回起始值a
和目标值b
之间的插值。
Lerp函数插值的计算方式是通过如下公式来实现的:
result = a + (b - a) * t
这个公式实际上是线性插值的定义:它从起始值a
开始,根据参数t
与目标值b
之间的差异,按比例插值到目标值。当t
为0时,插值结果等于起始值a
;当t
为1时,插值结果等于目标值b
;当t
在0和1之间时,插值结果在起始值a
和目标值b
之间进行平滑过渡。
插值运算用法一:先快后慢,无限接近终点
//每帧改变start的值——变化速度先快后慢,位置无限接近,但是不会得到end位置
//可以理解为 每一帧的时间不变 计算出来的下一帧的位置肯定会更大
//但是因为下一帧的位置更大 (end - start)就会变小 每一帧的位置移动相比起来就更小了
start = Mathf.Lerp(start, end, Time.deltaTime);
插值运算用法二:匀速接近终点
//每帧改变t的值——变化速度匀速,位置每帧接近,当t>=1时,得到结果
//当time大于1时,插值会继续进行,但是Mathf.Lerp方法返回的结果将始终等于end,因为此时参数t等于1,即线性插值的终点。
//因此,当time大于1时,result将保持等于end,不再发生变化。
//可以理解为 start和end都不变 只有t在每一帧都在累加一个恒定值
//比如第一帧t是0.1秒 第二帧就是0.2秒 第五帧t就是0.5秒了
//这样算出来没帧的结果就是接近线性的
time += Time.deltaTime;
result = Mathf.Lerp(start, end, time);
2.2 知识点代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Lesson02_3D数学_Mathf数学计算公共类 : MonoBehaviour
{
void Start()
{
#region 知识点一 Mathf和Math
//Math是C#中封装好的用于数学计算的工具类 —— 位于System命名空间中
//Mathf是Unity中封装好的用于数学计算的工具结构体 —— 位于UnityEngine命名空间中
//他们都是提供来用于进行数学相关计算的
#endregion
#region 知识点二 Mathf和Math的区别
//Mathf 和 Math中的相关方法几乎一样
//Math 是C#自带的工具类 主要就提供一些数学相关计算方法
//Mathf 是Unity专门封装的,不仅包含Math中的方法,还多了一些适用于游戏开发的方法
//所以我们在进行Unity游戏开发时
//使用Mathf中的方法用于数学计算即可
#endregion
#region 知识点三 Mathf中的常用方法——一般计算一次
//PI常量 代表圆周率π
//众所周知的“3.14159265358979...”值(只读)。
print(Mathf.PI);//3.141593
//Abs静态方法 取绝对值
//返回 f 的绝对值。
print(Mathf.Abs(-10));//10
print(Mathf.Abs(-20));//20
print(Mathf.Abs(1));//1
//CeilToInt静态方法 向上取整
//返回大于或等于 f 的最小整数。
float f = 1.3f;
int i = (int)f;
print(i);//1
print(Mathf.CeilToInt(f));//2
print(Mathf.CeilToInt(1.00001f));//2
//FloorToInt静态方法 向下取整
//返回小于或等于 f 的最大整数。
print(Mathf.FloorToInt(9.6f));//9
//Clamp静态方法 钳制函数
//在给定的最小浮点值和最大浮点值之间钳制给定值。如果在最小和最大范围内,则返回给定值。
//比最小还小,就取最小,比最大还大,就取最大,两者之间,就取本身
print(Mathf.Clamp(10, 11, 20));//11
print(Mathf.Clamp(21, 11, 20));//20
print(Mathf.Clamp(15, 11, 20));//15
//Max静态方法 获取最大值
//返回两个或更多值中的最大值。
//内部有一个可变长的参数
print(Mathf.Max(1, 2, 3, 4, 5, 6, 7, 8));//8
print(Mathf.Max(1, 2));//2
//Min静态方法 获取最小值
//返回两个或更多值中的最小值。
//内部有一个可变长的参数
print(Mathf.Min(1, 2, 3, 4, 545, 6, 1123, 123));//1
print(Mathf.Min(1.1f, 0.4f));//0.4
//Pow静态方法 一个数的n次幂
//返回 f 的 p 次幂。
print("一个数的n次方" + Mathf.Pow(4, 2));//16
print("一个数的n次方" + Mathf.Pow(2, 3));//8
//RoundToInt静态方法 四舍五入
//返回舍入为最近整数的 / f /。
print("四舍五入" + Mathf.RoundToInt(1.3f));//1
print("四舍五入" + Mathf.RoundToInt(1.5f));//5
//Sqrt静态方法 返回一个数的平方根 -
//返回 f 的平方根。
print("返回一个数的平方根" + Mathf.Sqrt(4));//2
print("返回一个数的平方根" + Mathf.Sqrt(16));//4
print("返回一个数的平方根" + Mathf.Sqrt(64));//8
//IsPowerOfTwo静态方法 判断一个数是否是2的n次方
//如果值是 2 的幂,则返回 true。
print("判断一个数是否是2的n次方" + Mathf.IsPowerOfTwo(4));//true
print("判断一个数是否是2的n次方" + Mathf.IsPowerOfTwo(8));//true
print("判断一个数是否是2的n次方" + Mathf.IsPowerOfTwo(3));//false
print("判断一个数是否是2的n次方" + Mathf.IsPowerOfTwo(1));//true
//Sign静态方法 判断正负数
//返回 f 的符号。
//正数和0就返回1,负数就返回-1
print("判断正负数" + Mathf.Sign(0));//1
print("判断正负数" + Mathf.Sign(10));//1
print("判断正负数" + Mathf.Sign(-10));//-1
print("判断正负数" + Mathf.Sign(3));//1
print("判断正负数" + Mathf.Sign(-2));//-1
#endregion
}
#region 知识点四 Mathf中的常用方法——一般不停计算
//插值运算外面定义的变量
//开始值 有时开始值直接拿来算后当做结果值
float start = 0;
//目标值
float end = 10;
//结果值
float result = 0;
//累加的时间
float time = 0;
#endregion
void Update()
{
#region 知识点四 Mathf中的常用方法——一般不停计算
//Lerp静态方法 插值运算
//用于执行线性插值运算。
//线性插值是一种基本的数学运算,用于在两个值之间进行平滑的过渡。
//在游戏开发中,它通常用于在两个状态之间进行平滑过渡,例如在动画中对象的位置、旋转或颜色之间进行插值以实现平滑的动画效果。
//public static float Lerp(float a, float b, float t);
//a:起始值。
//b:目标值。
//t:插值参数,通常介于0和1之间。当t为0时,返回起始值a;当t为1时,返回目标值b;当t在0和1之间时,返回起始值a和目标值b之间的插值。
//Lerp函数公式
//result = a + (b - a) * t
//这个公式实际上是线性插值的定义:
//它从起始值a开始,根据参数t与目标值b之间的差异,按比例插值到目标值。
//当t为0时,插值结果等于起始值a;当t为1时,插值结果等于目标值b;
//当t在0和1之间时,插值结果在起始值a和目标值b之间进行平滑过渡。
//一般写在Update内
//result = Mathf.Lerp(start, end, time);
////插值运算外面定义的变量
////开始值 有时开始值直接拿来算后当做结果值
//float start = 0;
////目标值
//float end = 10;
////结果值
//float result = 0;
////累加的时间
//float testNum = 0;
//result = start + (end - start)*time
//插值运算用法一
//每帧改变start的值——变化速度先快后慢,位置无限接近,但是不会得到end位置
//可以理解为 每一帧的时间不变 计算出来的下一帧的位置肯定会更大
//但是因为下一帧的位置更大 (end - start)就会变小 每一帧的位置移动相比起来就更小了
start = Mathf.Lerp(start, end, Time.deltaTime);
//插值运算用法二
//每帧改变t的值——变化速度匀速,位置每帧接近,当t>=1时,得到结果
//当time大于1时,插值会继续进行,但是Mathf.Lerp方法返回的结果将始终等于end,因为此时参数t等于1,即线性插值的终点。
//因此,当time大于1时,result将保持等于end,不再发生变化。
//可以理解为 start和end都不变 只有t在每一帧都在累加一个恒定值
//比如第一帧t是0.1秒 第二帧就是0.2秒 第五帧t就是0.5秒了
//这样算出来没帧的结果就是接近线性的
time += Time.deltaTime;
result = Mathf.Lerp(start, end, time);
#endregion
}
}
2.3 练习题
使用线性插值实现一个方块跟随另一个方块移动
场景设置
在场景中创建两个方块A和B,目的是让方块A跟随方块B移动。
脚本编写
创建一个脚本,将其挂载到方块A上。在脚本中添加必要的变量,包括要跟随的对象B、移动速度等。
// 使用线性插值实现方块A跟随方块B移动的脚本
//要跟随的对象B
public Transform B;
//移动速度
public float moveSpeed;
//位置中介
private Vector3 pos;
//B当前的位置
private Vector3 bNowPos;
//开始位置
private Vector3 startPos;
//匀速运动累加的时间
private float time;
//模式
public int mode = 1;
切换模式逻辑
使用GUI编写一个切换模式的逻辑。
private void OnGUI()
{
if(GUI.Button(new Rect(0, 0, 200, 200), "切换模式"))
{
if(mode == 1)
{
mode = 2;
}
else if(mode == 2)
{
mode = 1;
}
}
}
根据不同模式进行移动
在Update函数中根据不同的模式进行移动处理。
void Update()
{
if(mode == 1)
{
//第一种:先快后慢的形式
//记录当前这一帧A的位置
pos = this.transform.position;
//传入这一帧A的位置算出下一帧A的位置
pos.x = Mathf.Lerp(pos.x, B.position.x, Time.deltaTime * moveSpeed);
pos.y = Mathf.Lerp(pos.y, B.position.y, Time.deltaTime * moveSpeed);
pos.z = Mathf.Lerp(pos.z, B.position.z, Time.deltaTime * moveSpeed);
//算出下一帧A的位置赋值给A
this.transform.position = pos;
}
else if(mode == 2)
{
//第二种:匀速运动
//发现存储的B的位置不等于当前B的位置
if (bNowPos != B.transform.position)
{
//清空时间,重写设置B的位置,开始位置是A当前的位置
time = 0;
bNowPos = B.transform.position;
startPos = this.transform.position;
}
//时间每一帧运算累加
time += Time.deltaTime;
//startPos和bNowPos其实定死了,只有time在变化,传入算出下一帧A的位置
pos.x = Mathf.Lerp(startPos.x, bNowPos.x, time * moveSpeed);
pos.y = Mathf.Lerp(startPos.y, bNowPos.y, time * moveSpeed);
pos.z = Mathf.Lerp(startPos.z, bNowPos.z, time * moveSpeed);
//算出下一帧A的位置赋值给A
this.transform.position = pos;
}
}
移动B观察不同模式下A的跟随效果
在场景中移动B,观察不同模式下A的跟随效果。
2.4 练习题代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//这个脚本将要挂载到方块A上 实现方块A跟随着方块B
public class Lesson02_练习题 : MonoBehaviour
{
#region Lesson02 练习题一
//使用线性插值实现一个方块跟随另一个方块移动
//要跟随的对象B
public Transform B;
//移动速度
public float moveSpeed;
//位置中介
private Vector3 pos;
//B当前的位置
private Vector3 bNowPos;
//开始位置
private Vector3 startPos;
//匀速运动累加的时间
private float time;
//模式
public int mode = 1;
void Update()
{
if(mode == 1)
{
//第一种 就是 先快后慢的形式
//记录当前这一帧A的位置
pos = this.transform.position;
//传入这一帧A的位置算出下一帧A的位置
pos.x = Mathf.Lerp(pos.x, B.position.x, Time.deltaTime * moveSpeed);
pos.y = Mathf.Lerp(pos.y, B.position.y, Time.deltaTime * moveSpeed);
pos.z = Mathf.Lerp(pos.z, B.position.z, Time.deltaTime * moveSpeed);
//算出下一帧A的位置赋值给A
this.transform.position = pos;
}
else if(mode == 2)
{
//第二种 就是 匀速运动
//发现存储的B的位置不等于当前B的位置
if (bNowPos != B.transform.position)
{
//清空时间 重写设置B的位置 开始位置是A当前的位置
time = 0;
bNowPos = B.transform.position;
startPos = this.transform.position;
}
//时间每一帧运算累加
time += Time.deltaTime;
//startPos和bNowPos其实定死了 只有time在变化 传入算出下一帧A的位置
pos.x = Mathf.Lerp(startPos.x, bNowPos.x, time * moveSpeed);
pos.y = Mathf.Lerp(startPos.y, bNowPos.y, time * moveSpeed);
pos.z = Mathf.Lerp(startPos.z, bNowPos.z, time * moveSpeed);
//算出下一帧A的位置赋值给A
this.transform.position = pos;
}
}
private void OnGUI()
{
if(GUI.Button(new Rect(0, 0, 200, 200), "切换模式"))
{
if(mode == 1)
{
mode = 2;
}
else if(mode == 2)
{
mode = 1;
}
}
}
#endregion
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com