限定自身转动轴向的LookAt

//回头再看,发现这个方法是再造轮子

对于四元数没有理解,Quaternion的方法是一个简洁明了的方式

Quaternion.FromToRotation(Vector3 fromDir,Vector3 toDir):Quaternion

返回一个从fromDir到toDir的旋转表示

可以理解为从一个方向到另一个方向,该如何旋转

整个过程可以分解为两步:

1-炮管旋转,对准目标

2-坦克身体旋转,与坡度的法线重合

那么就是这样的一个代码:

void QuaternionRotation(){
        targetPos = target.transform.position;
        selfPos = transform.position;
        Vector3 pUp = transform.up;
        Vector3 headDir = targetPos-selfPos;
        //rotate to heading to the target
        Quaternion headRotation = Quaternion.FromToRotation(transform.forward,headDir);
        transform.rotation = headRotation*transform.rotation;
        //rotate to original transform up , that is the normal of the slope
        Quaternion bodyRotation = Quaternion.FromToRotation(transform.up,pUp);
        transform.rotation = bodyRotation*transform.rotation;
    }

//回头再看,发现这个方法是再造轮子

Unity中,transform有一个很有意思的方法:

TransForm.LookAt(Transform target, Vector3 WorldUp);

缺省时,

Transform.LookAt(Transform target);

这个方法可以理解成为:

1-目的是,使得自身的transform.forward指向目标对象

2-将自身以世界坐标的transform.up为转动轴,rotate,使得1得以实现

好比是一个人的身子绕头顶--脚心的轴转动,让自己的眼镜,看着对象。

方法很简洁实用。然而有局限性,主要在于转动轴上。设想如下一个问题:一个斜坡上有一辆坦克,需要转动炮塔,对准并不在这个斜坡上的一个物体。

上方侧视图中示意了坦克在斜面上,黄色物体在上方的水平面上。下方的透视图中,可以看到这二者的空间位置关系。

我们希望坦克用炮管对准这个黄色物体,这里的对准,其实是平面对准,让炮管的指向在坦克和物体构成的平面中。

示意一下,这个效果:

坦克旋转了,炮管指向了物体,并且这个旋转,使得坦克的transform.up方向还是垂直于所在的斜面。

先简单用LookAt来试试?

void Update () {
        transform.LookAt(target.transform);
        Debug.DrawRay(transform.position,10f*transform.forward,Color.red);
        Debug.DrawRay(transform.position,3f*transform.up,Color.green);
}

在Update中不断更新这个tranform.LookAt(),我们得到的是这样一个效果:炮管指向了物体,但是坦克旋转的轴向,并不是transform.up,而是WorldUp,显然,坦克翘起了脚,脱离了所在的斜面。

不能用简单的LookAt来实现,于是回归到基础的分析:

 

如果物体O,在坦克的水平面上,也就是图左所示。那么问题比较简单,向量AO就是向量forward需要转到的最终方向lookV,计算一下两者的夹角,按照A.transform.up作为旋转轴,旋转这个角度;至于顺时针还是逆时针,取决于两个向量的叉乘(另文已述)。

如果物体O,不在坦克的水平面上,如图右所示。A的旋转轴只能是A.transform.up,因此A的lookV,是向量AO‘,这个  O’   是O在坦克水平面上的投影,也就是说,需要计算出AO在坦克水平面的投影向量。

查找了一些资料,关于计算向量在平面上的投影向量,最靠谱的是这个:

http://zh.wikipedia.org/wiki/%E6%A0%BC%E6%8B%89%E5%A7%86%EF%BC%8D%E6%96%BD%E5%AF%86%E7%89%B9%E6%AD%A3%E4%BA%A4%E5%8C%96

那么,在unity中,这样来实现它:

1-计算A.transform.up与AO的点乘,{\mathbf  {a}}\cdot {\mathbf  {b}}=|{\mathbf  {a}}|\,|{\mathbf  {b}}|\cos \theta \;,由于A.transform.up的模长为1,

点乘得到的结果为图右中PA的长度,可以转化为向量AP:

Vector3 projectV =    Vector3.Dot(targetPos-selfPos,transform.up)*transform.up;//project to up-axis

2-AO‘就是PO’,容易得到向量AO 减去 向量AP,就可以得到

lookDir = targetPos-selfPos-projectV;

3-转化为在同一个平面中,旋转的lookV的问题了

 

void LookAtTarget(){
        targetPos = target.transform.position;
        selfPos = transform.position;
        if(targetPos != selfPos){//targetPos == selfPos, no where to look
            Vector3 projectV =    Vector3.Dot(targetPos-selfPos,transform.up)*transform.up;//project to up-axis
            lookDir = targetPos-selfPos-projectV;
            if(lookDir !=Vector3.zero){//lookDir == 0,target is up the head ,no where to look 
                Vector3 crossV = Vector3.Cross(lookDir,transform.forward).normalized;
                if(crossV !=Vector3.zero){//crossV == 0 , target is looking at ,no need to rotate
                    if(crossV == transform.up){
                        transform.Rotate(transform.up,-Vector3.Angle(lookDir,transform.forward),Space.World);                        
                    }
                    else{
                        transform.Rotate(transform.up,Vector3.Angle(lookDir,transform.forward),Space.World);
                    }
                }
            }            
        }        
    }

另外有一些细节的问题,已经在代码中注释。

 

总体来说,这个方法,由于特定的轴向旋转,而不是世界坐标轴,而其中向量点乘用于计算投影向量,向量叉乘用于计算旋转的顺时针逆时针。

转载于:https://www.cnblogs.com/tianiao/p/3545065.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值