首页 > 解决方案 > 如何在 Unity 中控制倾斜表面上的玩家相机/独立于 xyz 轴?

问题描述

我的问题/我正在努力实现的目标的说明

我设法让我的玩家利用重力在圆柱体的内表面周围移动,但在尝试旋转相机环顾四周时出现了问题。

首先,有一个脚本通过将玩家推向圆柱体的内壁来模拟重力。该脚本还通过将玩家的“向上”方向更改为始终面向圆柱体的中心来保持玩家直立。我知道这会阻止我的玩家向上看,所以现在我只是在努力让他们向左看和向右看。

其次,当玩家位于圆柱体底部并与 Y 轴平行时,我可以毫无问题地左右看,因为相机使用 x 轴旋转(参见图像左侧的圆圈)。然而,当玩家围绕圆柱体的一侧移动时,即使它们未对齐,相机仍会尝试基于 X 轴旋转,从而导致相机无法准确旋转(参见图像右侧的圆圈),并且当玩家围绕圆柱体旋转 90 度时,根本无法旋转(我认为是由于重力使玩家保持垂直于圆柱体的侧面),并且在旋转 180 度时会反转。

我假设有两种可能的解决方案我无法成功实施:

  1. 忽略世界 xyz 轴'并相对于玩家的 xyz 旋转。

  2. 当你考虑到玩家围绕圆柱体的旋转和当前“左”方向的角度时,做一些数学计算来计算出正确的角度。

第一个解决方案的问题是我无法成功地旋转独立于世界 xyz 的玩家。我尝试将 Space.Self 添加到 Rotate() 方法但没有成功。第二个问题是数学让我害怕,到目前为止我已经设法避免四元数和欧拉角,所以我什至不知道如何开始弄清楚这一点。

如果有人遇到过类似的问题,我将不胜感激有关如何解决的任何见解或建议。

这是我控制播放运动/相机方向的代码和我的重力代码:

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);
    }
}

标签: unity3d

解决方案


问题是您正试图在世界空间中的 transform.up 周围的局部空间中旋转。

尝试只使用 Vector3.up,而不是 transform.up。或者您可以使用 transform.InverseTransformDirection() 将任何向量从世界转换到本地空间

transform.Rotate(Vector3.up * mouseX * Time.deltaTime, Space.Self);

我不知道你的游戏对象的层次结构。为了让它工作,你应该旋转玩家游戏对象,使其面向圆柱体的中心。并且相机应该是玩家游戏对象的孩子


推荐阅读