Unity3D研究院之主角面朝方向一定区域内对象角度计算(转)

转自mono Unity3D研究院之主角面朝方向一定区域内对象角度计算(四十五) | 雨松MOMO程序研究院 (xuanyusong.com)

在上代码之前请大家跟我先做几个简单的练习题,角度向量的计算一定要学会,不然后面的东西会很难懂。

1.已知3D坐标,和一个旋转角度,以及一段距离,求目标点的3D坐标。

已知当前点为Target,目标点沿着Target的Y轴旋转30度,沿着Target的X轴延伸10米求目标点的3D坐标?

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度后向前平移。

C#

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.已知一个目标点,让模型朝着这个目标点移动。

这是一个比较简单的例子,大家应该都能看明白。

C#

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米。

在看看下面这段代码

C#

Vector3 vecn = (TargetCube.position - Target.position).normalized;

        Target.Translate(vecn *0.1f);

用向量减去一个向量求出它们的差值,normalized 是格式化向量,意思是把它们之间向量格式化到1米内。这样就可以更加精确的计算一次平移的距离了 vecn *0.1f 就标示一次平移1分米,蛤蛤。

向量不仅可以进行X Y Z轴的移动,同样可以进行旋转 ,下面这段代码就是让向量沿着Y轴旋转30度。

C#

    Vector3 vecn = (TargetCube.position - Target.position).normalized;

        vecn = Quaternion.Euler(0f,30f,0f) * vecn;

        Target.Translate(vecn *0.1f);

如果上述三道简单的练习题 你都能了然于心的话,那么本文最大的难题我相信也不会是什么难事,继续阅读吧。

假设我们需要计算主角面前5米内所有的对象时。以主角为圆心计算面前5米外的一个点,为了让大家看清楚我现将这条线绘制出来。

C#

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米以外的点。

image.png

接下来,我们需要计算主角面前的一个发散性的角度。假设主角看到的是向左30度,向右30度在这个区域。

C#

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);
}

如下图所示,这时主角面前的区域就计算出来了。看起来就是两个三角形之间的区域。

image.png

最后就是简单的套用公式,计算一个点是否在三角形内,在本文中就是计算敌人的点是否在面前的这两个三角形内。

C#

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;
    }
}

}

如下图所示,如果箱子对象是主角的视野中就会检测到。

image.png

注意,上图中我的视野选择了两个三角形,如果你需要视野目标点是椭圆形的话,那么可以多设置一些三角形。但是这样就会非常消耗效率,我觉得这里完全可以使用1个三角形,,只是正对的目标点会出现一些偏差,影响其实并不会很大。如下图所示

image.png

代码简单的修改一下即可。

C#

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;
    }
}

}

上面我们介绍了三角形判断,当然也可以通过矩形来判断是否相交。。


image.png

代码:

C#

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月补充

感谢楼下朋友给我的留言。, 我在补充一下这篇博客。

C#

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);

}

}

image.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,922评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,591评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,546评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,467评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,553评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,580评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,588评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,334评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,780评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,092评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,270评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,925评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,573评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,194评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,437评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,154评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,127评论 2 352

推荐阅读更多精彩内容