背景
最近需要做一个数字展厅,考虑到项目后期的运行,我放弃使用Threejs搭建Demo,改用了Unity实现。其中有一项功能就是需要实现人物的移动、旋转和跳跃,这里我总结了一套实现方式,分享给需要的小伙伴们。
人物预设体配置
人物预设体需要有4个部分,Capsule Collider胶囊碰撞体、相机、空对象检测体(用于检测地面)、人物模型。
Capsule Collider胶囊碰撞体
创建后命名为Player,将MeshRenderer取消勾选,这样就只会留下一个绿色的轮廓,为胶囊体添加CharacterController脚本组件。
人物模型
在胶囊体中添加人物模型,先将Position默认调成0,0,0
,再根据实际情况调整位置,确保在地面之上。
相机
在胶囊体中加入相机,放在人物的头部,用于控制人物的旋转和视角转化。
空对象检测体(用于检测地面)
在胶囊体中创建一个空对象检测体,命名为CheckGround,放在人物脚的位置,后面用于检测地面,确保不会受重力作用掉下去。这里有个快捷键curl(mac是command)+ 拖动
会有吸附效果,方便快速定位到地面。
小结
最后我们在Assets中创建一个Prefabs文件夹后,就可以将Player拖入其中,至此人物预设体就完成了。
编写人物控制脚本
点击进入预设体,我们后面编写脚本尽量都挂载在Player胶囊体上,方便查找和更改。
人物移动脚本 PlayerControllerByPc.cs
由于这里要涉及到层级Layer,就先说下层级怎么设置:将场景中主模型全选(不包括需要移动的人物模型),赋给一个Layer,最好是自定义一个,这里我自定义了一个Environment,配置的时候记得选择"Yes,change children",因为每个部分都需要检测到。
创建一个脚本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了。
2、出现可以连跳的情况:将检测半径设置小一点就行了。
结语
至此,我们就实现了比较流畅的人物控制系统了。这期先跟大家探讨电脑端的控制,后面我会写一篇移动端的人物控制系统,可以实现类似cf手游那样的感觉。有问题可以在评论区提出,小编只要看到都会回复的哟 😝 。