1、Unity【基础】3D数学

news2024/9/22 19:43:47

3D数学

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

文章目录

  • 3D数学
    • 1、数学计算公共类Mathf
      • 1、Mathf和Math
      • 2、区别
      • 3、Mathf中的常用方法(一般计算一次)
      • 4、Mathf中的常用方法(一般不停计算)
      • 练习 A物体跟随B物体移动
    • 2、三角函数
      • 1、角度和弧度
      • 2、三角函数
      • 3、反三角函数
      • 练习 物体曲线移动
    • 3、Unity中的坐标系
      • 1、世界坐标系
      • 2、物体坐标系
      • 3、屏幕坐标系
      • 4、视口坐标系
      • 5、坐标转换
    • 4、Vector3向量
      • 1、向量模长和单位向量
        • 1、向量基础
        • 2、两点决定 向量
        • 3、零向量和负向量
        • 4、向量模长
        • 5、单位向量
      • 2、向量加减乘除
        • 练习:向量实现摄像机跟随
      • 3、向量点乘
        • 1、点乘的计算
        • 2、点乘的几何意义
        • 3、公式推导
        • 4、通过点乘推导公式算出夹角
        • 练习 检测半径为5,角度为90°的扇形区域
      • 4、向量叉乘
        • 1、叉乘计算公式
        • 2、几何意义
        • 练习 物体方位
      • 5、差值运算
        • 1、线性插值
          • 两种线性插值代码实现
        • 2、球性插值
        • 思考1 Lerp摄像机跟随
        • 思考2 球形插值模拟太阳升降
    • 5、Quaternion四元数
      • 1、为何使用四元数
      • 2、四元数是什么
        • 1、四元数的构成
        • 2、Unity中的四元数
        • 3、四元数和欧拉角转换
        • 4、四元数弥补欧拉角的缺点
      • 3、四元数常用方法
        • 1、单位四元数
        • 2、插值运算
        • 3、向量方向 转换为对应 四元数角度
        • 思考1 写一个类似LookAt()的拓展方法
        • 思考2 用LookRotation实现摄像机跟随
      • 4、四元数计算
        • 1、四元数相乘
        • 2、四元数乘向量
        • 思考1 模拟飞机发射子弹,单发、双发、扇形、环形
        • 思考2 摄像机跟随效果

3D数学

1、数学计算公共类Mathf

1、Mathf和Math

Math是c#中封装好的用于【数学计算的工具类】位于system命名空间中
    public static class Math{}
Mathf是Unity中封装好的用于【数学计算的工具结构体】位于UnityEngine命名空间中
    public struct Mathf{}
他们都是提供来用于进行【数学相关计算】的

2、区别

一个是静态类,一个是结构体
Mathf比Math有更多的数学计算方法

3、Mathf中的常用方法(一般计算一次)

void Start()
{
    1、π PI
        Mathf.PI
    2、绝对值 Abs
    3、向上取整 CeilToInt
        print(Mathf.CeilToInt(1.3f)); //2
    4、向下取整 FloorToInt
    5、钳制函数 Clamp (在参数二,和参数三的范围里取值)
        print(Mathf.Clamp(10, 11, 22)); //11
        print(Mathf.Clamp(23, 11, 22)); //22
        print(Mathf.Clamp(16, 11, 22)); //16
    6、获取最大值 Max
    7、获取最小值 Min
    8、一个数的n次方 Pow
        print(Mathf.Pow(2, 3)); //2的3次方
    9、四舍五入 RoundToInt
    10、返回一个数的平方根 Sqrt
    11、判断一个数是否是2的n次方 IsPowerOfTwo
        print(Mathf.IsPowerOfTwo(1)); //true
    12、判断正负数 Sign
        print(Mathf.Sign(8)); //1
        print(Mathf.Sign(-8)); //-1
}

4、Mathf中的常用方法(一般不停计算)

float start = 0;
float result = 0;
float time = 0;
void Update()
{
插值运算 Lerp
    Lerp函数公式
        result = Mathf.Lerp(start,end,t);
    t为插值系数,取值范围为0~1,计算方法:
        result = start + (end - start) * t

    插值运算用法1
    每帧改变start的值:变化速度先快后慢,位置无限接近,但是不会得到end位置
		//此处相当于log函数
        start = Mathf.Lerp(start, 10, Time.deltaTime);
    
    插值运算用法2
    每帧改变t的值:变化速度匀速,每帧接近,当t>=1时,得到结果
        //此处相当于正比例函数
        time += Time.deltaTime;
        result = Mathf.Lerp(result, 10, time);
}

练习 A物体跟随B物体移动

using UnityEngine;

public class FollowCube : MonoBehaviour
{
    public Transform B;
    public float moveSpeed = 1;

    private Vector3 pos;
    private Vector3 bNowPos;
    private Vector3 startPos;
    private float time;
    void Update()
    {
        //先快后慢
        //pos = transform.position;
        //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);
        //transform.position = pos;

        //匀速运动
        if (bNowPos!= B.transform.position)
        {
            time = 0;
            bNowPos = B.transform.position;
            startPos= transform.position;
        }
        time += Time.deltaTime;
        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);
        transform.position = pos;
    }
}

2、三角函数

1、角度和弧度

1.角度和弧度
    角度:1°
    弧度:1 (radian)
    圆一周的弧度:2π (radian)

2.角度和弧度的关系
    π rad = 180°
    则:
    1 rad ≈ 57.3°
    1° ≈ 0.01745 rad
    
    角度 = 弧度 * 57.3
        float rad = 1;
        float angle = rad * Mathf.Rad2Deg;
    弧度 = 角度 * 0.01745
        angle = 1;
    	rad = angle * Mathf.Deg2Rad;

2、三角函数

Mathf中,只使用弧度,所以要角度转弧度(Deg2Rad)
sin30°
	Mathf.Sin(30 * Mathf.Deg2Rad);

3、反三角函数

通过反三角函数计算正弦值或余弦值对应的弧度值
    弧度 = Mathf.Asin(正弦值)
    弧度 = Mathf.Acos(余弦值)
        float radian = Mathf.Asin(0.5f);   //先将值转化为弧度
        float degree = radian * Mathf.Rad2Deg;   //再将弧度转角度
        print(degree);

练习 物体曲线移动

using UnityEngine;

public class SinMove : MonoBehaviour
{
    public float moveSpeed = 1; //前后移动
    public float changeSpeed = 2; //左右移动
    public float changeSize = 5; //左右幅度
    private float time = 0;
    void Update()
    {    
        time += Time.deltaTime * changeSpeed;
        //前后移动
        transform.Translate(Vector3.forward * moveSpeed * Time.deltaTime);
        //左右移动
        transform.Translate(Vector3.right * changeSize * Time.deltaTime * Mathf.Sin(time));
    }
}

3、Unity中的坐标系

1、世界坐标系

原点:世界的中心点
轴向:世界坐标系的三个轴向是固定的
        transform.position;
        transform.rotation;
        transform.eulerAngles; //欧拉角
        transform.lossyScale; //缩放

2、物体坐标系

原点:物体的中心点(建模时决定)
轴向:
    物体的方右为x轴正方向
    物体的上方为y轴正方向
    物体的前方为z轴正方向
    //相对父对象的坐标
        transform.localPosition;
        transform.localRotation;
        transform.localEulerAngles;
        transform.localScale;

3、屏幕坐标系

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

4、视口坐标系

原点:屏幕左下角
轴向:
	向右为x轴正方向
    向上为y轴正方向
特点:
    左下角(0,0)
    右上角(1,1)
和屏幕坐标类似,将坐标单位化
摄像机上的【视口范围】属于视口 Viewport Rect

5、坐标转换

世界转本地
    transform.InverseTransformDirection;
    transform.InverseTransformPoint;
    transform.InverseTransformVector;

本地转世界
    transform.TransformDirection;
    transform.TransformPoint;
    transform.TransformVector;

世界转屏幕
    Camera.main.WorldToScreenPoint;
屏幕转世界
	Camera.main.ScreenToWorldPoint;

世界转视口
	Camera.main.WorldToViewportPoint;
视口转世界
    Camera.main.ViewportToWorldPoint;

视口转屏幕
    Camera.main.ViewportToScreenPoint;
屏幕转视口
	Camera.main.ScreenToViewportPoint;

4、Vector3向量

1、向量模长和单位向量

1、向量基础
三维向量 Vector3
Vector3有两种意义:
    1、可以表示位置 代表一个点
    	transform.position;
    2、也可以表示方向 代表一个方向
    	transform.forward;
2、两点决定 向量
Vector3 A = new Vector3(1, 2, 3);
Vector3 B = new Vector3(2, 3, 4);
求向量 终点 - 起点
Vector3 AB = B - A;
Vector3 BA = A - B;
3、零向量和负向量
零向量
	(0,0,0)
    Vector3.zero;
负向量
	(x,y,z)的负向量为(-x,-y,-z)
    Vector3.forward;
	-Vector3.forward;
4、向量模长
模长:向量的长度
    AB.magnitude; //AB的模长

    Vector3 C = new Vector3(3, 4, 5);
    print(C.magnitude); //点C到原点的模长

	Vector3.Distance(A, B); //两点之间的距离
5、单位向量
模长为1的向量
	只管方向
	AB.normalized;
	AB / AB.magnitude;

2、向量加减乘除

1、向量加法:
    位置+位置:Unity无意义
    向量+向量:首位相连
    位置+向量:平移位置
    transform.Translate(Vector3.forward * 5);
    //transform.position += new Vector3(1, 2, 3);
	
2、向量减法:
	位置-位置:向量
    向量-向量:新向量 (A-B=B向量头指向A向量头)
    位置-向量:平移位置
    transform.Translate(-Vector3.forward * 5);
    //transform.position -= new Vector3(1, 2, 3);
	
3、向量的乘除
    向量 */ 标量 = 缩放向量的模长
    transform.localScale *= 2;
	transform.localScale /= 2;
	//LossyScale只能得,不能改
练习:向量实现摄像机跟随
public float z = 4;
public float y = 7;
public Transform target;

void LateUpdate()
{
    //摄像机移动在LateUpdate中
    transform.position = target.position + -target.forward * z + target.up * y;
    transform.LookAt(target);
}

3、向量点乘

1、点乘的计算
向量A(Xa,Ya,Za)
向量B(Xb,Yb,Zb)
    A*B = Xa*Xb + Ya*Yb + Za*Zb
向量 * 向量 = 标量
2、点乘的几何意义
点乘可以得到向量的投影长度
点乘结果>0,两个向量的夹角为锐角 (目标在前)
点乘结果=0,两个向量的夹角为直角 (目标在左or右两侧不确定)
点乘结果<0,两个向量的夹角为钝角 (目标在后)
作用:判断目标的大致方位

unity点乘

调试画线
    //线段 参数(起点,终点)
    Debug.DrawLine(transform.position, transform.position + transform.forward * 3, Color.red);
    //射线 参数(起点,方向)
    Debug.DrawRay(transform.position,transform.right, Color.green);
通过点乘判断对象方位
    //得到两个向量的点乘结果
    Debug.DrawRay(transform.position, transform.forward * 10);
	Debug.DrawRay(transform.position, target.position - transform.position);

    float a = Vector3.Dot(transform.forward, target.position - transform.position);
    if (a >= 0)
    {
        print("目标在前方");
    }
    else
    {
        print("目标在后方");
    }
    
3、公式推导
已知单位模长A 单位模长B,以及点乘的几何意义;
则:
    cosβ = 单位向量A * 单位向量B
再根据数学的反三角函数推出:
    β = Mathf.Acos(A*B) * Mathf.Rad2Deg
4、通过点乘推导公式算出夹角
//单位向量算出点乘结果(方向向量)
float dotResult = Vector3.Dot(transform.forward, (target.position - transform.position).normalized);
//用Unity反三角函数算出角度
print(Mathf.Acos(dotResult) * Mathf.Rad2Deg);

最简单直接的方法:
//Unity只需两个向量算出夹角的方法
float ang = Vector3.Angle(transform.forward, target.position - transform.position);
print("角度" + ang);
练习 检测半径为5,角度为90°的扇形区域
方法一:
public Transform B;
void Update()
{
    float c = Vector3.Distance(transform.position, B.position);
    if (c <= 5)
    {
        float dotResult = Vector3.Dot(transform.forward, (B.position - transform.position).normalized);
        //Debug.DrawRay(transform.position, (transform.forward + Vector3.right)*5);
        if ((Mathf.Acos(dotResult) * Mathf.Rad2Deg) <= 45f)
        {
            print("发现目标");
            print("与目标距离" + c + "米");
        }
    }
}
方法二: 用Angle
public Transform B;
void Update()
{
    float c = Vector3.Distance(transform.position, B.position);
    if (c <= 5 && Vector3.Angle(transform.position, B.position - transform.position) <= 45)
    {
        print("发现目标");
        print("与目标距离" + c + "米");
    }
}

4、向量叉乘

1、叉乘计算公式
向量 * 向量 = 向量
    向量A(Xa,Ya,Za)
    向量B(Xb,Yb,Zb)
    A*B=(X,Y,Z)
    X = Ya*Zb - Za*Yb
    Y = Za*Xb - Xa*Zb
    Z = Xa*Yb - Ya*Xb
    
    Vector3.Cross();
2、几何意义
A*B得到的向量同时垂直A和B的法向量
A*B = - B*A
    
若A、B向量在同一平面上,A*B(y为法向量):
y>=0;则B在A的右侧
y<0;则B在A的左侧
    
    总结:根据向量叉乘的顺序决定左右位置
        Vector3 cross = Vector3.Cross(A.position, B.position);
        if (cross.y > 0)
        {
            print("B在A的右侧");
        }
        else
        {
            print("B在A的左侧");
        }
练习 物体方位
1//判断物体B相对于物体A的位置,左上,左下,右上,右下方位
public Transform A;
public Transform B;

//点乘判断前后
private float dotResult;
//叉乘判断左右
private float crossResult;

private float distance; //距离
private float angle; //角度

void Update()
{
    dotResult = Vector3.Dot(A.forward, B.position - A.position);
    crossResult = Vector3.Cross(A.forward, B.position - A.position).y;
    
    //点乘判断前后
    if (dotResult >= 0)
    {//前
        print(crossResult >= 0 ? "B在右上方" : "B在左上方");
    }
    else
    {//后
        print(crossResult >= 0 ? "B在右后方" : "B在左后方");
    }

    2//判断一个物体在左前方20度角or右前方30度范围内,且在距离5米内
    distance = Vector3.Distance(A.position, B.position);
    angle = Vector3.Angle(A.forward, B.position - A.position);
    if (distance <= 5)
    {
        if (crossResult >= 0 && angle <= 30)
        {
            print("发现目标!!!");
            print("目标位于右前方" + decimalPoint2(angle) + "°," + decimalPoint2(distance) + "米处");
        }
        else if (crossResult < 0 && angle <= 20)
        {
            print("发现目标!!!");
            print("目标位于左前方" + decimalPoint2(angle) + "°," + decimalPoint2(distance) + "米处");
        }
    }
}

//小数点保留后两位
private float decimalPoint2(float f)
{
    int i = (int)(f * 100);
    return i * 0.01f;
}

5、差值运算

1、线性插值
Vector3.Lerp(start, end, t);
对两个点进行插值计算
t的取值范围为0~1
    计算公式:result = start + (end - start) * t
    
应用
    1、每帧改变start的值(先快后慢)
    2、每帧改变t的值(匀速)
两种线性插值代码实现
public Transform A;
public Transform B;
public Transform target;

private Vector3 startPos;
private float time;
private Vector3 nowTarget;

void Start()
{
    startPos = B.position;
}

void Update()
{
    //每帧改变start的值(先快后慢)
    A.position = Vector3.Lerp(A.position, target.position, Time.deltaTime);

    //每帧改变t的值(匀速)
    if (nowTarget != target.position)
    {
        nowTarget = target.position;
        startPos = B.position;
        time = 0;
    }
    time += Time.deltaTime;
    B.position = Vector3.Lerp(startPos, nowTarget, time);
}
2、球性插值
Vector3.slerp(start, end, t);
对两个向量进行插值运算 t:(0~1)
//运动轨迹为弧形
C.position = Vector3.Slerp(transform.forward * 10, -transform.forward * 3, time);
思考1 Lerp摄像机跟随
1.先快后慢
public float z = 4;
public float y = 7;
public Transform target;

public Vector3 targetPos;
public int speedMove = 1;

void LateUpdate()
{
    //目标位置
    if (targetPos != target.position + -target.forward * z + target.up * y)
    {
        targetPos = target.position + -target.forward * z + target.up * y;
    }
    transform.position = Vector3.Lerp(transform.position, targetPos, Time.deltaTime * speedMove);
    transform.LookAt(target);
}
2、匀速跟随
public float z = 4;
public float y = 7;
public Transform target;

public Vector3 targetPos;
public int speedMove = 1;
private Vector3 startPos;
private float time;
void LateUpdate()
{
    //目标位置
    if (targetPos != target.position + -target.forward * z + target.up * y)
    {
        targetPos = target.position + -target.forward * z + target.up * y;
        startPos = transform.position;
        time = 0;
    }
    time += Time.deltaTime;
    transform.position = Vector3.Lerp(startPos, targetPos, time*speedMove);
    transform.LookAt(target);
}
思考2 球形插值模拟太阳升降
public Transform C;
private float time;
void Update()
{
    time += Time.deltaTime;
    C.position = Vector3.Slerp(Vector3.right*10 + Vector3.up * 0.1f, Vector3.left*10, time*0.1f);
}

5、Quaternion四元数

1、为何使用四元数

Rotation表示欧拉角 transform.eulerAngles
因为欧拉角存在一些缺点:
1、同一旋转的表示不唯一
2、万向节死锁
而四元数旋转不存在万向节死锁问题
所以使用四元数来表示三维空间中的旋转信息

2、四元数是什么

1、四元数的构成
四元数包含一个标量和一个3D向量
[w,v] w为标量,v为3D向量
[w,(x,y,z)]
四元数表示3D空间中的一个旋转量

轴-角对(含义):
	绕一个轴(Q)旋转β度
2、Unity中的四元数
Unity中的四元数:Quaternion
轴角对初始化
1、基本写法(很少用):
    公式:
    四元数Q = [cos(β/2),sin(β/2)*x,sin(β/2)*y,sin(β/2)*z]
    简化记忆:Q = c,s(x,y,z)  β/2
    //(1,0,0)旋转60度
	Quaternion q = new Quaternion(Mathf.Cos(60 / 2 * Mathf.Deg2Rad), 0, 0, Mathf.Sin(60 / 2 * Mathf.Deg2Rad) * 1);

2、Unity简单写法
    依据公式:
		四元数Q = Quaternion.AngleAxis(角度,);
	Unity代码:
		Quaternion q = Quaternion.AngleAxis(60,Vector3.right);

	//将四元数赋值给一个物体
	GameObject obj = GameObject.CreatePrimitive(PrimitiveType.Cube);
	obj.transform.rotation = q;
3、四元数和欧拉角转换
1、欧拉角转四元数
Quaternion q = Quaternion.Euler(60,0,0);
2、四元数转欧拉角
q.eulerAngles;
4、四元数弥补欧拉角的缺点
四元数相乘:旋转四元数
1、四元数的结果始终是 -180~180
2、四元数旋转可以避免万向节死锁

3、四元数常用方法

1、单位四元数
没有旋转量(角位移)
Unity:
	Quaternion.identity
例:
    [1,(0,0,0)]
    [-1,(0,0,0)]
作用:
    对象角度初始化
2、插值运算
// Slerp比Lerp效果好
public Transform A;
public Transform B;
public Transform C;

private Quaternion start;
private float time;
void Start()
{
    start = B.rotation;
}

void Update()
{
    //先快后慢,无线接近
    A.rotation = Quaternion.Slerp(A.rotation, C.rotation, Time.deltaTime);

    //匀速变化 time>=1则到达目标
    time += Time.deltaTime;
    B.rotation = Quaternion.Slerp(start, C.rotation, time);
}
3、向量方向 转换为对应 四元数角度
API:Quaternion.LookRotation(目标向量)

public Transform LA;
public Transform LB;
void Update()
{
    LA.rotation = Quaternion.LookRotation(LB.position - LA.position);
}
思考1 写一个类似LookAt()的拓展方法
public static class Tool
{
    public static void MyLookAt(this Transform obj,Transform target)
    {
        obj.rotation = Quaternion.LookRotation(target.position - obj.position);
    }
}
思考2 用LookRotation实现摄像机跟随
public float z = 4;
public float y = 7;
public Transform target;
public Vector3 targetPos;

public int speedMove = 1;
private Vector3 startPos;
private float time;

private Quaternion targetQua;

private Quaternion startQua;
private float roundTime;
void LateUpdate()
{
    //目标位置
    if (targetPos != target.position + -target.forward * z + target.up * y)
    {
        targetPos = target.position + -target.forward * z + target.up * y;
        startPos = transform.position;
        time = 0;
    }
    time += Time.deltaTime;
    transform.position = Vector3.Lerp(startPos, targetPos, time * speedMove);
    //transform.LookAt(target);
    //1、先快后慢
    //targetQua = Quaternion.LookRotation(target.position - transform.position);
    //transform.rotation = Quaternion.Slerp(transform.rotation, targetQua, Time.deltaTime);
    //2、匀速跟随
    if (targetQua != Quaternion.LookRotation(target.position - transform.position))
    {
        targetQua = Quaternion.LookRotation(target.position - transform.position);
        startQua = transform.rotation;
        roundTime = 0;
    }
    roundTime += Time.deltaTime;
    transform.rotation = Quaternion.Slerp(startQua, targetQua, roundTime);
}

4、四元数计算

1、四元数相乘
q1 = q2 * q3
两个旋转量的叠加
旋转方向一直是本地坐标系

Quaternion qua = Quaternion.AngleAxis(30, Vector3.up);
transform.rotation *= qua; //沿y轴顺时针旋转30度
transform.rotation *= qua; //再旋转30度
2、四元数乘向量
V1 = v2 * q
旋转【四元数角度】向量
    
Vector3 v = Vector3.forward;
v = Quaternion.AngleAxis(45, Vector3.up) * v; //此处必须:先四元数,再乘向量
思考1 模拟飞机发射子弹,单发、双发、扇形、环形

AirPlane 飞机类

using UnityEngine;

public enum E_FireType
{
    SingleShoot,
    DoubleShoot,
    SectorShoot,
    AnnularShoot
}
public class AirPlane : MonoBehaviour
{
    public GameObject bullet;
    public int bulletNum = 4;
    private E_FireType nowType = E_FireType.SingleShoot;

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Alpha1))
        {
            print("切换为单发");
            nowType = E_FireType.SingleShoot;
        }
        if (Input.GetKeyDown(KeyCode.Alpha2))
        {
            print("切换为双发");
            nowType = E_FireType.DoubleShoot;
        }
        if (Input.GetKeyDown(KeyCode.Alpha3))
        {
            print("切换为扇形子弹");
            nowType = E_FireType.SectorShoot;
        }
        if (Input.GetKeyDown(KeyCode.Alpha4))
        {
            print("切换为环形子弹");
            nowType = E_FireType.AnnularShoot;
        }
        if (Input.GetKeyDown(KeyCode.Space))
        {
            print("开火");
            Fire();
        }

    }

    private void Fire()
    {
        switch (nowType)
        {
            case E_FireType.SingleShoot:
                Instantiate(bullet,transform.position,transform.rotation);
                break;
            case E_FireType.DoubleShoot:
                Instantiate(bullet, transform.position - transform.right * 0.5f, transform.rotation);
                Instantiate(bullet, transform.position + transform.right * 0.5f, transform.rotation);
                break;
            case E_FireType.SectorShoot:
                Instantiate(bullet, transform.position, transform.rotation);
                Instantiate(bullet, transform.position, transform.rotation * Quaternion.AngleAxis(-15, Vector3.up));
                Instantiate(bullet, transform.position, transform.rotation * Quaternion.AngleAxis(15, Vector3.up));

                break;
            case E_FireType.AnnularShoot:
                float angle = 360 / bulletNum;
                for (int i = 0; i < bulletNum; i++)
                    Instantiate(bullet,transform.position,transform.rotation*Quaternion.AngleAxis(i* angle, Vector3.up));
                break;
        }
    }
}

Bullet 子弹类

using UnityEngine;

public class Bullet : MonoBehaviour
{
    public float moveSpeed = 10;
    private void Start()
    {
        Destroy(gameObject, 5);
    }
    void Update()
    {
        transform.Translate(Vector3.forward * Time.deltaTime * moveSpeed);
    }
}
思考2 摄像机跟随效果

1、摄像机看向人物头顶上方一个位置(可调节)

2、摄像机在任务斜后方,通过角度控制斜率

3、通过鼠标滚轮可以控制摄像机与人物的距离(有最大最小限制)

4、Quaternion.Slerp实现摄像机看向人物

5、Vector3.Lerp实现相机跟随人物

using UnityEngine;

public class CameraMovePlus : MonoBehaviour
{
    //摄像目标
    public Transform target;
    //头顶偏移位置
    public float overHeadOffset = 1;
    //倾斜角度
    public float tiltAngle = 45;
    //摄像机到头顶偏移位置的距离
    public float distance = 5;
    public float minDis = 3;
    public float maxDis = 8;
    public int scrollSpeed = 2;
    public int lookSpeed = 1;
    Vector3 nowPos;
    Vector3 nowDir;
    void Start()
    {
        //transform.rotation *= Quaternion.AngleAxis(45, Vector3.right);
    }

    void Update()
    {
        //3、通过鼠标滚轮可以控制摄像机与人物的距离(有最大最小限制)
        //滚轮
        distance += Input.GetAxis("Mouse ScrollWheel") * scrollSpeed;
        distance = Mathf.Clamp(distance, minDis, maxDis);
        //1、摄像机看向人物头顶上方一个位置(可调节)
        //偏移
        nowPos = target.position + target.up * overHeadOffset;
        //2、摄像机在任务斜后方,通过角度控制斜率
        nowDir = Quaternion.AngleAxis(tiltAngle, target.right) * -target.forward;
        nowPos = nowPos + nowDir * distance;
        //transform.position = nowPos;
        //Debug.DrawLine(transform.position, target.position + target.up * overHeadOffset);
        //摄像机方向
        //4、Quaternion.Slerp实现摄像机看向人物
        transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(-nowDir), Time.deltaTime * lookSpeed * 2);
        //5、Vector3.Lerp实现相机跟随人物
        transform.position = Vector3.Lerp(transform.position, nowPos, Time.deltaTime);
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2036959.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

三、虚拟机安装CentOS 7

编写目的&#xff1a; 1.做个记录&#xff0c;防止参考的博客被删 2.做个基础的虚拟机镜像&#xff0c;无应用&#xff0c;固定ip&#xff0c;能联网即可。后面直接克隆一个镜像&#xff0c;安装Jenkins等 目录 一、下载CentOS镜像 二、vmware配置CentOS虚拟机 1.打开vmw…

MSF经典attack示范

免责声明:本文仅做分享... 目录 Nessus官网 指定攻击 自动在线攻击 nmap扫 查看扫到的服务 查看主机 离线攻击 Msfvenom 本地快速传递文件的方法-->py 哈希传递攻击 提权 后渗透阶段 1-收集操作系统和版本信息 2-主机名称&环境变量 3-用户账户信息 4-远…

Stable Diffusion绘画 | ControlNet应用-NormalMap(法线贴图)

NormalMap(法线贴图)&#xff0c;指的是一张只有红绿蓝RGB颜色的图片&#xff0c;通过指定的发法线算法&#xff0c;用RGB颜色相互混合搭配来表示物体的凹凸情况&#xff0c;常用于3D贴图模型使用。 整体配置如下&#xff1a; ControlNet 的法线控制可以把任何的图片转化为法线…

Java Web —— 第四天(HTTP协议,Tomcat)

HTTP-概述 概念:Hyper Text Transfer Protocol&#xff0c;超文本传输协议&#xff0c;规定了浏览器和服务器之间数据传输的规则 特点: 1. 基于TCP协议:面向连接&#xff0c;安全 2.基于请求-响应模型的:一次请求对应一次响应 3. HTTP协议是无状态的协议: 对于事务处理没有…

书生浦语大模型全链路开源开放体系学习

书生浦语大模型的开源体系经过一年努力&#xff0c;已实现从数据收集到模型部署的全链路打通。课程介绍了书生浦语的最新进展&#xff0c;包括7B和20B模型的开源及其性能提升。新版本模型在推理能力和上下文处理上表现优异&#xff0c;支持超长上下文和复杂任务解决。开源工具涵…

Java并发类API--Executor与ThreadPoolExecutor

在 Java 中&#xff0c;Executor 和 ThreadPoolExecutor 是用于管理线程和执行任务的工具&#xff0c;帮助开发者更好地控制多线程环境。下面是它们的使用和区别。 1.Executor 简介 Executor 是一个接口&#xff0c;用来创建线程池&#xff0c;它定义了一个 execute(Runnable c…

05 内部类API异常

1.内部类 1.1形参和返回值 1. 类名作为形参和返回值 方法的形参是类名,需要该类的对象 方法的返回值为类名,则返回该类的对象 2.抽象类名作为形参和返回值 方法的形参是抽象类名,需要该类的子类对象 方法的返回值是抽象类名,需要返回是该类的子类对象 3.接口名作为形参和返回值…

打印机维护好帮手 | 闪克打印机修复助手 v2.23.0.0

闪克打印机修复助手是一款专业的打印机修复软件&#xff0c;专为解决打印机相关问题而设计&#xff0c;能够帮助用户轻松识别和处理打印机设备的问题&#xff0c;从而提升工作效率。闪克打印机修复助手集成了错误修复、打印机驱动安装和综合修复三大功能。 它能够安全、快速、高…

C盘磁盘空间不足:VirtualBox的锅

文章目录 一&#xff0c;数据迁移二&#xff0c;修改默认配置三&#xff0c;在VirtualBox上删除原来的虚拟机四&#xff0c;在新的虚拟机目录下五&#xff0c;删除C盘上的虚拟机文件虚拟机消耗磁盘分析 开始在自己的windows电脑上使用Vitualbox虚拟机之后&#xff0c;发现C盘常…

【走迷宫】

题目 DFS代码 #include<bits/stdc.h> using namespace std; const int N 110; int matrix[N][N]; int n, m; int dx[4] {-1, 0, 1, 0}, dy[4] {0, 1, 0, -1}; int dis[N][N]; void dfs(int x, int y, int cnt) {if(cnt > dis[n-1][m-1]) return;if(x n-1 &&a…

Vue2.0 项目实战篇\部署篇

Vue2.0 项目实战篇\部署篇&#x1f4dc; 本篇文章学习记录于&#xff1a; bilibili-黑马程序♞ 104-139集 》》&#x1f3af;目标&#xff1a;冲击前后端全栈&#x1f525;&#xff0c;分享一下学过程&#xff1a; Java.慈祥的博客——个人前端技术栈blog记录&#xff1a;、感…

list使用及底层模拟实现

目录 一.list的使用 排序sort 去重unique remove按值删除 remove_if splice merge合并 二.模拟实现 1.成员变量及节点的实现 2.普通迭代器 成员变量 解引用operator* operator-> 前置和前置-- 后置和后置-- 等于与不等于重载 begin()迭代器 end()迭代器 3.co…

【论文阅读】YOLOv10: Real-Time End-to-End Object Detection

题目&#xff1a;YOLOv10: Real-Time End-to-End Object Detection 作者&#xff1a;Ao Wang Hui Chen∗ Lihao Liu Kai Chen Zijia Lin Jungong Han Guiguang Ding∗ 清华大学的 motivation: 作者觉得YOLO系列的NMS和某些结构非常的耗时&#xff0c;提出NMS-free和一些列高效…

AI大模型赋能游戏:更智能、更个性化的NPC

参考论文&#xff1a;https://arxiv.org/abs/2403.10249 在传统游戏中&#xff0c;NPC&#xff08;非玩家角色&#xff09;的行为往往是预先设定好的&#xff0c;缺乏灵活性和变化性。然而&#xff0c;基于大模型的NPC可以利用其强大的推理和学习能力&#xff0c;实时生成对话…

使用 JUnit 5 进行 Java 单元测试:最佳实践和技术说明

掌握 Java 单元测试&#xff1a;深入了解工具、最佳实践和技术&#xff0c;以确保代码的稳健性。增强软件可靠性并完美交付&#xff01; 想要提升 Java 开发工作量&#xff1f;本指南探索 Java 测试领域&#xff0c;涵盖基础概念和高级技术。您将了解测试驱动开发 (TDD) 的重要…

Wireshark分析工具

简单用例 首先打开软件,左上角点文件,选中要分析的文件列表。 导入用tcpdump抓的包后进行分析,这里要输入过滤条件,对网络包进行一定的过滤处理。(这里172网段是阿里云的地址,用自己写的python2脚本对阿里云进行压测。) 这里输入过滤条件 tcp.port == 80 ,语法含义是…

快速把文件名统计到excel表的方法

文件名统计到EXCEL表&#xff0c;这似乎很多人都没听说过&#xff0c;因为它与EXCEL表格不沾边&#xff0c;那么这个需求如何实现&#xff0c;用到什么方法&#xff0c;今天给大家介绍一个比较实用的方法&#xff0c;它可以把文件名或文件夹的名快速提取并统计到EXCEL表格上去。…

C++ | Leetcode C++题解之第334题递增的三元子序列

题目&#xff1a; 题解&#xff1a; class Solution { public:bool increasingTriplet(vector<int>& nums) {int n nums.size();if (n < 3) {return false;}int first nums[0], second INT_MAX;for (int i 1; i < n; i) {int num nums[i];if (num > s…

Unity 资源分享 之 恐龙Ceratosaurus资源模型携 82 个动画来袭

Unity 资源分享 之 恐龙Ceratosaurus资源模型携 82 个动画来袭 一、前言二&#xff0c;资源包内容三、免费获取资源包 一、前言 亲爱的 Unity 开发者和爱好者们&#xff0c;大家好&#xff01;今天要为大家分享一份超级酷炫的 Unity 资源——恐龙资源模型&#xff0c;而且它还…

HBase snapshot+replication 测试

一、背景 画像标签服务&#xff08;CDP&#xff09;是核心服务&#xff0c;被公司其他系统如现金、电商、风控等核心业务调用。异常的话&#xff0c;影响范围大。 二、目标 存量数据测试通过 snapshot 迁移。增量数据测试通过 replication 同步。 三、测试 方案二测试&#x…