人物移动:
操作规则:鼠标点击屏幕,人物移动到鼠标点击的位置。
思路:使用CharacterController组件,鼠标点击位置使用射线检测获取。
人物转身:
操作规则:人物开始移动时,将人物正面转向鼠标所点击位置的方向。
思路:使用四元数差值运算,使人物转身平滑。
人物移动
人物移动需要有一个目标位置,当鼠标点击屏幕,人物就要移动到屏幕的对应位置,因此,需要使用到射线检测。
人物在地面上行走,并且射线检测需要与地面碰撞,因此创建一个地面,并给地面设置一个标签,例如将地面的标签设置为“Ground”。
完成射线检测后,要对人物的移动方向进行判断。人物移动的目标位置就是鼠标点击之后射线与地面碰撞的位置。定义一个变量用于存放当前射线检测的位置。
要先确定人物移动的方向。定义临时变量dir,用于确定人物移动的方向。dir是一个Vector3类型变量,由目标位置减去当前位置得到。
由于CharacterController.Move()方法只需要传递一个Vector3变量,其余指令交由unity引擎完成,因此,计算得到dir并将其传给Move方法即可。至于Move方法是如何实现的,暂时不在此处详述,详细解答放在了笔记里面。
人物转身
一开始考虑直接使用transform.LookAt()方法,但是视觉效果上,人物转身特别生硬,需要有一个平滑转身的效果,因此换一种方法,使用四元数差值运算。
四元数中有一个方法可以让人物转向指定位置所在的方向,Quaternion.LookRotation(Vector3 forward, Vector3 upwards= Vector3.up)。我们使用这个方法让人物转身。在“人物移动”部分,我们得到了一个向量dir,旋转时用到这个向量,将dir传递给LookRotation()即可。
虽然视觉效果上相较于transform.LookAt()要好一些,但是这样并不能实现人物的平滑转身。查了一些资料,为何不使用transform.LookAt()而使用Quaternion.LookRotation(),这是因为四元数相较于前者更加灵活,能够与Quaternion.Lerp()配合使用实现平滑转向。transform.LookAt()适合于简单的操作,如让摄像机一直拍摄主角。
使用四元数获得了人物要转向的方向,此时就要考虑人物的平滑转身。使用差值运算Quaternion.Lerp()方法,让人物转身的速度达到一个合适的值,以此获得一个较为合理的转向效果。
另外有一些细节方面的东西,例如添加一个标志位,让旋转不是频繁地执行而是在特定条件下执行;转身的速度对转身效果的影响;转到什么程度可以表示转身完成等。这些细节在写程序时被考虑到或者在运行游戏时被考虑到。
以下是完整代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RoleCtrl : MonoBehaviour
{
/// <summary>
/// 移动的目标点
/// </summary>
private Vector3 m_TargetPos = Vector3.zero;
/// <summary>
/// 控制器
/// </summary>
[HideInInspector]
public CharacterController characterController;
/// <summary>
/// 移动速度
/// </summary>
[SerializeField] private float speed = 5;
/// <summary>
/// 角色转身的速度
/// </summary>
private float m_RotationSpeed = 0.2f;
/// <summary>
/// 转身的目标方向
/// </summary>
private Quaternion m_TargetQuaternion;
/// <summary>
/// 定义一个变量,防止转身频繁地执行。是否转身完成
/// </summary>
private bool m_RotationOver = false;
void Start()
{
characterController = GetComponent<CharacterController>();
}
private void Update()
{
//如果找不到角色控制器,则不能执行下面的代码
if (characterController == null) return;
//获取鼠标点击事件,若是在移动设备,则是获取手势。
if (Input.GetMouseButtonUp(0))
{
//获取点击的位置
//需要知道,点击屏幕时,在世界位置点击的是什么地方。
//从摄像机发射射线,和地面碰撞,碰撞的那个点就是要移动到的点。
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hitInfo;
if (Physics.Raycast(ray, out hitInfo))
{
//System.StringComparison.CurrentCultureIgnoreCase表示忽略大小写,是.net中的一个枚举类型
if (hitInfo.collider.gameObject.name.Equals("Ground", System.StringComparison.CurrentCultureIgnoreCase))
{
m_TargetPos = hitInfo.point;
m_RotationOver = false;
m_RotationSpeed = 0;
}
}
}
if (!characterController.isGrounded)
{
//让角色贴地面
characterController.Move(transform.position + new Vector3(0, -1000, 0) - transform.position);
}
//如果目标点不是原点,进行移动
if (m_TargetPos != Vector3.zero)
{
if (Vector3.Distance(m_TargetPos, transform.position) > 0.1f)
{
Vector3 direction = m_TargetPos - transform.position;
direction = direction.normalized;
direction = direction * Time.deltaTime * speed;
direction.y = 0;
//让角色缓慢转身
if (!m_RotationOver)
{
m_RotationSpeed += 5f;
//角色转身的方向
m_TargetQuaternion = Quaternion.LookRotation(direction);
//角色转身,使用四元数差值处理。速度乘上Time.deltaTime,实现平滑转身。
transform.rotation = Quaternion.Lerp(transform.rotation, m_TargetQuaternion, m_RotationSpeed * Time.deltaTime);
//判断,如果转身后的方向与目标方向夹角很小,则判定为转身已经到达目标方向。
if (Quaternion.Angle(m_TargetQuaternion, transform.rotation) < 1f)
{
m_RotationSpeed = 1;
m_RotationOver = true;
}
}
characterController.Move(direction);
}
}
}
}
——————————————————
Quaternion.LookRotation()的详细介绍
这篇文章详细地介绍了Quaternion.LookRotation()这个方法的作用以及手动实现LookRotation效果,值得研究。