Unity 自定义重力场景下如何独立于世界XYZ轴控制玩家相机旋转
我的问题及目标实现效果示意图
我已通过自定义重力逻辑实现玩家在圆柱内壁行走,但尝试旋转相机切换视角时出现异常。
首先,我编写了重力模拟脚本,通过施加力将玩家推向圆柱内壁,同时该脚本会调整玩家的“上”方向,使其始终指向圆柱中心,保证玩家直立。我已知该逻辑会限制玩家向上仰视的能力,因此当前仅尝试实现左右视角切换功能。
其次,当玩家处于圆柱底部、与世界Y轴平行时,绕X轴旋转即可正常切换左右视角(见示意图左侧圆圈标注);但当玩家移动到圆柱侧面时,相机仍尝试基于世界X轴旋转,与玩家当前朝向不匹配,导致视角旋转精度异常(见示意图右侧圆圈标注):当玩家绕圆柱移动90°时,相机完全无法旋转(推测是重力逻辑使玩家与圆柱侧壁垂直导致),当玩家绕圆柱移动180°时,视角旋转方向完全反转。
我梳理了两种可行的解决思路,但均未成功实现:
- 脱离世界XYZ轴坐标系,基于玩家自身的XYZ坐标系进行旋转。该思路的问题是我无法实现玩家独立于世界坐标系的旋转,已尝试在
Rotate()方法中添加Space.Self参数但未生效。 - 结合玩家绕圆柱的旋转角度、当前“左”方向的夹角计算出正确的旋转角度。该思路的问题是我不擅长数学,且至今一直尽量避开
Quaternion、欧拉角相关内容,不知从何入手。
如果有开发者遇到过同类问题,恳请分享相关思路或可行方案。以下是我当前使用的玩家移动/相机控制代码、重力逻辑代码:
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.InputSystem; public class PlayerController : MonoBehaviour { InputManager inputActions; InputManager.PlayerMovementActions playerMovement; public GravityAttractor GravityAttractor; public float moveSpeed = 15; public float jumpHeight = 10f; public float sensitivityX = 1f; public float sensitivityY = 1f; float mouseX, mouseY; Vector2 mouseInput; private bool isJumping = false; private Vector3 moveDir; private Vector2 moveInput; private Rigidbody rbody; private void Awake() { inputActions = new InputManager(); playerMovement = inputActions.PlayerMovement; playerMovement.Movement.performed += context => moveInput = context.ReadValue<Vector2>(); playerMovement.Jump.performed += ctx => Jump(); //playerMovement.MouseX.performed += context => mouseInput = context.ReadValue<Vector2>(); playerMovement.MouseX.performed += context => mouseInput.x = context.ReadValue<float>(); playerMovement.MouseY.performed += context => mouseInput.y = context.ReadValue<float>(); rbody = GetComponent<Rigidbody>(); isJumping = false; } private void Update() { moveDir = new Vector3(moveInput.x, 0, moveInput.y).normalized; if (rbody.velocity.y == 0) isJumping = false; } private void FixedUpdate() { rbody.MovePosition(rbody.position + transform.TransformDirection(moveDir) * moveSpeed * Time.deltaTime); MouseLook(mouseInput); } void Jump() { //use brackeys method of checking for contact with ground if(isJumping == false && rbody.velocity.y == 0) { isJumping = true; rbody.velocity = transform.up * jumpHeight; Debug.Log("player jumped"); } } void MouseLook(Vector2 mouseInput) { mouseX = mouseInput.x * sensitivityX; mouseY = mouseInput.y * sensitivityY; var upTransform = GravityAttractor.transform.position - transform.position; Vector3 relativeLook = upTransform - transform.forward; Vector3 qLook = transform.forward - transform.position; transform.Rotate(transform.up * mouseX * Time.deltaTime, Space.Self); //transform.Rotate(relativeLook.normalized); //transform.rotation = Quaternion.LookRotation(qLook); //transform.Rotate(relativeLook.normalized, mouseX); } private void OnEnable() { inputActions.Enable(); } private void OnDisable() { inputActions.Enable(); } }
using System.Collections; using System.Collections.Generic; using UnityEngine; public class GravityAttractor : MonoBehaviour { public float gravityMultiplier = -10f; private float radius; public float gravity; public float distance; private void Awake() { radius = transform.localScale.x/2; } public void Attract(Transform body) { Vector3 centerOfGravity = new Vector3(transform.position.x, transform.position.y, body.position.z); distance = Vector3.Distance(centerOfGravity, body.position); //gravity will match multiplier no matter the radius of cylinder gravity = gravityMultiplier * (distance/radius); Vector3 gravityUp = (centerOfGravity - body.position).normalized; Vector3 bodyUp = body.up; body.GetComponent<Rigidbody>().AddForce(gravityUp * gravity); Quaternion targetRotation = Quaternion.FromToRotation(bodyUp, gravityUp) * body.rotation; body.rotation = Quaternion.Slerp(body.rotation, targetRotation, 50 * Time.deltaTime); } }
内容的提问来源于stack exchange,提问作者very_human
相关产品推荐
相关产品推荐

