Unity 实现角色移动视角旋转以及跳跃

Posted 落英芳华

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unity 实现角色移动视角旋转以及跳跃相关的知识,希望对你有一定的参考价值。

【Unity】 实现角色移动、视角旋转以及跳跃

一、使用UGUI创建角色模型和地面

创建一个Capsule和一个Cube模型,将其放在空物体下面,命名为Player

创建一个Plane作为地面

二、在【Inspector】面板中调整Player属性

Player中添加RigidbodyCapsuleCollider组件
Transform中修改Position,将Y改为1
Rigidbody -> Constraints -> FreezeRotaion中勾选 X Y Z
CapsuleCollider中将Height属性改为2

三、为Player添加移动代码

添加PlayerContoller脚本,并拖拽到Player上,具体代码如下

Transform cameraObj;
Vector3 moveDirection;
Rigidbody rb;
//初始化
void Awake()

    cameraObj = Camera.main.transform;
    rb=GetComponent<Rigidbody>();

// Update is called once per frame
void Update()

    float h = Input.GetAxis("Horizontal");
    float v = Input.GetAxis("Vertical");

    moveDirection= cameraObj.forward*v;
    moveDirection+=cameraObj.right*h;
    moveDirection.Normalize();//归一化
    moveDirection *= 5f;//移动速度设定为5f
    moveDirection.y = 0f;//防止Player运动时倾斜

//物理更新
void FixedUpdate()

    rb.velocity = moveDirection;

运行效果如下

在Update中添加物体转向代码,旋转速度为0.3f

transform.forward = Vector3.Slerp(transform.forward, moveDirection, 0.3f);


此时物体在移动的过程中的朝向问题就解决了

四、添加角色跳跃

声明一个跳跃高度变量的

float jumpHeight;

在Awake下初始化jumpHeight

jumpHeight = 0;

调整FixedUpdate中的代码

rb.velocity = new Vector3(moveDirection.x, rb.velocity.y, moveDirection.z) + Vector3.up * jumpHeight;
jumpHeight = 0f;//初始化跳跃高度

添加跳跃方法

void Jump()

    jumpHeight = 5f;

在Update中添加键盘事件,当按下空格时触发跳跃

if (Input.GetKeyDown(KeyCode.Space))

    Jump();

恭喜你完成了本次教学,后续更新的文章多多支持

Unity 如何实现游戏Avatar角色头部跟随视角转动

文章目录


功能简介

如图所示,当相机的视角转动时,Avatar角色的头部会同步转动,看向视角的方向。

实现步骤

获取看向的位置

Avatar看向的位置即相机前方一定距离的某个坐标,该距离偏大于相机与Avatar角色的距离即可,可以取100来代表:

//获取看向的位置
private Vector3 GetLookAtPosition()

    //主相机前方100个单位的位置
    return mainCamera.transform.position + mainCamera.transform.forward * 100f;

获取头部的位置

头部位置可以通过Animator组件中的GetBoneTransform接口来获取

示例如下:

using UnityEngine;

namespace SK.Framework.Avatar

    public class HeadTrack : MonoBehaviour
    
        //动画组件
        [SerializeField] private Animator animator; 
        private Camera mainCamera; //主相机
        private Transform head; //头部

        private void Start()
        
        	mainCamera = Camera.main ?? FindObjectOfType<Camera>();
            head = animator.GetBoneTransform(HumanBodyBones.Head);
        

        //获取看向的位置
        private Vector3 GetLookAtPosition()
        
            //主相机前方100个单位的位置
            return mainCamera.transform.position + mainCamera.transform.forward * 100f;
        
    

有了头部的位置后,就可以计算头部的高度,声明一个变量headHeight来记录头部高度:

headHeight = Vector3.Distance(transform.position, head.position);

修改头部的朝向

有了看向的坐标和头部的坐标,就取得了看向的朝向,在LateUpdate中赋值该头部朝向,注意一定要使用LateUpdate,因为Animator动画组件在控制Avatar各骨骼的朝向,使用LateUpdate可以确保我们的旋转值修改起作用。

using UnityEngine;

namespace SK.Framework.Avatar

    public class HeadTrack : MonoBehaviour
    
        //动画组件
        [SerializeField] private Animator animator; 

        private Camera mainCamera; //主相机
        private Transform head; //头部
        private float headHeight; //头部的高度

        private void Start()
        
            mainCamera = Camera.main ?? FindObjectOfType<Camera>();
            head = animator.GetBoneTransform(HumanBodyBones.Head);
            headHeight = Vector3.Distance(transform.position, head.position);
        

        /// <summary>
        /// 看向某点
        /// </summary>
        /// <param name="position"></param>
        public void LookAtPosition(Vector3 position)
        
            //头部位置
            Vector3 headPosition = transform.position + transform.up * headHeight;
            //朝向
            Quaternion lookRotation = Quaternion.LookRotation(position - headPosition);

            head.rotation = lookRotation;
        
        
        private void LateUpdate()
        
            Debug.DrawLine(transform.position + transform.up * headHeight, GetLookAtPosition());
            LookAtPosition(GetLookAtPosition());
        

        //获取看向的位置
        private Vector3 GetLookAtPosition()
        
            //主相机前方100个单位的位置
            return mainCamera.transform.position + mainCamera.transform.forward * 100f;
        
    

如图所示,我们已经实现了头部的转向,但是旋转值过大会导致反人类现象,因此需要将旋转值进行限制。

限制旋转角度

//水平方向上的角度限制
[SerializeField] private Vector2 horizontalAngleLimit = new Vector2(-100f, 100f); 
 //垂直方向上的角度限制
[SerializeField] private Vector2 verticalAngleLimit = new Vector2(-60f, 60f); 

封装一个角度标准化的函数,当角度大于180度时减360度,当角度小于180度时加360度:

//角度标准化
private float NormalizeAngle(float angle)

    if (angle > 180) angle -= 360f;
    else if (angle < -180) angle += 360f;
    return angle;

封装看向某点的函数:

/// <summary>
/// 看向某点
/// </summary>
/// <param name="position"></param>
public void LookAtPosition(Vector3 position)

    //头部位置
    Vector3 headPosition = transform.position + transform.up * headHeight;
    //朝向
    Quaternion lookRotation = Quaternion.LookRotation(position - headPosition);
    Vector3 eulerAngles = lookRotation.eulerAngles - transform.rotation.eulerAngles;
    float x = NormalizeAngle(eulerAngles.x);
    float y = NormalizeAngle(eulerAngles.y);
    x = Mathf.Clamp(x, verticalAngleLimit.x, verticalAngleLimit.y);
    y = Mathf.Clamp(y, horizontalAngleLimit.x, horizontalAngleLimit.y);
    Quaternion rotY = Quaternion.AngleAxis(y, head.InverseTransformDirection(transform.up));
    head.rotation *= rotY;
    Quaternion rotX = Quaternion.AngleAxis(x, head.InverseTransformDirection(transform.TransformDirection(Vector3.right)));
    head.rotation *= rotX;

超出限制范围时自动回正

当角度超出限制的范围时,将头部自动回正,可以在GetLookAtPosition函数中加入判断,声明autoTurnback变量标识是否自动回正:

//获取看向的位置
private Vector3 GetLookAtPosition()

    Vector3 position = mainCamera.transform.position + mainCamera.transform.forward * 100f;
    if (!autoTurnback) return position;
    Vector3 direction = position - (transform.position + transform.up * headHeight);
    Quaternion lookRotation = Quaternion.LookRotation(direction, transform.up);
    Vector3 angle = lookRotation.eulerAngles - transform.eulerAngles;
    float x = NormalizeAngle(angle.x);
    float y = NormalizeAngle(angle.y);
    bool isInRange = x >= verticalAngleLimit.x && x <= verticalAngleLimit.y
        && y >= horizontalAngleLimit.x && y <= horizontalAngleLimit.y;
    return isInRange ? position : (transform.position + transform.up * headHeight + transform.forward);

加入插值运算,使自动回正时有过渡过程,代码如下:

using UnityEngine;

namespace SK.Framework.Avatar

    public class HeadTrack : MonoBehaviour
    
        
        [Tooltip("动画组件"), SerializeField] private Animator animator; 
        [Tooltip("水平方向上的角度限制"), SerializeField] private Vector2 horizontalAngleLimit = new Vector2(-70f, 70f); 
        [Tooltip("垂直方向上的角度限制"), SerializeField] private Vector2 verticalAngleLimit = new Vector2(-60f, 60f);
        [Tooltip("超出限制范围时自动回正"), SerializeField] private bool autoTurnback = true;
        [Tooltip("插值速度"), SerializeField] private float lerpSpeed = 5f;

        private Camera mainCamera; //主相机
        private Transform head; //头部
        private float headHeight; //头部的高度
        private float angleX;
        private float angleY;

        private void Start()
        
            mainCamera = Camera.main ?? FindObjectOfType<Camera>();
            head = animator.GetBoneTransform(HumanBodyBones.Head);
            headHeight = Vector3.Distance(transform.position, head.position);
        

        /// <summary>
        /// 看向某点
        /// </summary>
        /// <param name="position"></param>
        public void LookAtPosition(Vector3 position)
        
            //头部位置
            Vector3 headPosition = transform.position + transform.up * headHeight;
            //朝向
            Quaternion lookRotation = Quaternion.LookRotation(position - headPosition);
            Vector3 eulerAngles = lookRotation.eulerAngles - transform.rotation.eulerAngles;
            float x = NormalizeAngle(eulerAngles.x);
            float y = NormalizeAngle(eulerAngles.y);
            angleX = Mathf.Clamp(Mathf.Lerp(angleX, x, Time.deltaTime * lerpSpeed), verticalAngleLimit.x, verticalAngleLimit.y);
            angleY = Mathf.Clamp(Mathf.Lerp(angleY, y, Time.deltaTime * lerpSpeed), horizontalAngleLimit.x, horizontalAngleLimit.y);
            Quaternion rotY = Quaternion.AngleAxis(angleY, head.InverseTransformDirection(transform.up));
            head.rotation *= rotY;
            Quaternion rotX = Quaternion.AngleAxis(angleX, head.InverseTransformDirection(transform.TransformDirection(Vector3.right)));
            head.rotation *= rotX;
        

        //角度标准化
        private float NormalizeAngle(float angle)
        
            if (angle > 180) angle -= 360f;
            else if (angle < -180) angle += 360f;
            return angle;
        

        private void LateUpdate()
        
            LookAtPosition(GetLookAtPosition());
        

        //获取看向的位置
        private Vector3 GetLookAtPosition()
        
            Vector3 position = mainCamera.transform.position + mainCamera.transform.forward * 100f;
            if (!autoTurnback) return position;
            Vector3 direction = position - (transform.position + transform.up * headHeight);
            Quaternion lookRotation = Quaternion.LookRotation(direction, transform.up);
            Vector3 angle = lookRotation.eulerAngles - transform.eulerAngles;
            float x = NormalizeAngle(angle.x);
            float y = NormalizeAngle(angle.y);
            bool isInRange = x >= verticalAngleLimit.x && x <= verticalAngleLimit.y 
                && y >= horizontalAngleLimit.x && y <= horizontalAngleLimit.y;
            return isInRange ? position : (transform.position + transform.up * headHeight + transform.forward); 
        
    

如何让指定动画不受影响

如果我们想要在播放某个动画时不让头部转动,可以通过Tag标签来解决,如下图所示,为Hi动画增加IgnoreHeadTrack标签:


在代码中加入判断:

//获取看向的位置
private Vector3 GetLookAtPosition()

    AnimatorStateInfo animatorStateInfo = animator.GetCurrentAnimatorStateInfo(0);
    if (animatorStateInfo.IsTag("IgnoreHeadTrack"))
        return transform.position + transform.up * headHeight + transform.forward;

    Vector3 position = mainCamera.transform.position + mainCamera.transform.forward * 100f;
    if (!autoTurnback) return position;
    Vector3 direction = position - (transform.position + transform.up * headHeight);
    Quaternion lookRotation = Quaternion.LookRotation(direction, transform.up);
    Vector3 angle = lookRotation.eulerAngles - transform.eulerAngles;
    float x = NormalizeAngle(angle.x);
    float y = NormalizeAngle(angle.y);
    bool isInRange = x >= verticalAngleLimit.x && x <= verticalAngleLimit.y
        && y >= horizontalAngleLimit.x && y <= horizontalAngleLimit.y;
    return isInRange ? position : (transform.position + transform.up * headHeight + transform.forward);

完整代码:

using UnityEngine;

namespace SK.Framework.Avatar

    public class HeadTrack : MonoBehaviour
    
        
        [Tooltip("动画组件"), SerializeField] private Animator animator; 
        [Tooltip("水平方向上的角度限制"), SerializeField] private Vector2 horizontalAngleLimit = new Vector2(-70f, 70f); 
        [Tooltip("垂直方向上的角度限制"), SerializeField] private Vector2 verticalAngleLimit = new Vector2(-60f, 60f);
        [Tooltip("超出限制范围时自动回正"), SerializeField] private bool autoTurnback = true;
        [Tooltip("插值速度"), SerializeField] private float lerpSpeed = 5f;

        private Camera mainCamera; //主相机
        private Transform head; //头部
        private float headHeight; //头部的高度
        private float angleX;
        private float angleY;

        private void Start()
        
            mainCamera = Camera.main ?? FindObjectOfType<Camera>();
            head = animator.GetBoneTransform(HumanBodyBones.Head);
            headHeight = Vector3.Distance(transform.position, head.position);
        

        /// <summary>
        /// 看向某点
        /// </summary>
        /// <param name="position"></param>
        public void LookAtPosition(Vector3 position)
        
            //头部位置
            Vector3 headPosition = transform.position + transform.up * headHeight;
            //朝向
            Quaternion lookRotation = Quaternion.LookRotation(position - headPosition);
            Vector3 eulerAngles = lookRotation.eulerAngles - transform.rotation.eulerAngles;
            float x = NormalizeAngle(eulerAngles.x);
            float y = NormalizeAngle(eulerAngles.y);
            angleX = Mathf.Clamp(Mathf.Lerp(angleX, x, Time.deltaTime * lerpSpeed), verticalAngleLimit.x, verticalAngleLimit.y);
            angleY = Mathf.Clamp(Mathf.Lerp(angleY, y, Time.deltaTime * lerpSpeed), horizontalAngleLimit.x, horizontalAngleLimit.<

以上是关于Unity 实现角色移动视角旋转以及跳跃的主要内容,如果未能解决你的问题,请参考以下文章

Unity 如何实现游戏Avatar角色头部跟随视角转动

Unity第一人称视角开发

Unity第一人称视角开发

Unity中,3D角色的移动(斜面上)、跳跃、冲刺

请问Unity3D中,第三人称视角使用角色控制器的Move函数移动时,怎么实现由鼠标左右移动控制运动朝向?

Unity实现2D平面游戏角色跳跃