写在前面的话,前两天有个朋友在QQ上问我 如何获取主角面朝方向一定区域中的敌人对象。这个命题看似简单,其实里面蕴含了很多数学方面的东西。今天刚好有时间我就彻底的把这个疑问写在博客中。希望可以帮助到他。
在上代码之前请大家跟我先做几个简单的练习题,角度向量的计算一定要学会,不然后面的东西会很难懂。
1.已知3D坐标,和一个旋转角度,以及一段距离,求目标点的3D坐标。
已知当前点为Target,目标点沿着Target的Y轴旋转30度,沿着Target的X轴延伸10米求目标点的3D坐标?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
using UnityEngine; using System.Collections; public class Test : MonoBehaviour { public Transform Target; void LateUpdate () { Quaternion rotation = Quaternion.Euler(0f,30f,0f) * Target.rotation; Vector3 newPos = rotation * new Vector3(10f,0f,0f); Debug.DrawLine(newPos,Vector3.zero,Color.red); Debug.Log("newpos " + newPos +" nowpos " + Target.position + " distance " + Vector3.Distance(newPos,Target.position)); } } |
输出结果 :新坐标 (8.7, 0.0, -5.0) 当前坐标 (0.0, 0.0, 0.0)两点之间的距离 10。
2.已知3D模型的角度求它的向量。
已知3D模型Target,Y轴旋转30度后向前平移。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
using UnityEngine; using System.Collections; public class Test : MonoBehaviour { public Transform Target; void LateUpdate () { if(Input.GetMouseButton(0)) { Quaternion rotation = Quaternion.Euler(0f,30f,0f) * Target.rotation; Vector3 newPos = rotation * Vector3.forward; Target.Translate(newPos.x,newPos.y,newPos.z); } } } |
3.已知一个目标点,让模型朝着这个目标点移动。
这是一个比较简单的例子,大家应该都能看明白。
1 2 3 4 |
Target.transform.LookAt(new Vector3 (100f,200f,300f)); Target.Translate(Vector3.forward); |
这里我要说的就是Vector3.forward ,它等价与 new Vector3(0,0,1);它并不是一个坐标,它是一个标准向量,方向是沿着Z轴向前。这样平移一次的距离就是1米, 如果Vector3.forward * 100那么一次平移的距离就是100米。
在看看下面这段代码
1 2 3 4 5 |
Vector3 vecn = (TargetCube.position - Target.position).normalized; Target.Translate(vecn *0.1f); |
用向量减去一个向量求出它们的差值,normalized 是格式化向量,意思是把它们之间向量格式化到1米内。这样就可以更加精确的计算一次平移的距离了 vecn *0.1f 就标示一次平移1分米,蛤蛤。
向量不仅可以进行X Y Z轴的移动,同样可以进行旋转 ,下面这段代码就是让向量沿着Y轴旋转30度。
1 2 3 4 5 6 7 |
Vector3 vecn = (TargetCube.position - Target.position).normalized; vecn = Quaternion.Euler(0f,30f,0f) * vecn; Target.Translate(vecn *0.1f); |
如果上述三道简单的练习题 你都能了然于心的话,那么本文最大的难题我相信也不会是什么难事,继续阅读吧。
假设我们需要计算主角面前5米内所有的对象时。以主角为圆心计算面前5米外的一个点,为了让大家看清楚我现将这条线绘制出来。
1 2 3 4 5 6 7 8 9 |
private float distance = 5f; void Update () { Quaternion r= transform.rotation; Vector3 f0 = (transform.position + (r *Vector3.forward) * distance); Debug.DrawLine(transform.position,f0,Color.red); } |
如下图所,我们已经将这两个点计算出来了。此时你可以动态的编辑主角Y轴的坐标,这个点永远都是沿着主角当前角度面前5米以外的点。
接下来,我们需要计算主角面前的一个发散性的角度。假设主角看到的是向左30度,向右30度在这个区域。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
private float distance = 5f; void Update () { Quaternion r= transform.rotation; Vector3 f0 = (transform.position + (r *Vector3.forward) * distance); Debug.DrawLine(transform.position,f0,Color.red); Quaternion r0= Quaternion.Euler(transform.rotation.eulerAngles.x,transform.rotation.eulerAngles.y - 30f,transform.rotation.eulerAngles.z); Quaternion r1= Quaternion.Euler(transform.rotation.eulerAngles.x,transform.rotation.eulerAngles.y + 30f,transform.rotation.eulerAngles.z); Vector3 f1 = (transform.position + (r0 *Vector3.forward) * distance); Vector3 f2 = (transform.position + (r1 *Vector3.forward) * distance); Debug.DrawLine(transform.position,f1,Color.red); Debug.DrawLine(transform.position,f2,Color.red); Debug.DrawLine(f0,f1,Color.red); Debug.DrawLine(f0,f2,Color.red); } |
如下图所示,这时主角面前的区域就计算出来了。看起来就是两个三角形之间的区域。
最后就是简单的套用公式,计算一个点是否在三角形内,在本文中就是计算敌人的点是否在面前的这两个三角形内。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
using UnityEngine; using System.Collections; public class MyTest : MonoBehaviour { public Transform cube; private float distance = 5f; void Update () { Quaternion r= transform.rotation; Vector3 f0 = (transform.position + (r *Vector3.forward) * distance); Debug.DrawLine(transform.position,f0,Color.red); Quaternion r0= Quaternion.Euler(transform.rotation.eulerAngles.x,transform.rotation.eulerAngles.y - 30f,transform.rotation.eulerAngles.z); Quaternion r1= Quaternion.Euler(transform.rotation.eulerAngles.x,transform.rotation.eulerAngles.y + 30f,transform.rotation.eulerAngles.z); Vector3 f1 = (transform.position + (r0 *Vector3.forward) * distance); Vector3 f2 = (transform.position + (r1 *Vector3.forward) * distance); Debug.DrawLine(transform.position,f1,Color.red); Debug.DrawLine(transform.position,f2,Color.red); Debug.DrawLine(f0,f1,Color.red); Debug.DrawLine(f0,f2,Color.red); Vector3 point = cube.position; if(isINTriangle(point,transform.position,f1,f0) || isINTriangle(point,transform.position,f2,f0) ) { Debug.Log("cube in this !!!"); }else { Debug.Log("cube not in this !!!"); } } private float triangleArea(float v0x,float v0y,float v1x,float v1y,float v2x,float v2y) { return Mathf.Abs((v0x * v1y + v1x * v2y + v2x * v0y - v1x * v0y - v2x * v1y - v0x * v2y) / 2f); } bool isINTriangle(Vector3 point,Vector3 v0,Vector3 v1,Vector3 v2) { float x = point.x; float y = point.z; float v0x = v0.x; float v0y = v0.z; float v1x = v1.x; float v1y = v1.z; float v2x = v2.x; float v2y = v2.z; float t = triangleArea(v0x,v0y,v1x,v1y,v2x,v2y); float a = triangleArea(v0x,v0y,v1x,v1y,x,y) + triangleArea(v0x,v0y,x,y,v2x,v2y) + triangleArea(x,y,v1x,v1y,v2x,v2y); if (Mathf.Abs(t - a) <= 0.01f) { return true; }else { return false; } } } |
如下图所示,如果箱子对象是主角的视野中就会检测到。
注意,上图中我的视野选择了两个三角形,如果你需要视野目标点是椭圆形的话,那么可以多设置一些三角形。但是这样就会非常消耗效率,我觉得这里完全可以使用1个三角形,,只是正对的目标点会出现一些偏差,影响其实并不会很大。如下图所示
代码简单的修改一下即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
using UnityEngine; using System.Collections; public class MyTest : MonoBehaviour { public Transform cube; private float distance = 5f; void Update () { Quaternion r= transform.rotation; Vector3 f0 = (transform.position + (r *Vector3.forward) * distance); Debug.DrawLine(transform.position,f0,Color.red); Quaternion r0= Quaternion.Euler(transform.rotation.eulerAngles.x,transform.rotation.eulerAngles.y - 30f,transform.rotation.eulerAngles.z); Quaternion r1= Quaternion.Euler(transform.rotation.eulerAngles.x,transform.rotation.eulerAngles.y + 30f,transform.rotation.eulerAngles.z); Vector3 f1 = (transform.position + (r0 *Vector3.forward) * distance); Vector3 f2 = (transform.position + (r1 *Vector3.forward) * distance); Debug.DrawLine(transform.position,f1,Color.red); Debug.DrawLine(transform.position,f2,Color.red); Debug.DrawLine(f1,f2,Color.red); Vector3 point = cube.position; if(isINTriangle(point,transform.position,f1,f2)) { Debug.Log("cube in this !!!"); }else { Debug.Log("cube not in this !!!"); } } private float triangleArea(float v0x,float v0y,float v1x,float v1y,float v2x,float v2y) { return Mathf.Abs((v0x * v1y + v1x * v2y + v2x * v0y - v1x * v0y - v2x * v1y - v0x * v2y) / 2f); } bool isINTriangle(Vector3 point,Vector3 v0,Vector3 v1,Vector3 v2) { float x = point.x; float y = point.z; float v0x = v0.x; float v0y = v0.z; float v1x = v1.x; float v1y = v1.z; float v2x = v2.x; float v2y = v2.z; float t = triangleArea(v0x,v0y,v1x,v1y,v2x,v2y); float a = triangleArea(v0x,v0y,v1x,v1y,x,y) + triangleArea(v0x,v0y,x,y,v2x,v2y) + triangleArea(x,y,v1x,v1y,v2x,v2y); if (Mathf.Abs(t - a) <= 0.01f) { return true; }else { return false; } } } |
上面我们介绍了三角形判断,当然也可以通过矩形来判断是否相交。。
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
using UnityEngine; using System.Collections; public class MyTest : MonoBehaviour { public Transform cube; private float distance = 5f; void Update () { Quaternion r= transform.rotation; Vector3 left = (transform.position + (r *Vector3.left) * distance); Debug.DrawLine(transform.position,left,Color.red); Vector3 right = (transform.position + (r *Vector3.right) * distance); Debug.DrawLine(transform.position,right,Color.red); Vector3 leftEnd = (left + (r *Vector3.forward) * distance); Debug.DrawLine(left,leftEnd,Color.red); Vector3 rightEnd = (right + (r *Vector3.forward) * distance); Debug.DrawLine(right,rightEnd,Color.red); Debug.DrawLine(leftEnd,rightEnd,Color.red); Vector3 point = cube.position; if(isINRect(point,leftEnd,rightEnd,right,left)) { Debug.Log("cube in this !!!"); }else { Debug.Log("cube not in this !!!"); } } private float Multiply(float p1x , float p1y, float p2x,float p2y, float p0x,float p0y) { return ((p1x - p0x) * (p2y - p0y) - (p2x - p0x) * (p1y - p0y)); } bool isINRect(Vector3 point,Vector3 v0,Vector3 v1,Vector3 v2,Vector3 v3) { float x = point.x; float y = point.z; float v0x = v0.x; float v0y = v0.z; float v1x = v1.x; float v1y = v1.z; float v2x = v2.x; float v2y = v2.z; float v3x = v3.x; float v3y = v3.z; if (Multiply(x,y, v0x,v0y, v1x,v1y) * Multiply(x,y, v3x,v3y, v2x,v2y) <= 0 && Multiply(x,y, v3x,v3y, v0x,v0y) * Multiply(x,y, v2x,v2y, v1x,v1y) <= 0) return true; else return false; } } |
如果大家看了这篇文章后发现在你的项目中还有一些比较麻烦的角度与向量的算法,欢迎在下面留言给我,如果我有时间我会第一时间把方法贴在博客中。互相学习互相进步,加油!哇咔咔。。
马上过年了,雨松MOMO在这里祝福大家2013年幸幸福福的过日子,嘿嘿。
2013年4月补充
感谢楼下朋友给我的留言。, 我在补充一下这篇博客。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
using UnityEngine; using System.Collections; public class NewBehaviourScript : MonoBehaviour { public Transform target; void Update() { float distance = Vector3.Distance(target.position,transform.position); Quaternion right = transform.rotation * Quaternion.AngleAxis(30,Vector3.up); Quaternion left = transform.rotation * Quaternion.AngleAxis(30,Vector3.down); Vector3 n = transform.position + (Vector3.forward * distance); Vector3 leftPoint = left * n ; Vector3 rightPoint = right *n ; Debug.DrawLine(transform.position,leftPoint,Color.red); Debug.DrawLine(transform.position,rightPoint,Color.red); Debug.DrawLine(rightPoint,leftPoint,Color.red); } } |
这里我以角色左右个30度。 这样就可以根据两个模型的距离以及角度来判断了。。
- 本文固定链接: https://www.xuanyusong.com/archives/1977
- 转载请注明: 雨松MOMO 于 雨松MOMO程序研究院 发表
感觉最后一段有问题:
Vector3 n = transform.position + (Vector3.forward * distance);
Vector3 leftPoint = left * n ; //此时的n不是tranform的forward方向吧?
Vector3 rightPoint = right *n ;
松哥能不能推荐一点关于Unity中的方向矢量欧拉角等的计算的书籍或是文章。。
大神这行是什么意思啊Vector3 p2 = (transform.position transform.rotation * Vector3.forward).normalized;
直接这样:(判定扇形30度)
float Distance = Vector3.Distance(transform.position,targetPos.position);
Vector3 p2 = (transform.position transform.rotation * Vector3.forward).normalized;
Vector3 ver3 = (targetPos.position – transform.position).normalized;
if (Distance 0.867) {
return true;
}
return false;
直接这样:(判定扇形30度)
float Distance = Vector3.Distance(transform.position,targetPos.position);
Vector3 p2 = (transform.position + transform.rotation * Vector3.forward).normalized;
Vector3 ver3 = (targetPos.position – transform.position).normalized;
if (Distance 0.867) {
return true;
}
return false;
大神这行是什么意思啊Vector3 p2 = (transform.position + transform.rotation * Vector3.forward).normalized;
var relativePos =starttranform.position-target.position;
var rotation = Quaternion.LookRotation(relativePos);
allcamera.transform.rotation=rotation;
allcamera.transform.position=startpostion;
鱼松大大我这里有一段代码 就是移动摄像机并转动方向后我想复原摄像机的位置结果发现代码还是角度有点偏移 是我的计算有问题么
var relativePos =starttranform.position-target.position;
var rotation = Quaternion.LookRotation(relativePos);
allcamera.transform.rotation=rotation;
allcamera.transform.position=startpostion;
鱼松大大我这里有一段代码 就是移动摄像机并转动方向后我想复原摄像机的位置结果发现代码还是角度有点偏移 是我的计算有问题么
if (Mathf.Abs(t – a) <= 0.01f) 这里的0.01f是什么意思,我算出来这个值不管什么角度都是大于0.01F的
哎呀、评论呢
感谢分享, 评论框自带了智能反垃圾评论工具, 有时候会勿删。 ~
额,这。。。。。想多了吧
一个点是否在扇面上貌似只需要判定 目标点与原点的距离是否大于半径&&目标点与原点朝向的角度在扇形角度中 就可以了
第一步:以玩家为原点向玩家朝向给定一个已知距离的向量
第二步:给定扇形角度,已第一步得到的向量作为中分轴
第三步:求目标点与角色的距离得到向量并判定距离是否大于半径
第四部:由第一步和第三步的向量得到角度 Vector3.Angle()
第五步:判定角度绝对值是否大于第二步规定的扇形角度。
如果第三步和第五步都满足,就是在咯。
不知道看明白没有。。代码好像写几行就可以了。。。而且不是三角形哟!!!!
对了 第五步扇形角度记得除以2
我也这么觉得,不用化成三角形,直接用三角函数判断位置就可以确定,还比较精准。
松哥:Quaternion.Euler(0f,30f,0f) * Target.rotation这句话为什么要乘以 Target.rotation啊
我想问下如果做成三圆形的视野面积环绕主角的话该如何计算?我做了若干个点燃后连接所有的点一起,作的点越多就越像是圆,所有的点都加在一个list里了这个时候想测试该怎么办?用for loop直接一个个算过来么
我试了下,貌似可以,不过很奇怪,drawline画起来很完美没有错,计算就出错了…void StartSearching(){ find = false; Vector3 point = target.position; for (int i=0; i
囧,貌似发不了所有的code,算啦,谢谢momo大婶,我自己再琢磨琢磨
我试了下,貌似可以,不过很奇怪,drawline画起来很完美没有错,计算就出错了…void StartSearching(){find = false;Vector3 point = target.position;for (int i=0; i
1.建议代码里面加点注释,比如triangleArea(float v0x,float v0y,float v1x,float v1y,float v2x,float v2y) 这个方法的2.建议可以在图中标一下Vector3 f0 = (transform.position (r *Vector3.forward) * distance);Vector3 f1 = (transform.position (r0 *Vector3.forward) * distance);Vector3 f2 = (transform.position (r1 *Vector3.forward) * distance);比如说f0,f1,f2可以在图中标出来
1.建议代码里面加点注释,比如triangleArea(float v0x,float v0y,float v1x,float v1y,float v2x,float v2y) 这个方法的2.建议可以在图中标一下Vector3 f0 = (transform.position + (r *Vector3.forward) * distance); Vector3 f1 = (transform.position + (r0 *Vector3.forward) * distance); Vector3 f2 = (transform.position + (r1 *Vector3.forward) * distance);比如说f0,f1,f2可以在图中标出来
用Physics.OverlapSphere 做会不会很耗资源
老师。是不是能不用碰撞就不用?手游?
看完那本书,想开发游戏简直是做梦(就是小学水平),我扔了。那本书的深度还不如他写的博客。现在我去Unity Asset Store买源码,直接看别人写的。
你还想看一本书就登天了
用坐标判断有一点不好,忽略了体积因素,如果我体积很大,虽然大半个屁股都在你的剑气里面,但是我中心点不在,就被判定为打不到
那怎么判断呢。。。
你可以加一个辅佐体,这个辅佐体围绕体积旋转.永远垂直指向角色的垂直向量,用他判断坐标就行了