3D数学

---恢复内容开始---

Unity之3D数学

 

前言:向量是游戏开发的过程中最重要的数学工具之一,它能够使用简单的表达方式来实现各种复杂的游戏效果。例如,使用向量可以控制角色的行走和朝向。甚至还可以用来实现各种丰富的着色器的效果。

一:向量的概述:

向量在数学表达上就是一个有符号数字列表,它使用这个数字列表来表达各种不同的具体含义。向量是相对于标量(标量是只有大小而没有方向的量)而言的,它不但具有大小,而且还具有表达方向的能力。使用向量也能描述物体之间的相对位置,这个需要通过简单的向量计算来完成。

二:向量的运算:

1.向量的大小:向量的大小通常也被称之为向量的长度或者模。在数学中用双竖线来表示向量的模,如 ||v|| 向量具有大小和方向。但是按照向量的表示方法,其大小和方向都没有表示出来。如二维向量(3,4)的大小既不是3,也不是4,而是5,因为向量的大小没有明确的表示,所以需要计算。

2.在Unity3D中,我们使用向量的模来判断两个对象的距离是否低于设定的距离,完整的代码如下:

 

/**
*    Title:"":项目
*          主题 :3D数学小Demo
*    Description:
*          功能:使用向量的模来判断两个对象的距离是否低于设定的距离
*    Date:2017.11.05
*    Version:Unity5.5.4
*    Modify Recoder:
*    Operator:HaoYuHang
*
**/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;


public class Demo1_3DMaths : MonoBehaviour
{

    #region 字段和属性定义

    public Transform TraOtherTransform;                 //其他物体
    private float _floatCloseDistance = 5.0f;           //设定限制接近的最小的距离

    #endregion

    void Update()
    {
        if (TraOtherTransform)
        {
            //使用向量的减法来获得两个对象之间的向量
            float sprLen = (TraOtherTransform.position - transform.position).sqrMagnitude;
            print(sprLen);
            //sqrMagnitude:返回向量的平方长度
            //根据向量的乘法的定义,一个向量的平方等于自身长度的平方
            //使用sqrMagnitude来取代Magnitude来计算距离 提高运行的效率
            if (sprLen < _floatCloseDistance * _floatCloseDistance)
            {
                print("其他的物体太接近我了");
            }
            else
            {
                print("其他的物体距离很远,目前很安全");
            }
        }
    }

    #region 公共方法的定义

    #endregion

    #region 私有方法的定义

    #endregion

}//class_end

 


如果需要直接得到两个位置之间的距离,也可以 使用向量类中的Distance(Vector3 a,Vector3 b)函数。有的时候需要限定一个对象的运动的速度,可以使用向量类中的ClampMagnitude(Vector3 vector,float maxLength)方法,它可以返回一个把Vector向量限定在长度maxLength的另一个向量。

3.对向量进行单位化:有的时候,我们只需要关心向量的方向而不关心其大小。如当前的飞机的朝向是哪个方位,某个面的发现朝向等。这个时候,使用向量的单位向量是非常有用的。单位 向量就是大小为一的向量。它经常也被称为标准化向量或法线或者归一化。

三:向量的点乘:

两个向量之间也可以相乘,而且具有两种不同的相乘的方法。分别是点乘和叉乘。点乘也被称之为内积。

向量点乘的几何意义:点乘的计算的结果给出了两个向量的相似程度,该相似的程度反映了在两个向量之间的夹角。点乘的结果越大,两个向量就越接近。如果只需要知道a和b夹角的类型而不知道该夹角确切的值,可以只根据所求点乘的结果的符号来判断,如图表示:

3D数学

 在Unity中的使用方法。通过调用向量类所提供的Dot函数,可以计算两个向量的点乘,代码如下:

/**
*    Title:"":项目
*          主题 :3D数学
*    Description:
*          功能:通过调用向量类中所提供的Dot(点乘)函数,计算两个向量之间的点乘
*    Date:2017.11.05
*    Version:Unity5.5.4
*    Modify Recoder:
*    Operator:HaoYuHang
*
**/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;


public class Demo3_3DMaths : MonoBehaviour
{

    #region 字段和属性定义

    public Transform Other;                     //其他物体的方位

    #endregion

    void Update()
    {
        //如果方位不为空的话
        if (Other)
        {
            //使用TransformDirection把当前对象的正面朝向从局部坐标系转化到世界坐标系下,使得该向量与其他位置向量在世界坐标系下统一
            Vector3 forward = transform.TransformDirection(Vector3.forward);
            print(forward);
            //使用向量的减法获得其他对象到当前之间的向量
            Vector3 toOther = Other.position - transform.position;
            //使用点乘的方式计算结果的符号来判断其他对象是否在当前对象的后方。
            if (Vector3.Dot(forward, toOther) < 0)
            {
                print("我看不见敌人");
            }
            else
            {
                print("我可以看见敌人");
            }
        }
    }

    #region 公共方法的定义

    #endregion

    #region 私有方法的定义

    #endregion

}//class_end


如果需要确切知道两个向量之间的夹角,可以使用向量类中的Angle(Vector3 from,Vector3 to)函数,该函数会计算from向量到to向量之间的夹角并返回角度值(不是弧度值)。如果需要计算一个向量在另一个 向量上的投影,可以直接使用向量类中的Project(Vector3 vector,Vector3 onNormal)函数,它返回的是vector向量在onNormal向量上的投影向量。如果需要实现一个对象朝着另一个对象移动过去,可以使用向量类中的MoveTowards(Vector3 current,Vector3 target,float maxDistanceDelta)方法(该方法与向量类中的Lerp函数的作用相似,只是Lerp函数不做速度的限定)。该 函数将会把对象从当前的位置以不超过maxDistanceDelta的值的速度,逐渐移至至target目标位置上,同时每运行一次将返回当前对象的位置值。 

展示两个游戏对象之间夹角的计算程序:

/**
*    Title:"":项目
*          主题 :3D数学
*    Description:
*          功能:两个游戏对象之间的夹角的计算程序
*    Date:2017.11.05
*    Version:Unity5.5.4
*    Modify Recoder:
*    Operator:HaoYuHang
*
**/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;


public class Demo4_3DMaths : MonoBehaviour
{

    #region 字段和属性定义

    public Transform Target;                        //目标对象

    #endregion

    void Update()
    {
        //当对象的朝向与目标对象的夹角小于5的是时候,打印“视线靠近了”信息
        //使用向量的减法获得对象与目标对象之间的向量
        Vector3 targetDir = Target.position - transform.position;
        //当前对象的正方向向量
        Vector3 forward = transform.forward;
        //求出targetDir与forward向量之间的夹角值
        float angle = Vector3.Angle(targetDir, forward);
        //判断该夹角是否小于5
        if (angle < 5)
        {
            print("视线靠近了");
        }
        else
        {
            print("视线没有靠近");
        }
    }

    #region 公共方法的定义

    #endregion

    #region 私有方法的定义

    #endregion

}//class_end

四:向量的叉乘:

叉乘计算的结果是向量,该向量将垂直于原来的两个向量。

1 叉乘的数学意义 
表示方法 
两个向量a和b的叉积写作a×b(有时也被写成a∧b,避免和字母x混淆)。 
定义 
向量积可以被定义为: 
模长:(在这里θ表示两向量之间的夹角(共起点的前提下)(0° ≤ θ ≤ 180°),它位于这两个矢量所定义的平面上。) 
3D数学 
方向:a向量与b向量的向量积的方向与这两个向量所在平面垂直,且遵守右手定则。(一个简单的确定满足“右手定则”的结果向量的方向的方法是这样的:若坐标系是满足右手定则的,当右手的四指从a以不超过180度的转角转向b时,竖起的大拇指指向是c的方向。)

也可以这样定义(等效): 
向量积|c|=|a×b|=|a| |b|sin

 完整的代码演示如下:

/**
*    Title:"":项目
*          主题 :3D数学
*    Description:
*          功能:叉乘在实际项目中的应用
*    Date:2017.11.05
*    Version:Unity5.5.4
*    Modify Recoder:
*    Operator:HaoYuHang
*
**/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;


public class Demo5_3DMaths : MonoBehaviour
{

    #region 字段和属性定义

    public Transform TargetPosition;                        //目标位置

    #endregion

    void Update()
    {
        //求得当前目标的正方向
        Vector3 forward = transform.forward;
        //求得当前对象与目标位置的向量
        Vector3 targetDir = TargetPosition.position - transform.position;
        //使用叉乘判断方向
        if (Vector3.Cross(forward, targetDir).y > 0)
        {
            print("目标在自己的右方");
        }
        else
        {
            print("目标在自己的左方");
        }
    }

    #region 公共方法的定义

    #endregion

    #region 私有方法的定义

    #endregion

}//class_end

五:点乘和叉乘在项目中的应用:
项目需求:

1.通过点乘计算物体B在物体A的前方或者是后方。

2.通过叉乘计算物体A转向物体B时,最小角的旋转方向。

实现过程: 
首先计算出物体A的前方朝向向量v_Bz=B.transform.forward,然后计算物体B相对于物体A的位置向量 v_AB = A.position - B.position;

通过计算v_Bz与v_AB向量点乘结果的正负,判断物体B在物体A的前后 
如果物体B在物体A前方, Vector3.Dot(v_Bz,v_AB)大于0 
如果物体B在物体A后方, Vector3.Dot(v_Bz,v_AB)小于0

通过计算v_Bz与v_AB向量叉乘的结果,判断旋转方向 
如果逆时针旋转,v_C = Vector3.Cross(v_Bz, v_AB)为沿y轴负方向 
如果顺时针旋转,v_C = Vector3.Cross(v_Bz, v_AB)为沿y轴正方向

六:判断敌我方位:

1.判断目标在自己的前后方位可以使用下面的方法:

   Vector3.Dot(transform.forward, target.position-transform.position)

       返回值为正时,目标在自己的前方,反之在自己的后方

2.判断目标在机子的左右方位可以使用下面的方法:

   Vector3.Cross(transform.forward, target.position-transform.position).y

      返回值为正时,目标在自己的右方,反之在自己的左方

 七:Unity项目中的应用:

1.根据叉乘得到a b 向量的相对的位置和顺时针和逆时针方位。

2.简单的说:点乘判断角度 叉乘判断方向,形象的说:当一个敌人在你的身后=时候,点乘可以得到你当前的朝向和你敌人的方向所形成的角度。叉乘可以判断你是往左转还是往右转才能更好的转向敌人。 

 

3.得到a,b夹角的正弦值,计算向量的夹角(0,90),可以配合点乘和Angle方法计算出含正负的方向。

4.根据叉乘大小,得到a,b向量所形成的平行四边形的面积大小,根据面积大小得到向量的相对大小

 ---恢复内容结束---