Unity向量的算法:叉乘和叉乘的实战用法
如何用向量叉乘判断方向
在网上有很多人将向量的应用总结为一句话:点乘判断角度,叉乘判断方向,这里就说说如何用向量叉乘判断方向。
我们都知道在一个平面内的两个非平行向量叉乘的结果是这个平面的法向量,这个法向量是有方向的:
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; } }
版权声明:本文为WindMan原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。