一、在Mixamo网站下载人物模型和动画

选择人物模型进行下载,然后选择人物动画,在右侧可以预览效果,还能调整动画一些细节

二、将下载的人物模型和动画导入unity中,先将人物模型的动画类型设置为:Humanoid

将人物动画的Animation Type也调整为Humanoid

如果不想让动画控制人物位置变化,只通过代码控制人物位置,可以进行下图设置

三、创建空物体Player,将人物模型拖到Player下方,模型与空物体位置相同。在Player下添加组件Animator,在Project中新建一个Animator Controller,将Animator Controller拖入组件Animator,将模型的Avatar也拖入组件Animator。

进行动画切换

在Player上添加角色控制器Character Controller组件,调节Character Controller参数使其与模型大小匹配,Skin Width建议为Radius的10%,center.z建议偏人物前方一点,因为人物向前移动。

四、在Main Camera下新建脚本CameraController控制摄像机的移动,在Player新建脚本PlayerController,控制人物的移动与动画切换

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

/// <summary>
/// CameraController 控制相机跟随指定目标,并根据鼠标输入调整相机角度和位置。
/// </summary>
public class CameraController : MonoBehaviour
{
    // 目标变换对象,相机将跟随此对象移动和旋转
    [SerializeField] private Transform followTarget;
    // 相机与目标之间的距离
    [SerializeField] private float distance = 5f;
    // 旋转速度
    [SerializeField] private float rotationSpeed = 1f;
    // 垂直旋转角度的最小值和最大值
    [SerializeField] private float minVerticalAngle = -20f;
    [SerializeField] private float maxVerticalAngle = 80f;
    // 是否反转水平和垂直旋转方向
    [SerializeField] private bool invertX;
    [SerializeField] private bool invertY;
    // 框架偏移,用于调整相机相对于目标的位置
    [SerializeField] Vector2 framingOffset;

    // 水平和垂直旋转角度
    private float rotationY;
    private float rotationX;

    // 根据是否反转,计算反转值
    private float invertXVal;
    private float invertYVal;

    /// <summary>
    /// Start 初始化相机控制器,隐藏光标并锁定。
    /// </summary>
    private void Start()
    {
        Cursor.visible = false;
        Cursor.lockState = CursorLockMode.Locked;
    }

    /// <summary>
    /// Update 每帧更新相机位置和旋转,根据鼠标输入和目标位置。
    /// </summary>
    private void Update()
    {
        // 根据是否反转,计算反转值
        invertXVal = invertX ? -1 : 1;
        invertYVal = invertY ? -1 : 1;

        // 计算垂直旋转角度,并限制在最小和最大值之间
        rotationX += Input.GetAxis("Mouse Y") * rotationSpeed * invertYVal;
        rotationX = Mathf.Clamp(rotationX, minVerticalAngle, maxVerticalAngle);

        // 计算水平旋转角度
        rotationY += Input.GetAxis("Mouse X") * rotationSpeed * invertXVal;

        // 计算目标旋转
        var targetRotation = Quaternion.Euler(rotationX, rotationY, 0);

        // 计算焦点位置,根据框架偏移调整
        var focusPosition = followTarget.position + new Vector3(framingOffset.x, framingOffset.y, 0);

        // 计算相机位置,保持与目标的距离
        transform.position = focusPosition - targetRotation * new Vector3(0, 0, distance);

        // 应用目标旋转到相机
        transform.rotation = targetRotation;
    }

    /// <summary>
    /// PlanarRotation 获取水平旋转的四元数,忽略垂直旋转。
    /// </summary>
    /// <returns>水平旋转的四元数</returns>
    public Quaternion PlanarRotation => Quaternion.Euler(0, rotationY, 0);
}
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;

// 玩家控制器类,用于控制玩家角色的移动和动画
public class PlayerController : MonoBehaviour
{
    // 移动速度变量,通过Inspector界面设置
    [SerializeField] private float moveSpeed = 10f;
    // 旋转速度变量,通过Inspector界面设置
    [SerializeField] private float rotationSpeed = 500f;
    // 地面检测设置的Header,用于Inspector界面的组织
    [Header("Ground Check Settings")]
    // 地面检测半径,通过Inspector界面设置
    [SerializeField] private float groundCheckRadius = 0.2f;
    // 地面检测偏移量,通过Inspector界面设置
    [SerializeField] private Vector3 groundCheckOffset;
    // 地面检测层,通过Inspector界面设置
    [SerializeField] private LayerMask groundLayer;
    // 是否处于地面状态
    private bool isGrounded;
    // Y轴速度
    private float ySpeed;
    // 玩家动画控制器
    private Animator playerAnimator;
    // 目标旋转
    Quaternion targetRotation;
    // 相机控制器
    private CameraController cameraController;
    // 角色控制器
    private CharacterController characterController;

    // 初始化方法,用于设置相机控制器、玩家动画控制器和角色控制器的引用
    void Start()
    {
        cameraController = Camera.main.GetComponent<CameraController>();
        playerAnimator = GetComponent<Animator>();
        characterController = GetComponent<CharacterController>();
    }

    // 每帧调用的更新方法,用于处理玩家的移动、旋转和动画
    void Update()
    {
        // 获取水平和垂直输入轴的值
        float h = Input.GetAxis("Horizontal");
        float v = Input.GetAxis("Vertical");

        // 计算移动量,并进行0到1之间的限制
        float moveAmount = Mathf.Clamp01(Mathf.Abs(h) + Mathf.Abs(v));

        // 创建一个标准化的移动输入向量,以确保在对角线移动时速度不会增加
        var moveInput = (new Vector3(h, 0, v)).normalized;

        // 使用相机控制器的平面旋转来调整移动方向,以适应玩家的视角
        var moveDir = cameraController.PlanarRotation * moveInput;

        // 进行地面检测
        GroundCheck();

        // 根据是否处于地面状态来调整Y轴速度
        if (isGrounded)
        {
            ySpeed = -0.5f;
        }
        else
        {
            ySpeed += Physics.gravity.y * Time.deltaTime;
        }

        // 计算最终的移动速度,并设置Y轴速度
        var velocity = moveDir * moveSpeed;
        velocity.y = ySpeed;

        // 使用角色控制器进行移动
        characterController.Move(velocity * Time.deltaTime);

        // 如果有移动输入,则更新玩家的旋转
        if (moveAmount > 0)
        {
            transform.rotation = Quaternion.LookRotation(moveDir);
        }

        // 逐步向目标旋转靠近
        transform.rotation = Quaternion.RotateTowards(transform.rotation, targetRotation, rotationSpeed * Time.deltaTime);

        // 更新玩家动画控制器的移动量参数
        playerAnimator.SetFloat("moveAmount", moveAmount, 0.2f, Time.deltaTime);
    }

    // 地面检测方法,用于检查玩家是否处于地面
    private void GroundCheck()
    {
        isGrounded = Physics.CheckSphere(transform.TransformPoint(groundCheckOffset), groundCheckRadius, groundLayer);
    }

    // 在编辑器中绘制选中时的Gizmos,用于可视化地面检测范围
    void OnDrawGizmosSelected()
    {
        Gizmos.color = new Color(0, 1, 0, 0.5f);
        Gizmos.DrawSphere(transform.TransformPoint(groundCheckOffset), groundCheckRadius);
    }
}

五、新建一个Layer层,将地面的Layer设置为Obstacles

配置Player中Player Controller脚本的参数

配置主相机上控制相机的脚本

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐