Custom Runtime Transform Gizmos Is Not Working Correctly

I am trying to coding 2D transform gizmos with Rotate and Scale options. There are only two button. One is for rotate the object and another one is for scale the object in local space:

celebratedpleasantharborporpoise

If rotation z (angle) of the target shape is equal to 0, then there is no problem. But if I rotate the object, then local scaling is not working correctly (As you can see in gif).

My Code:

using UnityEngine;

public class Test : MonoBehaviour
{
    //Shape
    [Header("Shape")]
    public Transform shapeTransform;

    //Rotate
    [Header("Rotate")]
    public RectTransform rotateButtonRectTransform;
    private Vector3 rotateButtonDragOffset;
    private bool rotateButtonDown;

    //Scale
    [Header("Scale")]
    public RectTransform scaleButtonOriginRectTransform;
    public RectTransform scaleButtonRectTransform;
    public float scaleMultiplier = 0.005f;
    private Vector3 scaleButtonDragOffset;
    private Vector3 oldScale;
    private Vector3 scaleButtonPosition;
    private bool scaleButtonDown;

    //General
    public RectTransform handleTransform;

    public void OnRotateButtonDown()
    {//Firing by EventTrigger
        rotateButtonDragOffset = rotateButtonRectTransform.position - Input.mousePosition;
        rotateButtonDown = true;
    }

    public void OnRotateButtonUp()
    {//Firing by EventTrigger
        rotateButtonDown = false;
    }

    public void OnScaleButtonDown()
    {//Firing by EventTrigger
        scaleButtonDragOffset = scaleButtonRectTransform.position - Input.mousePosition;
        oldScale = shapeTransform.localScale;
        scaleButtonPosition = scaleButtonRectTransform.position;
        scaleButtonDown = true;
    }

    public void OnScaleButtonUp()
    {//Firing by EventTrigger
        scaleButtonDown = false;
        scaleButtonRectTransform.position = scaleButtonPosition;
    }

    private void Update()
    {
        //Rotate
        if (rotateButtonDown)
        {
            Vector3 targetPosition = Input.mousePosition + rotateButtonDragOffset;
            Vector3 pos = Vector3.Normalize(targetPosition - handleTransform.position);
            handleTransform.eulerAngles = new Vector3(0f, 0f, Mathf.Atan2(-pos.x, pos.y)) * Mathf.Rad2Deg;

            //Set rotation of shape
            var eulerAngles = shapeTransform.eulerAngles;
            eulerAngles.z = handleTransform.eulerAngles.z;
            shapeTransform.eulerAngles = eulerAngles;
        }

        //Scale
        if (scaleButtonDown)
        {
            scaleButtonRectTransform.position = Input.mousePosition + scaleButtonDragOffset;

            Vector3 dif = scaleButtonPosition - scaleButtonRectTransform.position;

            Vector2 scale = new Vector2(
                oldScale.x + (dif.x * scaleMultiplier),
                oldScale.y + (-1f * (dif.y * scaleMultiplier))
                );

            //Set scale of shape
            shapeTransform.localScale = new Vector3(scale.x, scale.y, 1f);
        }

        //Scale and rotate gizmo
        ScaleAndRotateGizmo();
    }

    private void ScaleAndRotateGizmo()
    {
        var distance = Vector3.Distance(scaleButtonOriginRectTransform.position, scaleButtonRectTransform.position);
        scaleButtonOriginRectTransform.localScale = new Vector3(1f, distance * 0.004f, 1f);
        Vector3 targetPosition = scaleButtonRectTransform.position;
        Vector3 pos = Vector3.Normalize(targetPosition - scaleButtonOriginRectTransform.position);
        scaleButtonOriginRectTransform.eulerAngles = new Vector3(0f, 0f, Mathf.Atan2(-pos.x, pos.y)) * Mathf.Rad2Deg;
    }
}

Well, if you’re using local scaling, shouldn’t you be using local position as well? At least when calculating the new scale, not when dragging it around.

I think local position and world position is same in this situation because the shape is in root in hierarchy:

7528946--929318--upload_2021-9-28_9-52-27.png

This is not about the position of the shape, but the position of the handle that you use to scale your object. When your shape is rotated you need to have your handle displacement in local space coordinates rather than worldspace. I guess all you may need is something like this

dif = shapeTransform.InverseTransformDirection(dif);

or maybe

dif = Quaternion.Inverse(shapeTransform.rotation) * dif;

in your line 74.

1 Like

It’s helped me. Thank you so much

You solution is working but only in 2d view or when main camera is looking to forward. In 3d space, when I changing rotation of main camera around the object/shape like orbit camera, your solution is not work. I think I need use camera direction in my script but I don’t have any idea