You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity 3D中为角色控制器添加相机相对移动机制求助

相机相对移动实现方案

针对你的需求,核心是让角色的移动方向基于相机的水平朝向,而非自身朝向。以下是修复后的完整代码及关键修改说明:

问题根源

  1. 原代码移动逻辑直接使用角色自身的transform.forward,未关联相机朝向
  2. 未正确保存相机引用,且之前尝试的四元数旋转逻辑可能错误控制了相机(Cinemachine自由相机应独立处理旋转,角色无需干预)
  3. 移动与冲刺逻辑分离,导致冲刺时未遵循相机相对规则

修复后的完整代码

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

public class PlayerController : MonoBehaviour
{
    [SerializeField] private float _playerSpeed = 5f;
    [SerializeField] private float _sprintMultiplier = 1.8f;
    [SerializeField] private Rigidbody rb;

    [SerializeField] private float _jumpforce = 100f;
    [SerializeField] private float fallMultiplier = 2.5f;
    [SerializeField] private float lowJumpMultiplier = 2f;
    public bool isGrounded;

    public bool jumpReady;
    public float jumpcoolDownTimer = 1.5f;
    public float jumpcoolDownCurrent = 0.0f;

    private Camera _mainCamera;
    private Vector3 _moveDirection;

    int isJumpingHash;
    int isFallingHash; 
    int isLandingHash;

    public Animator animator;
    Vector3 vel;

    void Start()
    {
        jumpcoolDownCurrent = jumpcoolDownTimer;

        Cursor.visible = false;
        Cursor.lockState = CursorLockMode.Locked;

        animator = GetComponent<Animator>();
        _mainCamera = Camera.main;

        // 哈希值优化动画参数
        isJumpingHash = Animator.StringToHash("isJumping");
        isFallingHash = Animator.StringToHash("isFalling");
        isLandingHash = Animator.StringToHash("isGrounded");
    }

    void Update()
    {
        Move();
        Jump();
    }

    private void OnCollisionEnter(Collision collision){
        if(collision.gameObject.tag == "Surface"){
            isGrounded = true;
        }
    }

    private void Move()
    {
        // 获取输入轴
        float horizontalInput = Input.GetAxis("Horizontal");
        float verticalInput = Input.GetAxis("Vertical");

        // 基于相机朝向计算移动方向,忽略Y轴保持水平
        Vector3 cameraForward = _mainCamera.transform.forward;
        Vector3 cameraRight = _mainCamera.transform.right;
        cameraForward.y = 0f;
        cameraRight.y = 0f;
        cameraForward.Normalize();
        cameraRight.Normalize();

        // 计算最终移动方向
        _moveDirection = (cameraForward * verticalInput + cameraRight * horizontalInput).normalized;

        // 应用移动速度,区分冲刺状态
        float currentSpeed = Input.GetKey(KeyCode.LeftShift) && verticalInput > 0 ? _playerSpeed * _sprintMultiplier : _playerSpeed;
        vel = _moveDirection * currentSpeed;

        // 当有移动输入时,让角色朝向移动方向
        if (_moveDirection.magnitude > 0.1f)
        {
            Quaternion targetRotation = Quaternion.LookRotation(_moveDirection);
            transform.rotation = Quaternion.Lerp(transform.rotation, targetRotation, Time.deltaTime * 10f);
        }
    }

    private void Jump(){
        // 保留Y轴速度,避免覆盖跳跃/下落速度
        vel.y = rb.velocity.y;
        rb.velocity = vel;

        // 跳跃冷却逻辑
        jumpcoolDownCurrent += Time.deltaTime;
        jumpReady = jumpcoolDownCurrent >= jumpcoolDownTimer;

        bool isJumping = animator.GetBool(isJumpingHash);
        bool isFalling = animator.GetBool(isFallingHash);

        // 跳跃触发
        if(Input.GetKeyDown(KeyCode.Space) && isGrounded && jumpReady){
            rb.AddForce(Vector3.up * _jumpforce, ForceMode.Impulse);
            isGrounded = false;
            jumpcoolDownCurrent = 0.0f;
            animator.SetBool(isJumpingHash, true);
        }
        // 下落加速
        if(rb.velocity.y < 0 && !isGrounded){
            rb.velocity += Vector3.up * Physics.gravity.y * (fallMultiplier - 1) * Time.deltaTime;
            animator.SetBool(isFallingHash, true);
        }
        // 落地重置动画
        if(isGrounded){
            animator.SetBool(isJumpingHash, false);
            animator.SetBool(isFallingHash, false);
            animator.SetBool(isLandingHash, true);
            
            // 延迟重置落地动画(根据你的动画长度调整)
            Invoke(nameof(ResetLandingAnim), 0.3f);
        }
    }

    private void ResetLandingAnim()
    {
        animator.SetBool(isLandingHash, false);
    }
}

关键修改说明

  1. 相机引用保存:将Camera.main保存为成员变量_mainCamera,避免重复调用的性能损耗
  2. 相机相对方向计算:
    • 提取相机的前向和右向向量,强制Y轴为0确保水平移动
    • 通过输入轴与相机向量的组合,得到相机相对的移动方向
  3. 角色旋转逻辑:当有移动输入时,让角色平滑转向移动方向,符合第三人称游戏的常规表现
  4. 冲刺逻辑统一:冲刺状态下同样使用相机相对的移动方向,避免逻辑分裂
  5. 动画状态优化:修复落地动画的重置逻辑,避免状态异常

额外注意事项

  • 确保Cinemachine自由相机的Body设置为Framing Transposer,Aim设置为POV,让相机独立处理旋转,角色无需控制相机
  • 检查地面检测逻辑,建议使用OnCollisionStay或射线检测替代OnCollisionEnter,避免因碰撞检测丢失导致isGrounded状态错误

内容的提问来源于stack exchange,提问作者PrimoCop

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 07:35:23