Unity向量的演算法:叉乘和叉乘的實戰用法

彭然心跳發表於2017-09-08

如何用向量叉乘判斷方向

在網上有很多人將向量的應用總結為一句話:點乘判斷角度,叉乘判斷方向,這裡就說說如何用向量叉乘判斷方向。

我們都知道在一個平面內的兩個非平行向量叉乘的結果是這個平面的法向量,這個法向量是有方向的:
 1 using UnityEngine;
 2 using System.Collections;
 3 public class VectorCrossDemo : MonoBehaviour 
 4 {
 5 private GameObject wheelObj;
 6 private Vector3 wheelPos = Vector3.zero; 
 7 private Vector3 oldVec = Vector3.zero;
 8 private Vector3 currVec = Vector3.zero;
 9 // Use this for initialization
10 void Start () 
11 {
12 wheelObj = GameObject.Find("Wheel");
13 if(null != wheelObj)
14 {
15 wheelPos = wheelObj.transform.position;
16 }
17 }
18  
19 // Update is called once per frame
20 void Update () 
21 {
22 if (Input.GetMouseButton(0))
23 {
24 var ray = Camera.main.ScreenPointToRay(Input.mousePosition);
25 RaycastHit hit;
26 if (Physics.Raycast(ray, out hit)) 
27 {
28 if(hit.transform.name.Equals("Wheel"))
29 {
30 RotateWheel(hit.point);
31 }
32 }
33 }
34 }
35 void RotateWheel (Vector3 pos)
36 {
37 currVec = pos - wheelPos;//計算方向盤中心點到觸控點的向量        
38 Vector3 normalVec = Vector3.Cross(currVec, oldVec);//計演算法向量
39 float vecAngle = Vector2.Angle(currVec, oldVec);//計算兩個向量的夾角        
40 // 使用“右手定則”可知,當大拇指方向指向我們,四指方向為逆時針方向;
41 // 當大拇指遠離我們,四指方向為順時針方向。
42 // 這裡叉乘後的法向量平行於z軸,所以用法向量的z分量的正負判斷法向量方向
43 if (normalVec.z > 0)// 和z軸同向,則順時針轉
44 {
45 wheelObj.transform.Rotate(Vector3.forward, -vecAngle);// 順時針轉
46 }
47 else if (normalVec.z < 0)//和z軸反向,則逆時針轉
48 {
49 wheelObj.transform.Rotate(Vector3.forward, vecAngle);// 逆時針轉
50 }
51 oldVec = currVec;//賦值
52 }
53 }

 上面只是對叉乘的一個簡單用法,那麼下面就更為複雜通過點乘求出弧度(然後轉角度),叉乘求方向(叉乘的值點y大於0順時針旋轉,小於0逆時針旋轉,等於0正前方)

using BwSocket;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class AniSubGun : ArchAnimation
{
    //1.首先我得旋轉到我指定的目標點
    //2.控制旋轉的角度
    //3.判斷目標是否在我的攻擊範圍之內
    //4.判斷是否在我的扇形攻擊範圍角度之內
    //5. 
    //// 通過向量直接獲取兩個向量的夾角(預設為 角度), 此方法範圍 [0 - 180]  
    //float angle = Vector3.Angle(a, b);
    //這兩個演算法是一樣的
    //// 計算 a、b 點積結果  
    //float result = Vector3.Dot(a, b);
    //// 計算 a、b 單位向量的點積,得到夾角餘弦值,|a.normalized|*|b.normalized|=1;  
    //result = Vector3.Dot(a.normalized, b.normalized);
    //// 通過反餘弦函式獲取 向量 a、b 夾角(預設為 弧度)  
    //float radians = Mathf.Acos(result);
    //// 將弧度轉換為 角度  
    //angle = radians * Mathf.Rad2Deg;

    public const int ANI_SEARCH = 0;

    protected static float m_AniSpeed = 2f;
    protected static float m_MaxAngle = 120f;
    protected static float m_MinRanger = 0f;
    protected static float m_MaxRanger = 1500f;

    protected FightTarg m_AttackTarg = null;
    protected Vector3 m_NormalizedVec = Vector3.zero;

    public delegate void ArmAtEnemyEvent(bool bFire);
    protected ArmAtEnemyEvent OnArmAtEnemy;
    protected override void Awake()
    {
        base.Awake();
    }
    protected override void Start()
    { 
        base.Start();
    }
    protected override void Update()
    {
        base.Update(); 
        ArmAtTarg(); 
    } 
    protected override void OnDestroy()
    {
        base.OnDestroy(); 
    }
    public void SetAttackTarg(FightTarg itarg)
    {
        m_AttackTarg = itarg;
    }
    private void ArmAtTarg()
    {
        if (m_NormalizedVec == null || m_AttackTarg == null || m_AttackTarg.IsDie())
        {
            DoCallbackArmAtEnemy(false);
            m_AttackTarg = null;
            return;
        }
        Vector3 myV3 = m_go.transform.rotation * Vector3.forward; ;
        Vector3 npcV3 = m_AttackTarg.getHitGameObject().transform.position - m_go.transform.position;//敵人和我的向量

        npcV3.y = myV3.y;

        float RangleAngle = Mathf.Abs(Vector3.Angle(m_NormalizedVec, npcV3));//NPC和法向量的夾角(0-180)

        float RotationAngle1 = Mathf.Abs(Vector3.Angle(myV3, npcV3)); //炮和NPC的夾角(0-180) 
        float Distance = npcV3.magnitude;//NPC和我的距離 

        Vector3 CrossNorNpc = Vector3.Cross(m_NormalizedVec, npcV3);
        Vector3 CrossNorMe = Vector3.Cross(m_NormalizedVec, myV3);
        Vector3 CrossMeNpc = Vector3.Cross(myV3, npcV3);
        Vector3 CrossMeNor = Vector3.Cross(myV3, m_NormalizedVec); 

        float RotationAngle2 = Mathf.Abs(Vector3.Angle(myV3, m_NormalizedVec)); //炮和法向量的夾角(0-180)

        //Debug.LogError("AniAirArtilleryBase::RotationCent()和法向量的夾角:" + RangleAngle
        //    + ",NPC的夾角:" + RotationAngle1 + ",NPC距離:" + Distance + ",旋轉方向:" + CrossMeNpc.y);

        if (RangleAngle > m_MaxAngle || Distance > m_MaxRanger || Distance < m_MinRanger)
        {
            DoCallbackArmAtEnemy(false);
            return;
        }

        float sidesign = CrossNorNpc.y * CrossNorMe.y;
        float rotation = 0;
        if (sidesign < 0 && RotationAngle2>10)
        {
            //我和NPC不在法向量的同一邊,則先往法向量方向旋轉
            //角度為炮和NPC的旋轉夾角(大於等於RotationAngle1)
            rotation = (RangleAngle + RotationAngle2);
            if (CrossMeNor.y < 0)
                rotation = 0 - rotation; 
        }  
        else
        {
            rotation = RotationAngle1;
            if (CrossMeNpc.y < 0)
                rotation = 0 - RotationAngle1; 
        }
        if (Mathf.Abs(rotation) > 0.8)
            rotation = rotation * Time.deltaTime * m_AniSpeed; 
        m_go.transform.localEulerAngles += new Vector3(0, rotation, 0); 

        //可以發射子彈了
        DoCallbackArmAtEnemy(true);
    }
    private void DoCallbackArmAtEnemy(bool bArmAt)
    {
        if (OnArmAtEnemy == null)
            return;
        OnArmAtEnemy(bArmAt);
    }
    public void RegisterOnArm(ArmAtEnemyEvent _callback)
    {
        OnArmAtEnemy -= _callback;
        OnArmAtEnemy += _callback;
    }
    public void UnRegisterOnArm(ArmAtEnemyEvent _callback)
    {
        OnArmAtEnemy -= _callback;
    }  
}

 

相關文章