Unity——#12 第三人称视角


  在PC游戏里,第三人称视角的操纵是通过鼠标;而在主机上,则是通过右摇杆。本节实现的是类似于右摇杆操纵的第三视角,不过不是右摇杆,而是键盘的4个方向键,你问我为什么不实现鼠标操纵视角?因为它用到四元数(Quaterninos),比较复杂,日后再去研究。
  如何把camera放在第三人称视角?这其实不难,在PlayerHandle底下创建一个子的empty,命名为CameraHandle,把这个CameraHandle的位置抬到角色的脖子处,然后在往后拉到适当的地方,把已经存在的Camera拖到CameraHandle底下,那么一个标准的第三人称视角相机就ok了:


各GameObject的层级关系图


  由于Camera与PlayerHandle的父子关系,就能实现相机跟着角色走:



  但是这还不够,我们要做到的是使用方向键控制视角的变化。思路就是先在PlayerInput.cs文件里定义好控制视角变化的按键变量,并在Camera下新增一Component来实现按键与相机之间的联系。
  所以第一步就是在PlayerInput.cs里定义四个控制相机方向的变量和两个坐标轴变量(跟角色行走一样的做法,不再赘述)。
    //负责Camera移动
    [Header("===== Camera Move =====")]
    public string keyCUp = "up";
    public string keyCDown = "down";
    public string keyCLeft = "left";
    public string keyCRight = "right";

    public float CUp;
    public float CRight;

  与角色行走如出一辙,以xy坐标轴的方式将这4个string变量与这两个float变量联系起来:

        CUp = (Input.GetKey(keyCUp)?1.0f:0) - (Input.GetKey(keyCDown)?1.0f:0);
        CRight = (Input.GetKey(keyCRight)?1.0f:0) - (Input.GetKey(keyCLeft)?1.0f:0);

  第二步就是在Camera里新增组件,命名为CameraController:

CameraController

  在里面,我们要做的是,将CUpCRight与相机的旋转欧拉角联系起来。
  而对于相机的旋转,我打算通过水平旋转角色胶囊体来水平旋转相机;通过垂直旋转CameraHandle来垂直旋转相机,这样做的好处就是能分开处理两个不同平面的旋转。

  为此,我们需要在CameraController里获得cameraHandle和PlayerHandle,因为需要控制它们的旋转角,就需要获得这两个GameObject:

    private GameObject cameraHandle;
    private GameObject playerHandle;

    public PlayerInput pi;

    void Awake () {
        cameraHandle = transform.parent.gameObject;     //负责垂直旋转
        playerHandle = cameraHandle.transform.parent.gameObject;        //负责水平旋转
    }

  从上面的GameObject关系图可以看到,Camera和CameraHandle是父子关系,而CameraHandle和PlayerHandle也是父子关系,所以要在Camera下获得CameraHandle就要通过Camera的transform.parent.gameOeject来获得它的爸爸,而PlayerHandle就要通过CameraHandle的transform.parent.gameOeject获得。
  另外,为了获得CUpCRight,就要通过外部传递的方式获得PlayerHandle下的PlayerInput组件:

pi

  直接把PlayerHandle这个GameObject拖到这个框下,就会自动筛选出PlayerInput组件。
  Ok,万事俱备,只差一个函数的调用,那么什么函数能旋转游戏里的物体呢?那就是transform.Rotate(),这个函数有多个Signature,我们要用到是
public void Rotate(Vector3 axis, float angle, Space relativeTo = Space.Self);这一个。
  第一个参数(Vector3 axis)就是旋转的方向,要注意的是它这个方向指定的是绕哪个方向向量旋转,而不是向哪个方向旋转;第二个参数(float angle)是旋转的角度,这不难理解;第三个参数(Space relativeTo)是指定它在哪个坐标系下旋转,有Space.WorldSpace.Self两个选择,Space.World指的是物体在世界坐标系下旋转,而Space.Self指的是物体在自身坐标系下旋转,至于这两个坐标系有什么区别,日后有机会可以详谈一下,这里用它的默认值就行,就是Space.Self
  好,现在就用这个函数来实现水平旋转试试看:

    public float horizontalSpeed = 20.0f;
    void Update () {
        playerHandle.transform.Rotate (Vector3.up, pi.CRight * horizontalSpeed * Time.deltaTime);
    }

  水平旋转绕的是y轴,那就是Vector3.up的方向;而horizontalSpeed为的是提高它旋转的速度和找到一个适当的旋转速度;乘上Time.deltaTime就是要使得这个旋转的过程是渐进和连续的,我试过去掉Time.deltaTime,结果就是相机疯狂大角度旋转,而且是瞬移式的旋转,令人晕头转向。
  可以看看效果如何:

水平旋转

  那么我们是否可以故技重施,用这个函数实现垂直旋转呢?如果对垂直旋转的角度没有限制的话,这是可以的;如果有限制,那这个方法恐怕不能奏效,为什么?我们可以试试看:

    public float horizontalSpeed = 20.0f;
    public float verticalSpeed = 20.0f;
    void Update () {
        playerHandle.transform.Rotate (Vector3.up, pi.CRight * horizontalSpeed * Time.deltaTime);
        cameraHandle.transform.Rotate (Vector3.right, pi.CUp * verticalSpeed * Time.deltaTime);
    }


  显然这不是不对的,我们需要把垂直旋转限制在某一角度范围内,不能让角色消失在电脑屏幕中。
  我们先找一个合适的角度区间:-20到30就感觉可以了


  那么怎么把旋转角限制在某个区间呢?首先,我们要找到表示物体旋转角的变量,那就是transform.eulerAngles,eulerAngels就是欧拉角的意思,三维空间里物体的旋转角都可以用三个欧拉角来表示,所以这是个Vector3变量(public [Vector3] eulerAngles;),分别代表绕x/y/z轴旋转x/y/z度,下面是官网的描述:

Description

The rotation as Euler angles in degrees.

The x, y, and z angles represent a rotation z degrees around the z axis, x degrees around the x axis, and y degrees around the y axis.

Only use this variable to read and set the angles to absolute values. Don't increment them, as it will fail when the angle exceeds 360 degrees. Use Transform.Rotateinstead.

EulerAngels

  其次,我们需要把一个变量限制在某个范围内,那么Mathf.Clamp()能帮上忙,它的Signature有两个,分别是对应float类型和int类型,显然我们需要float类型:public static float Clamp(float value, float min, float max);,它的功能是:

Description
Clamps a value between a minimum float and maximum float value.

  把一个值限制在一个最小浮点值和最大浮点值之间。
  因为我们要限制的是绕x轴旋转的角度,所以只对transform.eulerAngles的x分量做限制:

void Update () {
        playerHandle.transform.Rotate (Vector3.up, pi.CRight * horizontalSpeed * Time.deltaTime);
        cameraHandle.transform.Rotate (Vector3.right, pi.CUp * verticalSpeed * Time.deltaTime);

        cameraHandle.transform.eulerAngles = new Vector3 (
                Mathf.Clamp (cameraHandle.transform.localEulerAngles.x, -20, 30), 
                0,0);
    }

  ok看看效果如何,


  你可能会想,啊这什么玩意儿,晃得我头晕。这个图可能有点帧数不够(我减少帧数了,避免文件太大),所以你可能看不太明白发生了什么,我来解释一下:现在相机垂直旋转能很好的限制在30度(不能大于30度),但却不能去到负值的度数,你可以看到这个X并没有出现负值,而在它到达0度时却回到了30度,所以造成了这种上下抖动的现象。为什么会这样呢?为什么没有负值出现?我们或许可以在transform.eulerAngles的官方描述中找到一点端倪,在上面我已经给到关于它的描述,里面有这么一句说明:

Only use this variable to read and set the angles to absolute values. Don't increment them, as it will fail when the angle exceeds 360 degrees.

  只能读写这个数值的绝对值,且当角度超过360度时不要去增加它们,这样做会失败。这个就是部分人对transform.eulerAngles的误区,虽然在面板上它确实能显示负值,但实际上它的内部代码实现的欧拉角范围是0-359,并没有负值,例如面板上显示-1,实际上内部表示为359。
  据此这个上下抖动就能解释得通了,在角度刚到负值时(例如-1),而它内部表示其实是359,但是现在这个transform.eulerAngles.x被Clamp了在-20到30了啊,它不能大于30啊,所以此刻它只能赋值为30。当初我想通这里的时候,真的有种醍醐灌顶的感觉。
  那么不能故技重施,那要怎么实现垂直旋转的限制呢?虽然这个transform.eulerAngles它不能连续增加直至超过360,但它是能接受负值的,它会自动转回它内部表示的正数。照这么说,虽然我们不可以限制transform.eulerAngles在一个有负值的范围内,但可以将这个限制作用于一个临时变量,而且玩家操纵视角的变化也作用于这个临时变量,然后再把这个临时变量赋给transform.eulerAngles,这样就可以实现垂直旋转的限制了。

private float tempEuler;
    void Update () {
        playerHandle.transform.Rotate (Vector3.up, pi.CRight * horizontalSpeed * Time.deltaTime);
        //cameraHandle.transform.Rotate (Vector3.right, pi.CUp * verticalSpeed * Time.deltaTime);
        tempEuler = Mathf.Clamp (tempEuler, -20, 30);
        tempEuler += pi.CUp * -verticalSpeed * Time.deltaTime;
        cameraHandle.transform.localEulerAngles = new Vector3 (tempEuler, 0, 0);
    }

  因为将这个Clamp作用于tempEuler,并不会出现-1回到359而再回到30的情况,所以这个-1会确确实实赋值给cameraHandle.transform.localEulerAngles,虽然它的内部表示是359,但是这两者的实际效果是一样的。
  至于什么使用transform.localEulerAngles而不是刚才的transform.eulerAngles,是因为我要改变的是它自己在父层坐标系的旋转角,即cameraHandle相对于PlayerHandle的旋转角,所以用的是local。
  另外一个需要注意的是在改变tempEuler时我对verticalSpeed取了负号,是因为原本是镜头垂直旋转的方向与玩家按的方向键是相反的(即键入↑,相机往下转),所以要取反处理。
  现在可以看看效果:


  现在可以看到,相机的垂直旋转角度被很好的限制在了(-20, 30)这个区间内。
  现在的总体效果是:

  现在这个第三人称视角的实现只能说是差强人意,并不是最好的。可以看到,现在当我旋转相机,人物会一起旋转,这并不是我想要,因为我只想操纵视角,并不改变人物的方向。下节将会解决这个问题,并且制造出一种人物移动时,相机在追赶人物的效果(即相机并不是固定在与人物有一段距离的某个点上)。

你可能感兴趣的:(Unity——#12 第三人称视角)