三维向量
向量是指一个同时具有大小和方向,且满足平行四边形法则的几何对象。
向量的模
po点相对于世界坐标原点的距离: po.magnitude。
标准向量,归一向量,指的是将向量的模变成1,方向不变。改变后的向量: po.normalized。
向量的方向
求向量的方向(求向量模的方向),先求向量的标准化向量(归一化向量),然后根据标准向量得到向量的方向。
// 向量的长度:求模的大小 void Update1() { Vector3 po = this.transform.position; // 向量的模 var l1 = Mathf.Sqrt(Mathf.Pow(po.x, 2) + Mathf.Pow(po.y, 2) + Mathf.Pow(po.z, 2)); var l2 = Vector3.Distance(Vector3.zero, po); //po.magnitude: po向量的模,po点相对于世界坐标原点的距离 var l3 = po.magnitude; Debug.LogFormat("{0}-{1}-{2}", l1, l2, l3); // 标准向量,归一向量,单位向量:模长度为1的向量 //po.normalized: 标准向量,归一向量,指的是将向量的模变成1,方向不变。改变后的向量。 //debug划线,从世界坐标系原点,到当前的坐标点 Debug.DrawLine(Vector3.zero, po); Debug.DrawLine(Vector3.zero, po.normalized,Color.red); } // 向量的方向:求方向,求向量模的方向,求标准向量,归一化向量 private void Update2() { Vector3 po = this.transform.position; //向量/向量模长 = 标准化向量 Vector3 n1 = po / po.magnitude; //使用向量API 求的 标准化向量 Vector3 n2 = po.normalized; Debug.DrawLine(Vector3.zero,po); Debug.DrawLine(Vector3.zero,n2,Color.blue); }
向量的加、减运算
向量的加运算
两个向量相加,它的结果是这2个向量再加上它们对应的辅助向量(复制,平移)组成的平行四边形后,它们中间的连线。从开始指向结束。
向量的减运算
两个向量相减,它的结果是从被减数的箭头开始,指向减数的箭头结束。
public Transform t1, t2, t3; private void Update3() { //减 向量:结果是 结果向量从减数箭头点指向t1【被减数箭头点】+ 平移到t1和t2的起点交点处 Vector3 n1 = t1.position - t2.position; if (Input.GetKey(KeyCode.A)) { // 每次移动单位向量,这样距离越长,花费的时间就越长,能体现出距离感 t3.Translate(n1.normalized); } // 加 向量:结果是 两个向量分别生成各自的辅助虚线向量,组成一个平行四边形,加向量的结果就是这个平行四边形的中间连线 Vector3 n2 = t1.position + t2.position; if (Input.GetKey(KeyCode.B)) { // 每次移动单位向量,这样距离越长,花费的时间就越长,能体现出距离感 t3.Translate(n2.normalized); } Debug.DrawLine(Vector3.zero, n1); Debug.DrawLine(Vector3.zero, n2, Color.red); }
向量的点乘与叉乘
使用向量的点乘可以求这2个向量的夹角,不过这个夹角是比较小的那个。
使用向量的叉乘可以求这2个向量的夹角是否大于180度,小于180时,结果向量的y是大于0的,大于180时,结果向量的y是小于0的。
public float dotDegValue; private void Update() { Debug.DrawLine(Vector3.zero, t1.position); Debug.DrawLine(Vector3.zero, t2.position); //根据向量的点乘,求夹角 //注意:点乘求出来的夹角是2个单位向量的最小夹角,如果两个向量的夹角大于180,比如270,则求出来的结果是哪个小部分,90度。 float dotValue = Vector3.Dot(t1.position.normalized, t2.position.normalized); dotDegValue = Mathf.Acos(dotValue) * Mathf.Rad2Deg; Debug.Log(dotDegValue); //根据2个向量的叉乘求夹角是否大于180,当小于180时,结果向量的y是大于0的,大于180时,结果向量的y是小于0的 //2个向量叉乘的意义为:得出2个向量组成平面的垂直向量 Vector3 crossValue = Vector3.Cross(t1.position, t2.position); Debug.DrawLine(this.transform.position, crossValue, Color.red); //y小于0,大于180 if (crossValue.y < 0) { dotDegValue = 360 - dotDegValue; } }
角度和弧度的转换:Degree角度 -> Radian弧度
弧度 = 角度数 * PI/180
private void Update4() { //角度 -> 弧度: 弧度 = 角度数 * PI/180 float d1 = 60; float r1 = d1 * Mathf.Deg2Rad; float r2 = d1 * Mathf.PI / 180; print("r1: "+r1 + " r2:"+r2); //弧度 -> 角度: 角度 = 弧度数 * 180/PI float r02 = Mathf.PI / 3; float g02 = r02 * 180 / Mathf.PI; float g03 = r02 * Mathf.Rad2Deg; print("g02: "+g02+" g03:"+g03); }
三角函数
三角函数, 已知一个三角形里的2个部分(角度,边长),就能求出其他部分(角度,边长)
private void Update5() { //列如:已知角度x, 边长b, 求边长a //根据公式:tanx = a/b float x = 50, b = 20; float a = Mathf.Tan(x * Mathf.Deg2Rad) * b; //Debug.Log(a); //已知:边长a, 边长b, 求角度 angle //公式:angle = arc tan(a/b) float angle = Mathf.Atan(a / b); float angle2gad = Mathf.Rad2Deg * angle; //Debug.Log(string.Format("{0}:{1}", angle, angle2gad)); //三角函数在项目中的运用 //TransformPoint将自身坐标系中的点转成世界坐标系中的点, //TransformPoint(0, 0, 10)的意思是延物体自身坐标向前(z轴)走10米,然后将这个点转到世界坐标系中对应的点 Vector3 worldSpaceP = transform.TransformPoint(0, 0, 10); Debug.DrawLine(this.transform.position, worldSpaceP); //练习:计算物体右前方30度,10m远的坐标 // 根据题目可知,是知道角度和斜边,求a,b边 // 由公式:sinx = a/c, cosx = b/c 得: // x = a = c * sinx; z = b = c * cosx; float movX = 10 * Mathf.Sin(30 * Mathf.Deg2Rad); float movZ = 10 * Mathf.Cos(30 * Mathf.Deg2Rad); Vector3 worldSpaceP2 = transform.TransformPoint(movX, 0, movZ); Debug.DrawLine(this.transform.position, worldSpaceP2, Color.red); }
欧拉角和四元数
欧拉角和四元数:用于表示在三维坐标系中的一个物体, 包括这个物体的位置,角度。
欧拉角采用Vector3三维坐标类型设置是因为Vector3中有对应x,y,z的值,这和欧拉角中设置x,y,z轴上的旋转角度虽然数值意义不同,但它们有相同的数据结构,这是欧拉角选择使用Vector3表示的原因。
四元数就是用来旋转用的,它是轴角模式的旋转,与欧拉角不同的是四元数的旋转全部是绕自己的x,y,z轴旋转。而欧拉角是x,z绕自身的轴y是绕世界坐标系的y,用来解决欧拉角的万向节死锁问题。
private void OnGUI() { //欧拉角 if (GUILayout.RepeatButton("欧拉角X轴")) { //欧拉角采用Vector3类型设置是因为Vector3中有对应x,y,z的值,这和欧拉角中设置x,y,z轴上的旋转角度虽然数值意义不同,但它们有相同的数据结构,这是 //欧拉角选择使用Vector3表示的原因 //两者的区别如下: //1.位置:有方向(从世界坐标系原点指向当前点),有大小(从世界坐标原点到当前点的位置) //向量的x,y,z分别表示当前点在各个轴向上的有向位移 Vector3 pos = this.transform.position; //2.欧拉角,没有方向,大小的概念。它表示的是在x,y,z轴上转了多少度 Vector3 euler = this.transform.eulerAngles; //各分量相加 this.transform.eulerAngles += new Vector3(1, 0,0); } if (GUILayout.RepeatButton("欧拉角Y轴")) { this.transform.eulerAngles += Vector3.up; } if (GUILayout.RepeatButton("欧拉角Z轴")) { this.transform.eulerAngles += Vector3.forward; } //四元数就是用来旋转用的,它是轴角模式的旋转,与欧拉角不同的是四元数的旋转全部是绕自己的x,y,z轴旋转。而欧拉角是x,z绕自身的轴y是绕世界坐标系的y,用来解决欧拉角的万向节死锁问题 if (GUILayout.RepeatButton("四元数旋转")) { //四元数设置需要2个条件:1.绕哪个轴,2.转多少度 //绕y轴 Vector3 axis = Vector3.right; //旋转弧度 float radValue = 60 * Mathf.Deg2Rad; //组建四元数 Quaternion qt = new Quaternion(); qt.x = axis.x * Mathf.Sin(radValue / 2); qt.y = axis.y * Mathf.Sin(radValue / 2); qt.z = axis.z * Mathf.Sin(radValue / 2); qt.w = Mathf.Cos(radValue / 2); //设置四元数 //this.transform.rotation = qt; //使用系统便捷方式设置四元数。欧拉角转成四元数 this.transform.rotation = Quaternion.Euler(60,0,0); } if (GUILayout.RepeatButton("四元数X轴旋转")) { this.transform.rotation *= Quaternion.Euler(1,0,0); } if (GUILayout.RepeatButton("四元数Y轴旋转")) { this.transform.rotation *= Quaternion.Euler(0, 1, 0); } if (GUILayout.RepeatButton("四元数Z轴旋转")) { this.transform.rotation *= Quaternion.Euler(0, 0, 1); } } void Update() { //四元数应用:求当前坐标右前方30度,距离10的坐标 if (Input.GetMouseButtonDown(1)) { Vector3 v0 = new Vector3(0,0,10); // v0向量绕y轴旋转60度 Vector3 v1 = Quaternion.Euler(0, 30, 0) * v0; // v1随自身四元数的旋转而旋转 Vector3 v2 = this.transform.rotation * v1; // 两个向量相加,意义:将这个v0向量的起点移动到当前物体的位置上。 target = this.transform.position + v2; } Debug.DrawLine(this.transform.position, target,Color.blue); }
碰撞检测
碰撞条件
1.两者都有碰撞器
2.任一个有刚体(运动者,主动发起撞击的GO)
碰撞回调方法 // 碰撞开始时,接触的第一帧,触发回调 private void OnCollisionEnter(Collision collision) //中间的每一帧 private void OnCollisionStay(Collision collision) // 碰撞结束时,接触的最后一帧,触发回调 private void OnCollisionExit(Collision collision)
触发条件
1.两者都有碰撞器
2.任一个有刚体
3.任一个刚体的Is Trigger被勾选
public class ColisionDemo : MonoBehaviour { public float speet = 300; // 碰撞开始时,接触的第一帧,触发回调 private void OnCollisionEnter(Collision collision) { //通过collision.collider拿到了另一碰撞对象的碰撞器,那么就可以通过这个碰撞器获取这个对象上所有的其他组件。 //collision.collider.GetComponent<MeshRenderer>(); //Debug.Log(collision.collider.name); Debug.Log(string.Format("碰撞器碰撞了:{0}", collision.collider.name)); //撞击的碰撞点 ContactPoint cp = collision.contacts[0]; //cp.point碰撞点的世界坐标 //cp.normal碰撞点接触面的法线 } //中间的每一帧 private void OnCollisionStay(Collision collision) { } // 碰撞结束时,接触的最后一帧,触发回调 private void OnCollisionExit(Collision collision) { } //触发回调,接触的第一帧,触发回调 private void OnTriggerEnter(Collider other) { Debug.Log(string.Format("触发器触发了:{0}", other.name)); } private void OnTriggerStay(Collider other) { } private void OnTriggerExit(Collider other) { } //当物体的移动速度非常快时可能检测不到触发和碰撞,情况是在接触前那一刻检测,等第二次检测时已经穿过去了,判断结果还是没有接触 private void FixedUpdate() { Debug.Log(string.Format("frameCount: {0}", Time.frameCount)); this.transform.Translate(Time.deltaTime * speet * -1, 0, 0); } private RaycastHit hit; public LayerMask layer; private Vector3 targetPos; // 使用射线解决移动速度过快,接触检查失效问题 void Start() { //射线投射命中 //射线投射:Raycast(起点坐标,方向,受击物体信息,距离,图层) var res = Physics.Raycast(this.transform.position, -this.transform.right, out hit, 500, layer); if (res) { //击中的位置 targetPos = hit.point; } else { //没有命中目标 //targetPos = this.transform.TransformPoint(0,0,500); targetPos = this.transform.position + (-this.transform.right * 100); } } // Update is called once per frame void Update() { transform.position = Vector3.MoveTowards(transform.position, targetPos, speet*Time.deltaTime); //子弹从发射位置走到击中的位置就停止了 if ((transform.position - targetPos).sqrMagnitude < 0.1) { //击中: 击中的物体销毁,子弹也销毁 Destroy(hit.collider.gameObject); Destroy(this.gameObject); } } }
三维向量Vector的常用API使用
模长:Vector3.Distance
标准化向量:Vector3.normalized;
反射向量:Vector3.Reflect
物体的运动
Vector3.Lerp:有快到慢,每次前进总长度的10%,无限接近目标点。
Vector3.MoveTowards:匀速前进,无限接近目标点。
Vector3.SmoothDamp:平滑阻尼,速度按固定的速率在减弱。
Vector3.LerpUnclamped:变速运动,与Lerp变速对比变速运动为:起点,终点不变,比例改变。
public class VectorAPIDemo : MonoBehaviour { public Transform t1; private Vector3 tangent; private Vector3 binNormal; public Vector3 currentSpeed; public AnimationCurve curve; private float x; public float time = 5; //test private float speetScall = 1/30; // Start is called before the first frame update void Start1() { //属性设置注意,因为this.transform.position返回的是position的副本,无法真正修改position的值,所以会报错。 //this.transform.position.z = 1; //解决方案:将position作为一个整体设置 Vector3 p = this.transform.position; p.z = 2; this.transform.position = p; //Distance: 为模长。 //sqrMagnitude: 为(位置1-位置2).模长平方。 Vector3.Distance(tangent, binNormal); } // Update is called once per frame void Update() { Vector3 vect = new Vector3(); Vector3 vect0 = vect.normalized; vect.Normalize(); //计算垂直向量:在三维坐标系中,一个向量的垂直向量有2条 //OrthoNormalize(ref Vector3 normal, ref Vector3 tangent); //计算t1物体在地面上的投影 Vector3 norm = t1.position; Vector3 project = Vector3.ProjectOnPlane(norm, Vector3.up); Debug.DrawLine(Vector3.zero, norm); Debug.DrawLine(Vector3.zero, project, Color.red); //计算反射向量:Vector3.Reflect; //向量的加,减,点乘,差乘等。 } private void OnGUI() { if (GUILayout.RepeatButton("Lerp")) { //Lerp有快到慢,每次前进总长度的10%,无限接近目标点; //每次都是起点改变,终点和比例不变。 this.transform.position = Vector3.Lerp(transform.position, new Vector3(0, 0, 10), 0.1f*Time.deltaTime); } if (GUILayout.RepeatButton("MoveTowards---------------")) { //匀速前进,无限接近目标点; this.transform.position = Vector3.MoveTowards(transform.position, new Vector3(0, 0, 10), 0.1f); } if (GUILayout.RepeatButton("SmoothDamp")) { //平滑阻尼,速度按固定的速率在减弱 this.transform.position = Vector3.SmoothDamp(transform.position, new Vector3(0,0,10),ref currentSpeed,2); } if (GUILayout.RepeatButton("变速运动")) { x += Time.deltaTime / time; Vector3 begin = Vector3.zero; //与Lerp变速对比变速运动为:起点,终点不变,比例改变 //curve.Evaluate(x):随着时间的变化,根据x值取y值。因为x值没有,这里自己造一个,通过x += Time.deltaTime累加法,每秒加一 transform.position = Vector3.LerpUnclamped(begin, new Vector3(0,0,10),curve.Evaluate(x)); } } }
四元数-欧拉角常用API使用
四元数
Quaternion qt = transform.rotation;
1.四元数 -> 欧拉角
Vector3 euler = qt.eulerAngles;
2.欧拉角 -> 四元数
Quaternion qt02 = Quaternion.Euler(0, 90, 0);
3.轴、角转换
transform.rotation = Quaternion.AngleAxis(30, Vector3.up);
4.注视旋转
transform.LookAt(target.position);
5.Lerp差值旋转,由快到慢
transform.rotation = Quaternion.Lerp(transform.rotation, dir, 0.1f);
6.RotateTowards: 匀速旋转
transform.rotation = Quaternion.RotateTowards(transform.rotation, dir, 0.1f);
public class QuaternionAPIDemo : MonoBehaviour { public float moveSpeed = 1; // Start is called before the first frame update void Start() { //四元数 Quaternion qt = transform.rotation; //1.四元数 -> 欧拉角 Vector3 euler = qt.eulerAngles; //2.欧拉角 -> 四元数 Quaternion qt02 = Quaternion.Euler(0, 90, 0); //3.轴、角转换 //transform.rotation = Quaternion.AngleAxis(30, Vector3.up); //transform.localRotation = Quaternion.AngleAxis(30,Vector3.up); } // Update is called once per frame public Transform target; private void OnGUI() { Quaternion dir = Quaternion.LookRotation(target.position - transform.position); if (GUILayout.RepeatButton("LookRotation+++++++++++++++++++")) { //4.注视旋转 //方法1 //Quaternion dir2 = Quaternion.LookRotation(target.position - transform.position); //transform.rotation = dir2; //方法2 transform.LookAt(target.position); } if (GUILayout.RepeatButton("Lerp")) { //5.Lerp差值旋转,由快到慢 //它与注视旋转的区别是:注视旋转是一帧设置完成,Lerp是多帧设置完成 transform.rotation = Quaternion.Lerp(transform.rotation, dir, 0.1f); } if (GUILayout.RepeatButton("RotateTowards")) { //6.RotateTowards: 匀速旋转 transform.rotation = Quaternion.RotateTowards(transform.rotation, dir, 0.1f); } if (GUILayout.RepeatButton("Angle角度判断")) { Quaternion dir2 = Quaternion.Euler(0, 180, 0); transform.rotation = Quaternion.Lerp(transform.rotation, dir2, 0.005f); //7.2个四元数角度差计算 if (Quaternion.Angle(transform.rotation, dir2) < 30) { transform.rotation = dir2; } } } void Update() { //上面提供的方法默认的旋转轴是绕z轴,如果想绕x轴旋转,可通过下面的方式 //this.transform.right = target.position - this.transform.position; //从x轴正方向 -> 注视目标位置的方向 //8.从?到?的旋转 //transform.rotation = Quaternion.FromToRotation(Vector3.right, target.position - transform.position); //课后作业:物体随ad/sw进行上下旋转 var hRes = Input.GetAxis("Horizontal"); var vRes = Input.GetAxis("Vertical"); if (hRes != 0 || vRes != 0) { Debug.Log(string.Format("hRes:{0}- vRes:{1}", hRes, vRes)); //transform.rotation = Quaternion.LookRotation(new Vector3(hRes, 0, vRes)); //带旋转过程 var targetRotation = Quaternion.LookRotation(new Vector3(hRes, 0, vRes)); transform.rotation = Quaternion.Lerp(this.transform.rotation, targetRotation, moveSpeed *Time.deltaTime); transform.Translate(0, 0, moveSpeed * Time.deltaTime); } } }