MMORPG跟做-05-02 人物移动及人物平滑转身

人物移动:
操作规则:鼠标点击屏幕,人物移动到鼠标点击的位置。
思路:使用CharacterController组件,鼠标点击位置使用射线检测获取。

人物转身:
操作规则:人物开始移动时,将人物正面转向鼠标所点击位置的方向。
思路:使用四元数差值运算,使人物转身平滑。

以下是写代码时的思路:


人物移动

  给人物添加CharacterController组件,然后使用CharacterController.Move()方法移动。
  人物移动需要有一个目标位置,当鼠标点击屏幕,人物就要移动到屏幕的对应位置,因此,需要使用到射线检测。
  人物在地面上行走,并且射线检测需要与地面碰撞,因此创建一个地面,并给地面设置一个标签,例如将地面的标签设置为“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效果,值得研究。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值