Unity实现人物移动、旋转、跳跃解决方案(电脑端)

2,599 阅读5分钟

背景

最近需要做一个数字展厅,考虑到项目后期的运行,我放弃使用Threejs搭建Demo,改用了Unity实现。其中有一项功能就是需要实现人物的移动、旋转和跳跃,这里我总结了一套实现方式,分享给需要的小伙伴们。

人物预设体配置

人物预设体需要有4个部分,Capsule Collider胶囊碰撞体、相机、空对象检测体(用于检测地面)、人物模型。

Capsule Collider胶囊碰撞体

创建后命名为Player,将MeshRenderer取消勾选,这样就只会留下一个绿色的轮廓,为胶囊体添加CharacterController脚本组件。

image.png

人物模型

在胶囊体中添加人物模型,先将Position默认调成0,0,0,再根据实际情况调整位置,确保在地面之上。

image.png

相机

在胶囊体中加入相机,放在人物的头部,用于控制人物的旋转和视角转化。

image.png

空对象检测体(用于检测地面)

在胶囊体中创建一个空对象检测体,命名为CheckGround,放在人物脚的位置,后面用于检测地面,确保不会受重力作用掉下去。这里有个快捷键curl(mac是command)+ 拖动会有吸附效果,方便快速定位到地面。

image.png

小结

最后我们在Assets中创建一个Prefabs文件夹后,就可以将Player拖入其中,至此人物预设体就完成了。

image.png

编写人物控制脚本

点击进入预设体,我们后面编写脚本尽量都挂载在Player胶囊体上,方便查找和更改。

人物移动脚本 PlayerControllerByPc.cs

由于这里要涉及到层级Layer,就先说下层级怎么设置:将场景中主模型全选(不包括需要移动的人物模型),赋给一个Layer,最好是自定义一个,这里我自定义了一个Environment,配置的时候记得选择"Yes,change children",因为每个部分都需要检测到。

image.png

image.png

创建一个脚本PlayerControllerByPc.cs,人物的移动速度speed、检测体检测范围半径checkRadius、检测层checkLayout、跳跃高度jumpHeight、重力gravity都是需要根据不同平台和不同场景进行适配更换的,我们将这些参数放开到编辑面板上。具体代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerControllerByPc : MonoBehaviour
{
    [Header("移动速度")]
    public float speed = 2f;
    [Header("检测范围")]
    public float checkRadius = 0.5f;
    [Header("检测层级")]
    public LayerMask checkLayout;
    [Header("跳跃高度")]
    public float jumpHeight = 5f;
    [Header("重力")]
    public float gravity = 9.8f;

    //角色控制组件
    private CharacterController characterController;
    //碰撞检测体
    private Transform checkGround;
    //是否接触地面
    private bool isGround;
    //用于计算下降的速度,x和z没什么用,默认为0
    private Vector3 velocity;

    private void Awake()
    {
        //获取角色控制组件
        characterController = GetComponent<CharacterController>();
        //获取碰撞检测体
        checkGround = transform.Find("CheckGround");
    }

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        //球型检测 checkRadius检测半径尽量调小一点,太大了会出现连跳的情况
        isGround = Physics.CheckSphere(checkGround.position, checkRadius, checkLayout);
        //如果触碰地面并且下降速度小于0,速度就不在变化,小编亲测:给小于0的值会比给0效果更好
        if(isGround && velocity.y < 0)
        {
            velocity.y = -2f;
        }
        //左右移动变化值
        float horizontal = Input.GetAxis("Horizontal") * speed * Time.deltaTime;
        //上下移动变化值
        float vertical=Input.GetAxis("Vertical") * speed * Time.deltaTime;
        //算出移动方向向量
        Vector3 moveDir = transform.forward * vertical + transform.right * horizontal;
        //人物移动
        characterController.Move(moveDir);

        //如果接触地面并且按下空格键,就给一个向上的速度,实现跳跃。
        if(isGround && Input.GetKeyDown(KeyCode.Space))
        {
            velocity.y = Mathf.Sqrt(jumpHeight * 2f * gravity);
        }
        //在重力作用下,速度不断减小,为负数时开始下降,这样赋值感觉更为真实
        velocity.y -= gravity * Time.deltaTime;
        //只完成向下的移动
        characterController.Move(velocity * Time.deltaTime);
    }
}

完成后可以编译试一下,看下会不会顺拐 😜 ,根据场景调节一下合适的参数。

相机控制脚本 CameraControllerByPc.cs

相机充当人物的头部,上下转动需要受限制,并且上下转动的时候人物不能转,左右转动需要带着人物转。由于计算机三维世界存在万向锁现象,控制相机最好不要使用欧拉角,虽然理解起来容易但是也会带来很多奇奇怪怪的现象,这里我使用四元数代替欧拉角,具体代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CameraControllerByPc : MonoBehaviour
{
    [Header("鼠标灵敏度")]
    public float mouseSensitivity = 5f;
    [Header("上下旋转最小角度")]
    public float minRotate = -70f;
    [Header("上下旋转最大角度")]
    public float maxRotate = 70f;

    //头部
    private Transform head;
    private float mouseX = 0f;
    private float mouseY = 0f;

    private void Awake()
    {
        //获取相机
        head = transform.Find("Camera");
    }

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        //获取左右转动度数
        mouseX += Input.GetAxis("Mouse X") * mouseSensitivity;
        //获取上下转动度数
        mouseY += Input.GetAxis("Mouse Y") * mouseSensitivity;
        
        //上下转动限制范围
        mouseY = Mathf.Clamp(mouseY, minRotate, maxRotate);

        //控制相机和人物左右转动,人物上下不可动
        Quaternion quaternionX = Quaternion.AngleAxis(mouseX, Vector3.up);
        Quaternion quaternionY = Quaternion.AngleAxis(0, Vector3.left);
        transform.rotation = quaternionX * quaternionY;
        //控制相机上下转动
        quaternionY = Quaternion.AngleAxis(mouseY, Vector3.left);
        head.rotation = quaternionX * quaternionY;

    }
}

这样镜头就可以转动了,相机转动灵敏度和上下转动角度范围根据具体情况调配好即可。

可能出现的问题

1、运行后会发现人没有完全站在地上:这是因为检测体占一部分空间的原因,解决办法也很简单,将人物模型相对父对象往下拉一点就ok了。

image.png

2、出现可以连跳的情况:将检测半径设置小一点就行了。

结语

至此,我们就实现了比较流畅的人物控制系统了。这期先跟大家探讨电脑端的控制,后面我会写一篇移动端的人物控制系统,可以实现类似cf手游那样的感觉。有问题可以在评论区提出,小编只要看到都会回复的哟 😝 。