Unity第三人称控制实现方式

第三人称移动,主要两个部分,一是人物,二是相机。

先说人物,unity其实提供了一个CharacteController组件可以方便地用于控制人物移动,但是这个组件会与刚体冲突。如果使用CharacterController,人物将不会受到力的作用(包括重力),有碰撞效果,但碰撞后不会对其他物体施加力,也就是不会把被碰撞的物体挤开,感觉不是很符合现实,所以我仍然使用rigidbody+碰撞体的组合。

首先给人物添加这两个组件,设置好碰撞体大小。
《Unity第三人称控制实现方式》
然后创建一个PlayerController脚本,用于控制人物操作,脚本如下:

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

public class PlayerController : MonoBehaviour
{ 
    private Transform cam;

    public float speed = 10f;

    public float turnSmoothTime = 0.1f;

    float turnSmoothVelocity;

    float horizontal;

    float vertical;

    public float jumpForce;

    private bool canJump = true;


    Animator ani;

    private Rigidbody rb;
    
    void Start()
    { 
        cam = Camera.main.transform;
        ani = this.GetComponent<Animator>();
        rb = this.GetComponent<Rigidbody>();
    }


    private void Update()
    { 
        if (Input.GetKeyDown(KeyCode.Space) && canJump)
        { 
            ani.SetTrigger("jump");
            this.rb.AddForce(Vector3.up * jumpForce);
            canJump = false;
        }
    }

    void FixedUpdate()
    { 
        horizontal = Input.GetAxisRaw("Horizontal");
        vertical = Input.GetAxisRaw("Vertical");
        Vector3 dir = new Vector3(horizontal, 0f, vertical).normalized;

        

        if ((dir.magnitude >= 0.1f))
        { 
            float targetAngle = Mathf.Atan2(dir.x, dir.z) * Mathf.Rad2Deg + cam.eulerAngles.y;
            float angle = Mathf.SmoothDampAngle(transform.eulerAngles.y, targetAngle, ref turnSmoothVelocity, turnSmoothTime);
            transform.rotation = Quaternion.Euler(0f, angle, 0f);

            Vector3 moveDir = Quaternion.Euler(0f, targetAngle, 0f) * Vector3.forward;

            this.rb.velocity = this.rb.velocity.y * Vector3.up + moveDir * speed;

        }
        else
        { 
            this.rb.velocity = new Vector3(0, rb.velocity.y, 0);
        }
        

        playAni();
    }

    private void LateUpdate()
    { 
        this.transform.position = this.rb.transform.position;
    }

    void playAni()
    { 
        ani.SetFloat("horizontal", Mathf.Abs(horizontal));
        ani.SetFloat("vertical", Mathf.Abs(vertical));
    }


    private void OnCollisionEnter(Collision collision)
    { 
        if (collision.gameObject.CompareTag("Ground"))
        { 
            canJump = true;
        }
    }
}

说一下里面的点,变量声明中,cam是主相机的位置,speed是人物移动速度,turnSmoothTime用于转向花的时间,turnSmoothVelocity是后面调用函数的参数,horizontal和vertical是用户输入,jumpForce跳跃力,canJump用于标记能否跳跃。

物理相关的运动应当放在FixedUpdate中,但是我把跳跃逻辑放在了Update里,具体原因移步:【Unity Tips】备忘录(扫盲篇)中的第4点。跳跃逻辑如果放在FixedUpdate里,会使得跳跃不能及时检测。检测按下空格键时,给刚体一个向上的力来跳跃。然后为了防止连续跳跃,在起跳后canJump设为false,在接触地面后回归true。

在FixedUpdate中,检测按键输入,用dir来存储这个输入,dir是一个三维向量,因为人物在平面移动只需要x和z方向,所以y值为0。判断当dir ≥ 0.1f时,执行移动相关的逻辑。计算一个targetAngle,通过Quaternion.Euler对人物坐标进行旋转,但是在游戏中人物不可能瞬间转向,因此使用Mathf.SmoothDampAngle将人物逐渐转向,转向时间则由turnSmoothTime决定。然后计算移动方向,根据这个方向设置刚体的移动速度。在每一帧的最后,将人物位置与刚体同步。playAni()用来设置animator中的参数,控制人物动画。

将这个脚本挂载到人物身上,人物的移动就完成了,接下来是相机控制的部分。关于第三人称相机控制,unity也给了一个Cinemachine,其中的FreeLook Camera比较适合第三人称,但是各种设置调整起来也挺麻烦的,我直接用代码来实现了。

创建一个CameraController脚本,挂载到相机上,代码如下:

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

public class CameraController : MonoBehaviour
{ 
    public Vector3 m_Camera;
    public Transform target;
    public float targetHeight = 1.8f;
    public float targetSide = 0.1f;
    public float distance = 4;
    public float maxDistance = 8;
    public float minDistance = 2.2f;
    public float xSpeed = 250;
    public float ySpeed = 125;
    public float yMinLimit = -10;
    public float yMaxLimit = 72;
    public float zoomRate = 80;
  	private float x = 20;
    private float y = 0;
    // Start is called before the first frame update
    void Start()
    { 
        //Cursor.lockState = CursorLockMode.Locked;
        //Cursor.visible = false;
    }

    // Update is called once per frame
    void Update()
    { 
        m_Camera.Set(Input.GetAxis("Mouse X"), Input.GetAxis("Mouse Y"), Input.GetAxis("Mouse ScrollWheel"));

        x += m_Camera.x * xSpeed * Time.deltaTime;
        y -= m_Camera.y * ySpeed * Time.deltaTime;
        y = clampAngle(y, yMinLimit, yMaxLimit);
        Quaternion rotation = Quaternion.Euler(y, x, 0);
        transform.rotation = rotation;

        distance -= (m_Camera.z * Time.deltaTime) * zoomRate * Mathf.Abs(distance);
        distance = Mathf.Clamp(distance, minDistance, maxDistance);
        transform.position = target.position + new Vector3(0, targetHeight, 0) + rotation * (new Vector3(targetSide, 0, -1) * distance);

    }

    float clampAngle(float angle, float min, float max)
    { 
        if (angle < -360)
        { 
            angle += 360;
        }
        if (angle > 360)
        { 
            angle -= 360;
        }
        return Mathf.Clamp(angle, min, max);
    }
}

m_Camera是鼠标输入,target是目标人物,targetHeight,targetSide控制相机的上下左右位置,distance控制相机与人物的距离,maxDistance和minDistance则是距离的上下限,防止相机靠得太近或离得太远,xSpeed是左右移动视角时的旋转速度,ySpeed是上下移动视角时的旋转速度,yMinLimit和yMaxLimit是控制上下移动视角的界限,zoomRate是用滚轮控制相机距离时的调整速度。

每帧先获取鼠标的输入,存进m_Camera中,根据m_Camera和xSpeed、ySpeed来计算xy值,y再通过clampAngle函数将它的值限定在设定的limit范围内,然后计算相机的旋转、与目标的距离,再结合目标位置和设置的偏移量计算相机位置。

将目标人物赋给相机的target,再简单做个人物的动画状态机,一个第三人称控制就完成了。做了个简单demo可以参考:

https://github.com/zwjzwk/Unity-Third-Person-Control

    原文作者:伊蕾娜^_^
    原文地址: https://blog.csdn.net/weixin_47260762/article/details/123903137
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞