Unity3D中如何实现角色随摄像机指向同步调整朝向?
Unity角色移动与摄像机同步问题
问题现象
移动摄像机时,处于移动状态的角色会原地旋转,且角色的前进方向仅在左摇杆输入更新时才会变动,不符合预期。
自行尝试修复均未成功:
- 在Update生命周期中更新方向,会导致角色原地转圈
- 将更新逻辑绑定到右摇杆(控制摄像机)的触发事件中,会出现角色旋转和移动方向与预期不符的问题,仅在推动左摇杆更新角色移动时问题会临时修复
现有控制方案
- 左摇杆:移动玩家
- 右摇杆:控制摄像机
- 东侧按键:跳跃
- 北侧按键:奔跑
预期需求
移动摄像机时,角色可以同步更新自身朝向与前进方向,而非仅在左摇杆操作时才更新方向。
相关依赖
项目中影响该逻辑的Unity依赖包为:Cinemachine、Input System
受影响的移动代码
using System.Collections; using System.Collections.Generic; using UnityEngine; using Cinemachine; using UnityEngine.AI; using UnityEngine.InputSystem; public class PlayerMovement : MonoBehaviour { private DefaultControls controls; [Header("Unity General")] [SerializeField] private CharacterController controller; public Transform cameraTransform; public InputActionReference cameraControl; [Header("General Settings")]//Player movement. public bool canMovePlayer; private Vector2 currentMovementInput; private Vector3 currentMovement; private Vector3 currentRunMovement; private bool isMovementPressed; private bool isRunPressed; [Space]//Animator stuff. public Animator characterAnimator; private int isWalkingHash; private int isRunningHash; [Space]//Player running speed & how fast the player will turn when going left or right. public float rotationFactorPerFrame = 15.0f; public float runMultiplier = 3.0f; [Space]//Default gravity for when the player is falling and gravity for when the player is grounded. public float gravity = -9.81f; public float groundedGravity = -0.05f; [Space]//Playing jumping. public float initialJumpVelocity; private bool isJumpPressed = false; private float maxJumpHeight = 1f; private float maxJumpTime = 0.5f; private bool isJumping = false; private int isJumpingHash; private bool isJumpAnimating = false; private void Start() { Cursor.lockState = CursorLockMode.Locked; npcInteraction = GetComponent<NPCInteraction>(); } private void Awake() { controls = new DefaultControls(); controller = GetComponent<CharacterController>(); isWalkingHash = Animator.StringToHash("isWalking"); isRunningHash = Animator.StringToHash("isRunning"); isJumpingHash = Animator.StringToHash("isJumping"); controls.Movement.Walking.started += OnMovementInput; controls.Movement.Walking.canceled += OnMovementInput; controls.Movement.Walking.performed += OnMovementInput; controls.Movement.Run.started += OnRun; controls.Movement.Run.canceled += OnRun; controls.Movement.Jump.started += OnJump; controls.Movement.Jump.canceled += OnJump; SetupJumpVariables(); } private void SetupJumpVariables() { float timeToApex = maxJumpTime / 2; gravity = (-2 * maxJumpHeight) / Mathf.Pow(timeToApex, 2); initialJumpVelocity = (2 * maxJumpHeight) / timeToApex; } private void HandleJump() { if (!isJumping && controller.isGrounded && isJumpPressed) { characterAnimator.SetBool(isJumpingHash, true); isJumpAnimating = true; isJumping = true; currentMovement.y = initialJumpVelocity * 0.5f; currentRunMovement.y = (initialJumpVelocity + 0.5f) * 0.5f; } else if (!isJumpPressed && isJumping && controller.isGrounded) { isJumping = false; } } private void OnJump(InputAction.CallbackContext context) { isJumpPressed = context.ReadValueAsButton(); } private void OnRun(InputAction.CallbackContext context) { isRunPressed = context.ReadValueAsButton(); } private void HandleRotation() { Vector3 positionToLookAt; //Change in position our character should point to. positionToLookAt.x = currentMovement.x; positionToLookAt.y = 0.0f; positionToLookAt.z = currentMovement.z; //Current rotation of our character. Quaternion currentRotation = transform.rotation; if (currentMovementInput != Vector2.zero) { //Creates a new rotation based on where the player is currently pressing. float targetAngle = Mathf.Atan2(currentMovementInput.x, currentMovementInput.y) * Mathf.Rad2Deg + cameraTransform.eulerAngles.y; Quaternion targetRotation = Quaternion.Euler(0f, targetAngle, 0f); transform.rotation = Quaternion.Lerp(currentRotation, targetRotation, rotationFactorPerFrame * Time.deltaTime); } } private void OnMovementInput(InputAction.CallbackContext context) { currentMovementInput = context.ReadValue<Vector2>(); currentMovement = new Vector3(currentMovementInput.x, 0f, currentMovementInput.y); currentRunMovement.x = currentMovementInput.x * runMultiplier; currentRunMovement.z = currentMovementInput.y * runMultiplier; MovementDirection(); isMovementPressed = currentMovementInput.x != 0 || currentMovementInput.y != 0; } private void MovementDirection() { currentMovement = cameraTransform.forward * currentMovement.z + cameraTransform.right * currentMovement.x; currentMovement.y = 0f; currentRunMovement = cameraTransform.forward * currentRunMovement.z + cameraTransform.right * currentRunMovement.x; currentRunMovement.y = 0f; } private void HandleAnimation() { bool isWalking = characterAnimator.GetBool(isWalkingHash); bool isRunning = characterAnimator.GetBool(isRunningHash); if (isMovementPressed && !isWalking) { characterAnimator.SetBool(isWalkingHash, true); } else if (!isMovementPressed && isWalking) { characterAnimator.SetBool(isWalkingHash, false); } if ((isMovementPressed && isRunPressed) && !isRunning) { characterAnimator.SetBool(isRunningHash, true); } else if ((!isMovementPressed || !isRunPressed) && isRunning) { characterAnimator.SetBool(isRunningHash, false); } } private void HandleGravity() { bool isFalling = currentMovement.y <= 0.0f; float fallMultiplier = 1.5f; if (controller.isGrounded) { characterAnimator.SetBool(isJumpingHash, false); isJumpAnimating = false; currentMovement.y = groundedGravity; currentRunMovement.y = groundedGravity; } else if (isFalling) { float previousYVelocity = currentMovement.y; float newYVelocity = currentMovement.y + (gravity * fallMultiplier * Time.deltaTime); float nextYVelocity = (previousYVelocity + newYVelocity) * 0.5f; currentMovement.y = nextYVelocity; currentRunMovement.y = nextYVelocity; } else { float previousYVelocity = currentMovement.y; float newYVelocity = currentMovement.y + (gravity * Time.deltaTime); float nextYVelocity = (previousYVelocity + newYVelocity) * 0.5f; currentMovement.y = nextYVelocity; currentRunMovement.y = nextYVelocity; } } // Update is called once per frame public void Update() { HandleRotation(); HandleAnimation(); controller.Move(currentMovement * Time.deltaTime); characterAnimator.SetFloat("Speed", controls.Movement.Walking.ReadValue<Vector2>().magnitude); if (isRunPressed) { controller.Move(currentRunMovement * Time.deltaTime); } else { controller.Move(currentMovement * Time.deltaTime); } HandleGravity(); HandleJump(); if (cameraControl.action.triggered) { MovementDirection(); } LockOnTarget(); Interaction(); } private void OnEnable() { controls.Movement.Enable(); } private void OnDisable() { controls.Movement.Disable(); } }
内容的提问来源于stack exchange,提问作者Somiaz
相关产品推荐
相关产品推荐

