You need to enable JavaScript to run this app. Unity 自定义重力场景下如何独立于世界XYZ轴控制玩家相机旋转
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.29 13:15:03