zoukankan      html  css  js  c++  java
  • unity3D:游戏分解之角色移动和相机跟随

          游戏中,我们经常会有这样的操作,点击场景中某个位置,角色自动移动到那个位置,同时角色一直是朝向那个位置移动的,而且相机也会一直跟着角色移动。有些游戏,鼠标滑动屏幕,相机就会围绕角色旋转。
    看似很简单的操作,那么到底是怎么实现的呢?
     
    我们把上述操作分解为以下几个步骤
     
    角色的移动
    1. 移动到下一个路点,线性插值、曲线插值
    2. 角色朝向,一直面朝下一个路点
     
    相机跟随角色
    1. 相机俯视角度,决定相机的高度
    2. 相机跟随距离,前向距离或者直线距离(就是三角形的水平边长或者斜边长)
    3. 相机一直看角色的后背(Y轴旋转角度和角色一致)
    4. 相机围绕角色旋转
     
    技术点:
    1. 向量
    2. 旋转
     
    先来看效果,请原谅我未注册屏幕录像orz
     
    角色移动
      包括位移和方向,就是移动角色的同时角色一直要朝向移动的方向。
     
     
     
    左边的图,角色从A移动到B,朝向却一直是向前方的,明显不符合跑动的显示逻辑。正确的表现是右图所展示那样,角色面朝移动方向。
     
    那么我们要怎么做才能实现这个效果呢?位移很简单,A到B的坐标插值。
     
    其次是旋转角色,Unity提供了一个方法Quaternion.LookRotation。关于这个方法,官方的解释如下:

    Quaternion.LookRotation 注视旋转

    static function LookRotation (forward : Vector3, upwards : Vector3 = Vector3.up) : Quaternion
    
    Description描述
    
    Creates a rotation that looks along forward with the the head upwards along upwards
    
    创建一个旋转,沿着forward(z轴)并且头部沿着upwards(y轴)的约束注视。也就是建立一个旋转,使z轴朝向view  y轴朝向up。
    
    Logs an error if the forward direction is zero.
    
    如果forward方向是0,记录一个错误。

    光看描述,是不是比较难理解。网上对这个方法的解释也挺多的,但是各说纷纭,没个简单明了的说法,更容易误导人。

    我们知道向量,包含大小和方向。大小很容易得到,那么方向怎么获得呢?常规来说,可以通过把向量分解为x、y、z三个分量,然后通过三角函数依次求得个分量的夹角。
    Unity提供了更简单的方法,就是Quaternion.LookRotation,这个方法就是获得传入向量的方向,即旋转值,是个四元数。
     
    代码实际上很简单,就几行。主要是要理解为什么
    1             //计算当前位置到下一个坐标点的向量
    2             var vector = (posB - posA).normalized;
    3             //取得向量的方向
    4             var rotation = Quaternion.LookRotation(vector).eulerAngles;
    5             //将物体旋转到指向下一个坐标点的方向
    6             transform.rotation = Quaternion.Euler(0, rotation.y, 0);
    7             //设置物体的坐标
    8             transform.position = posB;
    想想为什么Quaternion.Euler(0, rotation.y, 0)这里x和z方向都是填的0?
    因为角色的朝向是根据偏转角Yaw,也就是Y轴决定的,x和z轴是没有发生偏转的,倘若改变x轴z轴旋转值,就会发现角色会有俯仰、翻滚的效果。
     
    相机跟随角色
    好了,角色的朝向解决了。那么,如果我要让相机一直跟着角色走,同时相机一直看到角色的后背,也就是角色旋转时,相机要跟着转动,同时保持固定距离,该如何实现?
     
    我们先计算相机的位置,然后在旋转相机朝向角色的后背。
    1. 计算相机的旋转值,这里需要指定相机的俯仰角Pitch的值,假定是30度,可以根据具体情况调节
    //相机的俯仰角和偏航角,Y方向偏航和目标对象一致
    Quaternion ro = Quaternion.Euler(Pitch, transform.rotation.eulerAngles.y, 0);
    2. 计算指定长度Distance的向量,这个向量是与世界坐标z方向平行
    var vector = Vector3.forward * Distance;
    3. 用上面的相机旋转值左乘第二步得到的向量,改变这个向量的方向( 四元数左乘向量,改变向量的方向)
    vector  = ro * vector;
    4. 用目标位置减去vector,得到指向目标位置的坐标点,也就是相机的最终位置。(为什么这样就得到位置了,回去看看向量的知识吧)
    var pos = transform.position - vector; 
    5. 最后,将旋转值和坐标赋值给相机,相机就完成了跟随效果, 是不是很简单
    CameraGo.transform.position = pos;
    CameraGo.transform.rotation = ro;
    1 //相机的俯仰角和偏航角,Y方向偏航和目标对象一致
    2 Quaternion ro = Quaternion.Euler(Pitch, transform.rotation.eulerAngles.y, 0);
    3 //给向量赋予旋转
    4 var distanceVector = ro * Vector3.forward * Distance;
    5 var pos = transform.position - distanceVector;
    6 CameraGo.transform.position = pos;
    7 CameraGo.transform.rotation = ro;
     
    至于相机围绕角色旋转,我们只需要改变一下Quaternion.Euler(Pitch, transform.rotation.eulerAngles.y, 0) 中transform.rotation.eulerAngles.y这个值
    本来这个值是指定相机朝向角色的方向,我们改变这个值,就可以实现相机围绕角色的效果。我们可以这样做
    //delta就是围绕角色旋转的旋转角度0~360.
    Quaternion ro = Quaternion.Euler(Pitch, transform.rotation.eulerAngles.y + delta, 0)
     
    最终,上诉代码如下,代码不完整,请各位自行补全:
     
     1      //角色移动
     2         void SmoothMove()
     3         {
     4             Vector3[] vector3s = _transDataList;// CurvePath.PathControlPointGenerator(_transDataList);
     5             int sample = _transDataList.Length * SampleRate;
     6  
     7             _movePtg += Time.deltaTime * MoveSpeed;
     8  
     9             //曲线插值
    10             transform.position = CurvePath.Interp(vector3s, _movePtg / sample);
    11  
    12             //计算当前位置到下一个坐标点的向量
    13             var vector = (transform.position - _prevPos).normalized;
    14             //取得向量的方向
    15             var rotation = Quaternion.LookRotation(vector, Vector3.right).eulerAngles;
    16             //去处x和z方向的影响,仅作用y方向偏转
    17             rotation.x = 0;
    18             rotation.z = 0;
    19  
    20             //将物体旋转到指向下一个坐标点的方向
    21             transform.rotation = Quaternion.Euler(rotation);
    22  
    23  
    24             _prevPos = transform.position;
    25             if (_movePtg >= sample)
    26             {
    27                 ResetLocalData();
    28             }
    29         }
    30  
    31         //相机跟随
    32         void FollowCamera()
    33         {
    34             if (CameraGo == null) return;
    35  
    36             if(UseFollow != 0)
    37             {
    38                 //相机的俯仰角和偏航角,Y方向偏航和目标对象一致
    39                 Quaternion ro = Quaternion.Euler(Pitch, transform.rotation.eulerAngles.y + Slider, 0);
    40  
    41                 //给向量赋予旋转
    42                 var distanceVector = ro * Vector3.forward * Distance;
    43                 var pos = transform.position - distanceVector;
    44                 CameraGo.transform.position = pos;
    45                 CameraGo.transform.rotation = ro;
    46                 return;
    47             }
    48         }
  • 相关阅读:
    正则表达式的一些符号
    备份 Repository Backup
    Delphi 文件类型
    superobject.pas 其他人修改的
    TCustomCombo.SetItemIndex(const Value: Integer);
    Error reading symbol file
    任务栏窗口列表
    找回已删除的文件或目录
    总目录
    在Mac下使用crontab执行定时脚本
  • 原文地址:https://www.cnblogs.com/fishyu/p/6817231.html
Copyright © 2011-2022 走看看