上一次我们讲述了如何在球上运动并且始终朝向一点,其实之前的代码是有BUG的,因为我们点击的方向不同,他的转向也会不同,所以我们可能会+angle,也可能-angle。我会在这一章的例子中把之前的问题顺便阐述清楚。

我们之前做的是始终朝向一点运动,那么,我们是否能朝向我们所运动的方向呢?似乎这样更符合我们的认知。还有,之前在球上的操作是鼠标长按,鼠标在哪小人就在哪,那么我们是否可以实现点击球上一点,让小人自己走过去呢?今天就来解决这些问题。

通过射线碰撞获取碰撞点,直接把碰撞点的位置赋给小人,这样显然有些操作上的不方便,那么我们现在点击一点,小人在球面上运动到目标点。如何实现呢?我们有这样一种思路:


unity3d 方向指示 unity指向目标_unity3d 方向指示




如图,我们正常是从直线的一端走向另一端,但是我们现在可以先计算出从起始点到目标点的直线移动坐标变化,每帧记录下这些坐标,然后每帧发射射线指向这些坐标,与球面碰撞的位置就是我们要在曲面上运动的轨迹,有了这个思路,我们的移动问题就解决了。


接下来就是文章一开头提到的转向问题:




unity3d 方向指示 unity指向目标_球_02




如图,我们之前只考虑到一种转向问题,就是如图曲线的方向,这只是在面朝自己的这一面向下走时应该转的方向。


首先我们必须明确的一点是小人是有父对象的,并且父对象的正方向始终不变,为的就是和正确方向计算得出正确转向,那么我们的问题就来了,既然父物体对象正方向一直不变,怎么可能始终让子物体朝着一个方向转动呢,我们的角度一直都是非负的!不可能自动调整。所以我们就必须分情况讨论:


首先我们确定一个前提,就是父物体的x轴始终朝向我们,这样方便我们描述方向


1.面朝我们的球的一面:


面朝我们,就拿刚才第二个图来说,此时我们看到的球就是面朝着我们的一面。在这一面上,如果我们点击让小人往下走,就必须让小人顺时针旋转;如果往上走,就必须让小人逆时针旋转。


2.背朝我们这面:

与面朝我们这一面刚好相反,往下走逆时针,往上走顺时针。


这样,我们上一章的问题也理清了,知道了解决的方案,代码也就一起给上:


using    System.Collections;
  
using    System.Collections.Generic;
  
using    UnityEngine;
    
public    class 
   MoveOnSphere 
   : 
   MonoBehaviour 
   {
  
       private 
   Vector3 
   desPos;
  
       private 
   bool 
   isMove; 
  //是否移动
  
       private 
   Vector3 
   currentPos;
  
       private 
   RaycastHit 
   hit;
  
       public 
   GameObject 
   sphere;
  
   
  
           void 
   Update () {
  
           if 
   ( 
  Input 
  .GetMouseButtonDown(0))
  
        {
  
               Ray 
   ray = 
   Camera 
  .main.ScreenPointToRay( 
  Input 
  .mousePosition);           
  
               if 
   ( 
  Physics 
  .Raycast(ray, 
   out 
   hit, 1000, 1 << 8))
  
            {
  
                desPos = hit.point;
  
                isMove =    true 
  ;
  
                   Debug 
  .Log( 
  "开始移动" 
  );
  
            }
  
        }
  
           if 
   (isMove)
  
        {
  
               //获取每帧移动时当前的点
  
            currentPos =    Vector3 
  .MoveTowards(transform.position,desPos,0.1f);
  
               Debug 
  .Log(currentPos);
  
               //每帧发射的射线
  
               Ray 
   rayEveryFrame = 
   new 
   Ray 
  ( 
  Camera 
  .main.transform.localPosition, (currentPos - 
   Camera 
  .main.transform.localPosition).normalized);
  
               //发射射线
  
               if 
   ( 
  Physics 
  .Raycast(rayEveryFrame, 
   out 
   hit, 1000, 1 << 8))
  
            {         
  
                   //求当前点的法线
  
                     Vector3 
   normal =  (transform.position - sphere.transform.position).normalized;
  
                   //次切线
  
                     Vector3 
   binormal = 
   Vector3 
  .Cross(normal,desPos-sphere.transform.position).normalized;
  
                   //切线
  
                     Vector3 
   tangent = 
   Vector3 
  .Cross(binormal,normal).normalized;                 
  
                  transform.parent.position = hit.point;
  
                  transform.parent.up = normal;
  
                   //计算父级物体正方向和切线的夹角
  
                     float 
   angle = 
   Vector3 
  .Angle(transform.parent.forward, tangent);
  
                   //将子物体的方向矫正
  
                   //要分在物体上方点击还是下方点击来判断子物体应该往那边偏移
  
                     if 
   (transform.position.x > sphere.transform.position.x)
  
                  {
  
                         if 
   (desPos.y > transform.position.y)
  
                      {
  
                          transform.localEulerAngles =    new 
   Vector3 
  (0, transform.parent.localEulerAngles.y - angle, 0);
  
                      }
  
                         else
  
                      {
  
                          transform.localEulerAngles =    new 
   Vector3 
  (0, transform.parent.localEulerAngles.y + angle, 0);
  
                      }
  
                  }
  
                     else
  
                  {
  
                         if 
   (desPos.y > transform.position.y)
  
                      {
  
                          transform.localEulerAngles =    new 
   Vector3 
  (0, transform.parent.localEulerAngles.y + angle, 0);
  
                      }
  
                         else
  
                      {
  
                          transform.localEulerAngles =    new 
   Vector3 
  (0, transform.parent.localEulerAngles.y - angle, 0);
  
                      }
  
                  }
  
                 
    
            }
  
               if 
   ( 
  Vector3 
  .Distance(transform.position, desPos) < 0.1f)
  
            {
  
                isMove =    false 
  ;
  
            }
  
        }
  
        }
  
}


如果还有BUG,或者有什么疑问,可以留言;我会在第一时间与大家讨论!感谢大家的关注:)