Unity3D:向量运算

向量的几何意义

几何意义上说,向量是有大小和方向的有向线段。
在这里插入图片描述

点和向量的关系

“点”有位置,但没有大小和方向,“向量”有大小和方向,但没有位置。所以使用“点”和“向量”的目的完全不同。”点”描述位置,“向量”描述位移。

零向量

零向量非常特殊,因为它是唯一大小为零且没有方向的向量。

负向量

几何解释:向量变负,将得到一个和向量大小相等,方向相反的向量。

向量大小

向量的大小就是向量各分量平方和的平方根
设三维向量A(x,y,z),则A向量的大小为:|A|=√(x^2 +y^2 +z^2)
二维向量同理
Unity中代码实现

Vector3 pos;//向量
// 开平方计算
Mathf.Sqrt(Mathf.Pow(pos.x, 2) + Mathf.Pow(pos.y, 2) + Mathf.Pow(pos.z, 2));
// UNITY现成的API计算
pos.magnitude;

标量与向量的乘法

标量与向量不能相加,但它们可以相乘。结果将得到一个向量。与原向量平行,但长度不同或者方向相反。
同理标量与向量也能相除

标准化向量

对于许多向量,我们只关心向量的方向不在乎向量的大小,如:“我面向的是什么方向?”,在这样的情况下,使用单位向量非常方便,单位向量就是大小为1的向量,单位向量经常也被称作为标准化向量或者法线。
要标准化向量,将向量除以它的大小(模)即可。
Unity中代码实现

Vector3 pos;//向量
// UNITY现成的API计算
pos.normalized;

向量的加法和减法

两个向量的维数相同,那么它们能相加,或者相减。结果向量的维数与原向量相同。向量加减法的记发和标量加减法的记法相同。例如:[x,y,z] + [a,b,c] = [x+a,y+b,z+c]

图像表示

在这里插入图片描述

向量点乘

向量点乘就是对应分量乘积的和。其结果是一个标量. [x,y,z] · [a,b,c] = ax+by+cz;
几何解释:
一般来说,点乘结果描述了两个向量的“相似”程度,点乘结果越大,两个向量越相近,也就是两个向量之间的夹角越小
通过反余弦函数 Acos 可以将点积的结果转换为两向量之间的夹角 θ 的角度,取值范围 [0, 180)
案例代码

void Start()
    {
        Vector3 up = Vector3.up;
        Vector3 right = Vector3.right;
        float dot = Vector3.Dot(up, right);
        Debug.Log("点积结果:" + dot);
        float rad = Mathf.Acos(dot);
        Debug.Log("反余弦函数获取到的弧度值:" + rad);
        float angle = Mathf.Rad2Deg * rad;
        Debug.Log("弧度值转换后的角度值:" + angle);
    }

在这里插入图片描述

点积示例

判断物体的前后关系

using UnityEngine;

public class Test : MonoBehaviour
{
    public Transform Cube1;
    public Transform Cube2;
    
    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            Vector3 direction = Cube2.position - Cube1.position;
            float dot = Vector3.Dot(direction.normalized, Cube1.forward);
            if (dot > 0)
            {
                Debug.Log("Cube2在Cube1的前方");
            }
            else if (dot < 0)
            {
                Debug.Log("Cube2在Cube1后方");
            }
            else
            {
                Debug.Log("Cube2和Cube1在相同的位置");
            }
        }
    }
}

判断敌人是否在技能的攻击范围之内

using UnityEngine;

public class Test : MonoBehaviour
{
    public Transform Player;//玩家
    public Transform Enemy;//敌人
    public float AttackDistance = 2;//攻击距离
    public float AttackRange = 90;//攻击角度

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            if(Vector3.Distance(Player.position,Enemy.position) <= AttackDistance)
            {
                //在攻击距离内,继续判断是否在攻击范围内
                float dot = Vector3.Dot((Enemy.position - Player.position).normalized, Player.forward);
                float angle = Mathf.Acos(dot) * Mathf.Rad2Deg;
                Debug.Log("玩家和敌人之间的夹角:" + angle);
                if(angle <= AttackRange)
                {
                    Debug.Log("敌人在玩家的攻击范围内,可以进行攻击");
                }
                else
                {
                    Debug.Log("敌人在玩家的攻击范围外");
                }
            }
            else
            {
                Debug.Log("敌人在玩家的攻击距离外");
            }
        }
    }
}

向量叉乘

向量叉乘得到一个向量,并且不满足交换律。 它满足反交换律 a × b = -(b × a)
叉乘公式:[x,y,z] × [a,b,c] = [yc-zb , za-xc , xb-ya]
几何解释:叉乘得到的向量垂直于原来的两个向量。
在这里插入图片描述
上图中向量a 和向量b 在一个平面上,向量c 垂直该平面,向量c 垂直于a和b
向量c 的长度 等于 向量a、向量b 模与夹角 sin值的积如下
c=axb = |a||b|sinθ
通过 |a||b|sinθ公式也可以推算出|axb|等于以a和b为两边的平行四边形面积
h = |a|sinθ
平行四边形面积=|b|h = |b|(|a|sinθ)
平行四边形面积=|b|h = |a||b|sinθ
也就是说a、b向量的叉积结果等于平行四边形的面积
在这里插入图片描述
那么垂直于平面的向量有两个,如何确认叉乘结果向量的朝向呢
通过计算向量c在坐标轴正方向 normal = (1, 1, 1).normalized 单位向量上的投影长度,判断向量a 到向量b是顺时针还是逆时针
令 value = c· normal = (y1z2 - z1y2, z1x2 - x1z2, x1y2 - y1x2) · (1, 1, 1).normalized
在右手坐标系中
当 value > 0 时,向量a到向量b是逆时针方向, θ 取值范围是 [0, 180)
当 value = 0 时,向量a 与向量b 平行共线, θ 值是 0
当 value < 0 时,向量a到向量b 是顺时针方向, θ 取值范围是 [0, -180)

在左手坐标系中(Unity 使用的是左手坐标系)
当 value > 0 时,向量a到向量b是顺时针方向, θ 取值范围是 [0, 180),
当 value = 0 时,向量a 与向量b 平行共线, θ 值是 0
当 value < 0 时,向量a到向量b是逆时针方向, θ 取值范围是 [0, -180),
在这里插入图片描述
在这里插入图片描述
叉乘符合右手定则
在左手坐标系中四根手指的旋转方向是顺时针的,所以在左手坐标系中(Unity 使用的是左手坐标系),如果向量a和向量b呈顺时针,那么叉乘结果c 指向大拇指方向,如果呈逆时针,则c指向大拇指的反方向

在右手坐标戏中四根手指的旋转方向是逆时针的,所以在右手坐标系中,如果向量a和向量b呈逆时针,那么叉乘结果c指向大拇指方向,如果呈顺时针,则c指向大拇指的反方向

叉积示例

叉积判断物体的左右关系

using UnityEngine;

public class Test : MonoBehaviour
{
    public Transform Cube1;
    public Transform Cube2;
    
    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            Vector3 direction = Cube2.position - Cube1.position;
            Vector3 cross = Vector3.Cross(direction, Cube1.forward);
            if (cross.y > 0)
            {
                Debug.Log("Cube2在Cube1的左方");
            }
            else if (cross.y < 0)
            {
                Debug.Log("Cube2在Cube1右方");
            }
            else
            {
                Debug.Log("Cube2和Cube1在相同的位置");
            }
        }
    }
}

叉积判断鼠标是否在UI矩形内

using UnityEngine;

public class Test : MonoBehaviour
{
    private RectTransform mRectTrans;//UI
    public Camera UICamera;//UI摄像机
    Vector3[] points = new Vector3[4];//矩形的四个顶点
    private void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            //获取UI坐标系中的鼠标位置
            Vector2 mousePos = Input.mousePosition;
            Vector2 localPos;
            RectTransformUtility.ScreenPointToLocalPointInRectangle(mRectTrans, mousePos, UICamera, out localPos);
            //获取矩形的四个顶点位置
            mRectTrans.GetLocalCorners(points);
            if (IsInRect(points[0], points[1], points[2], points[3], localPos))
            {
                Debug.Log("在UI矩形内");
            }
            else
            {
                Debug.Log("不在UI矩形内");
            }
        }
    }
    private bool IsInRect(Vector2 A, Vector2 B, Vector2 C, Vector2 D,Vector2 E)
    {
        //true:水平方向在矩形内,false:水平方向在矩形外
        bool h = Cross(A - B, A - E) * Cross(C - D, C - E) >= 0;

        //true:垂直方向在矩形内,false:垂直方向在矩形外
        bool v = Cross(A - D, A - E) * Cross(C - B, C - E) >= 0;

        return h && v;
    }

    // Start is called before the first frame update
    void Start()
    {
        mRectTrans = GetComponent<RectTransform>();
    }
    // 计算两个向量的叉积
    public static float Cross(Vector2 a, Vector2 b)
    {
        return a.x * b.y - b.x * a.y;
    }
}

总结

Unity中常用点乘和叉乘来计算两个物体之间的位置关系
例如:敌人在你身后的时候,叉乘可以判断你是往左转还是往右转更好的转向敌人,点乘得到你当前的面朝向的方向和你到敌人的方向之间的夹角
转向敌人代码实现

using UnityEngine;
public class Test : MonoBehaviour
{
    public Transform Player;//玩家
    public Transform Enemy;//敌人
    public float RotateSpeed = 20f;
    private void Start()
    {
    }
    private void Update()
    {
        //计算夹角
        float dot = Vector3.Dot((Enemy.position - Player.position).normalized, Player.forward); ;
        float angle = Mathf.Acos(dot) * Mathf.Rad2Deg;
        //夹角不为0
        if(angle != 0 && angle > 1f)
        {
            //**使Player一直朝向敌人**
            //计算方向
            Vector3 direction = Vector3.Cross(Enemy.position - Player.position, Player.forward);
            if (direction.y < 0)
            {
                //左转
                Player.Rotate(Player.up, RotateSpeed * Time.deltaTime);
            }
            else if (direction.y > 0)
            {
                //右转
                Player.Rotate(Player.up, -RotateSpeed * Time.deltaTime);
            }
        }
    }
}
  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值