Rotate Item Using Mouse Using Player Objects Space

Viewed 43

The title is hopefully not too bad, however the issue I'm facing seems to be related to the "space" being used and thus seems important. So in short, I'm working on a game in which the user can rotate an object using the mouse. It works to a degree only, this is the current code (currently being called upon the update function):

    float mouseX = 0, mouseY = 0;

    var delta = Mouse.current.delta.ReadValue();
    mouseX += delta.x;
    mouseY += delta.y;

    mouseX *= mouseSensitivity * Time.deltaTime;
    mouseY *= mouseSensitivity * Time.deltaTime;

    float XaxisRotation = (mouseX * rotationSpeedMultiplier) / grabbedObjectMass; 
    float YaxisRotation = ((mouseY*-1) * rotationSpeedMultiplier) / grabbedObjectMass; 
    grabbedObject.transform.Rotate(Vector3.down * XaxisRotation, Space.Self);                        
    grabbedObject.transform.Rotate(Vector3.right * YaxisRotation, Space.Self);                       

It works in the sense that I can rotate the object. However it immediately feels bad once you turn it too much and thus it no longer behaves in a relatable way. I'm using Space.Self and that means that rotation uses the objects own X,Y,Z coordinates, Space.World would use the scenes X,Y,Z as far as I understand it. This is with Space.Self using my Photoshop skills just to illustrate the problem: object specific rotation

Now what I want instead is that it will always rotate "properly" according to the players perspective or the players X/Y/Z. So no matter what side I look on, it should just rotate the same as it uses the player as space - if that makes any sense. However, the only options seem to be Space.Self and Space.World.

I've seen games do it so it's definitely possible and probably not super complicated, but I just don't know how yet. I've tried this alternative code (copied and adapted) from the Unity Docs, however it behaves exactly the same as the code above using Space.Self:

    float mouseX = 0, mouseY = 0;
    var delta = Mouse.current.delta.ReadValue();
    mouseX += delta.x;
    mouseY += delta.y;
    mouseX *= mouseSensitivity * Time.deltaTime;
    mouseY *= mouseSensitivity * Time.deltaTime;
    float XaxisRotation = (mouseX * rotationSpeedMultiplier) / grabbedObjectMass; 
    float YaxisRotation = (mouseY * rotationSpeedMultiplier) / grabbedObjectMass;
    Vector3 EulerAngleVelocity = new Vector3(YaxisRotation, XaxisRotation, 0f);
    Rigidbody rb = grabbedObject.GetComponent<Rigidbody>();

    Quaternion deltaRotation = Quaternion.Euler(EulerAngleVelocity * Time.deltaTime);
    rb.MoveRotation(rb.rotation * deltaRotation);

I honestly don't fully understand rotation, Quaternions etc. just yet. Any pointers would be appreciated!

1 Answers

You probably want to rotate in world space to limit the number of space conversions needed. The rotations you're looking for are always "from the players perspective" which loosely translates to mean "the axis of rotation are determined by the camera".

Here are two ways to think about (and code) these rotations:

using UnityEngine;

public class ObjectRotator : MonoBehaviour
{
    [SerializeField]
    private Camera camera;

    [SerializeField]
    private Transform targetTransform;

    [SerializeField]
    private Vector2 rotationSpeed;

    private Vector3 previousMousePosition;

    private void Update()
    {
        Vector3 mouseMovement = Input.mousePosition - previousMousePosition;

        if (Input.GetMouseButton(0))
        {
            //RotateObjectUsingAxis(mouseMovement);
            RotateObjectUsingOrientedEulers(mouseMovement);
        }

        previousMousePosition = Input.mousePosition;
        DrawDebugs();
    }

    private void RotateObjectUsingOrientedEulers(Vector3 mouseMovement)
    {
        // Apply mouse input as euler rotations
        // Mouse Y rotates around the X axis, Mouse X rotates around Y axis
        Vector3 scaledInput = new Vector3(mouseMovement.y, mouseMovement.x, 0);
        // Scale input by time to smooth a little
        scaledInput *= Time.deltaTime;
        // Scale input by desired rotation speeds
        scaledInput.Scale(rotationSpeed);
        // Orient the rotation amounts we've made with the camera's rotation
        Vector3 orientedRotations = camera.transform.rotation * scaledInput;
        // Apply rotation in world space
        targetTransform.Rotate(orientedRotations, Space.World);
    }

    private void RotateObjectUsingAxis(Vector3 mouseMovement)
    {
        // Mouse X rotates around axis created by camera.up
        targetTransform.Rotate(camera.transform.up * mouseMovement.x * Time.deltaTime * rotationSpeed.x, Space.World);
        // Mouse Y rotates around axis created by camera.right
        targetTransform.Rotate(camera.transform.right * mouseMovement.y * Time.deltaTime * rotationSpeed.y, Space.World);
    }

    private void DrawDebugs()
    {
        float length = 2;

        // camera axis positioned over target in world
        Debug.DrawLine(targetTransform.position, targetTransform.position + camera.transform.up * length, Color.green, Time.deltaTime);
        Debug.DrawLine(targetTransform.position, targetTransform.position + camera.transform.right * length, Color.green, Time.deltaTime);
        // this one isn't used.
        Debug.DrawLine(targetTransform.position, targetTransform.position + camera.transform.forward * length, Color.blue, Time.deltaTime);

        // if you need help visualizing the "local" axis of the target object
        //Debug.DrawLine(targetTransform.position, targetTransform.position + targetTransform.transform.up * length, Color.yellow, Time.deltaTime);
        //Debug.DrawLine(targetTransform.position, targetTransform.position + targetTransform.transform.right * length, Color.yellow, Time.deltaTime);
        //Debug.DrawLine(targetTransform.position, targetTransform.position + targetTransform.transform.forward * length, Color.red, Time.deltaTime);
    }
}

The transform.Rotate method will always rotate "in place". It doesn't change the position of the transform. A transform's orientation vectors are normalized, so they don't care about position either. That's why we don't need to worry about applying the camera's axis to the target's rotation, the target won't rotate around the camera. It rotates around the axis created by the camera.

Related