点乘
- 又称“点积”或“内积”
- 公式:各分量乘积和
[x1,y1,z1] [x2,y2,z2] = x1x2+y1y2+z1z2 - 几何意义:$a*b = |a| * |b| * cos<a,b>$
两个向量模长相乘后再乘以二者夹角的余弦值。 - API:float dot = Vector3.Dot(va,vb);
- 点乘只能求得两向量最小的夹角(0~180°)
叉乘
-
又称“叉积”或“外积”。
-
公式:[x1,y1,z1] [x2,y2,z2] = [y1z2-z1y2,z1x2-x1z2,x1y2-y1*x2]
-
几何意义:结果为两个向量所组成面的垂直向量,模长为两向量模长乘积再乘夹角的正弦值。
-
API:Vector3 vec = Vector3.Cross(a,b);
结果与角的关系
叉乘所得向量的模长与角度的关系:0~90度角
Vector3 cross = Vector3.Cross(v1.normalized,v2.normalized);
float angle = Mathf.Asin(cross.magnitude) * Mathf.Rad2Deg;
应用
- 创建垂直与平面的向量。
- 判断两向量的相对位置。
实例
判断两向量之间的夹角是否大于60度
float dot = Vector3.Dot(v1.normalized, v2.normalized);
//写法一,优点:可读性好,缺点:效率差些
float angle = Mathf.Acos(dot)*Mathf.Rad2Deg;
if(angle>60){...}
//写法二,优点:效率较高,缺点:可读性差
if(dot>0.5){...}
画出两向量的叉乘向量
先放置两个cube,并画出指向cube的两向量,再画出两向量的叉乘向量,代码如下:
private void Demo2()
{
Debug.DrawLine(Vector3.zero, cube.transform.position);
Debug.DrawLine(Vector3.zero, cube2.transform.position);
Debug.DrawLine(Vector3.zero, Vector3.Cross(cube.transform.position, cube2.transform.position),Color.yellow);
}
效果
判断player是否进入cube的前方半径10角度120°的扇形攻击范围内
这里有两种做法,效果一样,一种是先用Vector3.Distance判断两物体之间的间距,如果小于10,再做后面判断:求出cube指向扇形最左边顶点的向量,记为left,然后求得cube指向player的向量。然后用点乘求得这两向量的夹角,判断是否小于120°,再用叉乘的y值判断这个夹角是内角还是外角。如果条件都符合说明是在范围内,代码如下:
TestEmptyScript.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TestEmptyScript : MonoBehaviour
{
// Start is called before the first frame update
private GameObject player;
public Vector3 forward; //移动方向
public float speed = 3;
public float fireTime = 0.5f;
public float distance = 20; //当与玩家距离小于distance时就开火
private float lastFireTime;
private Vector3 left;
private Vector3 right;
private Vector3 targetFW;
private Vector3 VelFW;
void Start()
{
player = GameObject.Find("Player");
lastFireTime = Time.time;
left = new Vector3(-Mathf.Cos(30 * Mathf.Deg2Rad) * distance, 0, Mathf.Sin(30 * Mathf.Deg2Rad) * distance);
right = new Vector3(Mathf.Cos(30 * Mathf.Deg2Rad) * distance, 0, Mathf.Sin(30 * Mathf.Deg2Rad) * distance);
}
// Update is called once per frame
void Update()
{
draw();
if (Vector3.Distance(player.transform.position, this.transform.position) <= distance && Time.time - lastFireTime >= fireTime)
{
//是否在攻击角度内
float dot = Vector3.Dot((transform.TransformPoint(left) - transform.position).normalized, (player.transform.position - this.transform.position).normalized);
Vector3 cross = Vector3.Cross((transform.TransformPoint(left) - transform.position).normalized, (player.transform.position - this.transform.position).normalized);
if (dot >= -0.5 && cross.y > 0)
{
lastFireTime = Time.time;
transform.forward = (player.transform.position - this.transform.position).normalized;
fireBall();
}
}
/*if (Input.GetKey(KeyCode.K) == false)
transform.Translate(Vector3.forward * Time.deltaTime * speed);*/
}
private void draw()
{
Debug.DrawLine(transform.position, transform.TransformPoint(left));
Debug.DrawLine(transform.position, transform.TransformPoint(right));
}
private void fireBall()
{
GameObject bullet = (GameObject)Resources.Load("Bullet");
bullet = Instantiate(bullet);
bullet.transform.position = this.transform.position;
bullet.transform.rotation = this.transform.rotation;
}
}
BulletScript.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BulletScript : MonoBehaviour
{
public float speed = 50;
private int maxTF = 250;
private Rigidbody rig;
private float beginTime;
// Start is called before the first frame update
void Start()
{
transform.Translate(Vector3.forward);
rig = transform.GetComponent<Rigidbody>();
Vector3 vel = this.transform.forward*speed;
rig.velocity = vel;
beginTime = Time.time;
}
// Update is called once per frame
void Update()
{
//transform.Translate(Vector3.forward * Time.deltaTime * speed);
//出了最大有效坐标范围,就删掉对象
if (Mathf.Abs(transform.position.x) > maxTF || Mathf.Abs(transform.position.z) > maxTF||Time.time-beginTime>10)
Destroy(gameObject);
}
}
另一种是直接算cube指向player的向量与cube的foward向量的夹角是否小于120°/2即60°,如果小于六十度并且两物体间距小于10则说明进入了扇形范围内。需要把34~42行的代码改为如下代码:
//是否在攻击角度内
float dot = Vector3.Dot(transform.forward.normalized, (player.transform.position - this.transform.position).normalized);
if (dot >= 0.5)
{
lastFireTime = Time.time;
transform.forward = (player.transform.position - this.transform.position).normalized;
fireBall();
}
效果