0
\$\begingroup\$

Really need your help. I'm trying to create a custom controller for my app to allow users to resize and rotate game objects using a single drag object.

I was able to create the code to create the visuals and to rotate it but I'm stuck on how to resize the game object. To position the drag handle at the start, I'm getting the bounds of the target object and taking the bounds.extents.magnitude to determine the y position.

So I was hoping the behavior would be such that when you drag the handle closer to the center of the target object it shrinks the bounds.extents.magnitude based on the new handle position and then becomes bigger if you go further away from the center of the target object.

enter image description here

enter image description here

Here is where I am now:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CircleMaker : MonoBehaviour
{
    public float lineWidth = .1f;
    private int segments=360;
    private LineRenderer line;
    private float radius;
    private Renderer boundsSource;
    private Bounds bounds;
    public GameObject dragHandle;
    public GameObject target;
    private int pointCount;
    private Vector3[] points;
    public int angle = 0;
    public float zPosition = -8.0f;
    public bool isDraggingHandle = false;

    void Start()
    {
        line = GetComponent<LineRenderer>();
        boundsSource = target.GetComponent<Renderer>();
        bounds = boundsSource.bounds;
        line.useWorldSpace = true;
        line.startWidth = lineWidth;
        line.endWidth = lineWidth;
        line.positionCount = segments + 1;
        line.receiveShadows = false;
        line.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;

        pointCount = segments + 1;
        points = new Vector3[pointCount];

        float r = bounds.extents.magnitude;
        float d2r = Mathf.Deg2Rad * (target.transform.localEulerAngles.x * 360f / segments);
        dragHandle.transform.position = new Vector3(Mathf.Sin(d2r) * r, Mathf.Cos(d2r) * r, zPosition) + target.transform.position;

        DrawCircle();
        
    }

    void Update()
    {
        if (isDraggingHandle)
        {
            //rotate object based on drag handle location
            Vector3 direction = target.transform.position - dragHandle.transform.position;
            int sign = (direction.x >= 0) ? 1 : -1;
            float offset = (sign >= 0) ? 0 : 360;
            float angle = Vector2.Angle(Vector2.down, direction)*sign+offset;
            target.transform.rotation = Quaternion.Euler(0, 0, angle);

            
        }

        if (target.GetComponent<DragAndDrop>().isDragging == true)
        {
            dragHandle.transform.parent = target.transform;
        }
        else
        {
            dragHandle.transform.parent = null;
        }

        DrawCircle();
    }

    //draw circle around target gameobject based on bounds.extents
    void DrawCircle()
    {
        radius = bounds.extents.magnitude*target.transform.localScale.x;
        for (int i = 0; i < pointCount; i++)
        {
            float rad = Mathf.Deg2Rad * (i * 360f / segments);
            points[i] = new Vector3(Mathf.Sin(rad) * radius, Mathf.Cos(rad) * radius, zPosition) + target.transform.position;
        }

        line.SetPositions(points);
    }
}
\$\endgroup\$

2 Answers 2

1
\$\begingroup\$

fjmacs!

I created a solution for your answer; hope that's not too late.

Here you can see the whole project, with screenshots: https://github.com/burkard/ResizeHandler

If you prefer, simply use the two following scripts:

ObjectResizer.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ObjectResizer : MonoBehaviour
{
    public bool isBeingResized;
    private Bounds bounds;
    public float lineWidth = .1f;
    public float handlerSize = 0.2f;

    private int segments = 360;
    private int pointCount;
    private Vector3[] points;
    private float zPosition = 0.0f;
    private float radius;
    private LineRenderer line;

    private GameObject handler, raycastPlane;
    private float initialScale;
    private Quaternion initialRotation;

    void Start()
    {
        bounds = this.gameObject.GetComponent<Renderer>().bounds;
        isBeingResized = false;
        pointCount = segments + 1;
        initialScale = transform.localScale.x;
        initialRotation = transform.rotation;
    }

    void Update()
    {
        if (isBeingResized)
        {
            DrawCircle();
            ResizeObject();
            RotateObject();
        }
    }

    private void ResizeObject()
    {
        if (handler.GetComponent<ResizeHandler>().Scale() > 0)
            transform.localScale = Vector3.one * initialScale * handler.GetComponent<ResizeHandler>().Scale();
    }

    private void RotateObject()
    {
        transform.rotation = initialRotation;
        float rotAngle = handler.GetComponent<ResizeHandler>().Angle();
        transform.Rotate(Camera.main.transform.forward, rotAngle, Space.World);
    }


    private void OnMouseUp()
    {
        isBeingResized = !isBeingResized;
        if (isBeingResized) AddLineRenderer();
        else RemoveLineRenderer();
    }

    private void AddLineRenderer()
    {
        if (line == null)
        {
            points = new Vector3[pointCount];
            this.gameObject.AddComponent<LineRenderer>();
            line = this.gameObject.GetComponent<LineRenderer>();
            line.startWidth = lineWidth; line.endWidth = lineWidth;
            line.positionCount = pointCount;
            line.material = new Material(Shader.Find("Sprites/Default"));
            line.startColor = Color.blue; line.endColor = Color.blue;

            handler = GameObject.CreatePrimitive(PrimitiveType.Sphere);
            handler.transform.localScale = new Vector3(handlerSize, handlerSize, handlerSize);
            handler.gameObject.AddComponent<ResizeHandler>();
            handler.GetComponent<ResizeHandler>().parentCenter = transform.position;

            Vector3 cameraDirection = transform.position - Camera.main.transform.position;
            radius = bounds.extents.magnitude * this.transform.localScale.x * 1.2f;
            float rad = Mathf.Deg2Rad * (360f / segments);
            Vector3 handlerPos = new Vector3(Mathf.Sin(rad) * radius, Mathf.Cos(rad) * radius, zPosition) + this.transform.position;
            handlerPos = RotatePointAroundPivot(handlerPos, transform.position, cameraDirection);
            handler.transform.position = handlerPos;

            raycastPlane = GameObject.CreatePrimitive(PrimitiveType.Plane);
            Destroy(raycastPlane.GetComponent<MeshRenderer>());
            raycastPlane.transform.LookAt(Camera.main.transform);
            raycastPlane.transform.Rotate(new Vector3(90, 0, 0));
            raycastPlane.transform.localScale = new Vector3(1000f, 1000f, 1000f);
            raycastPlane.layer = 5;
        }
    }

    private void RemoveLineRenderer()
    {
        if (this.gameObject.GetComponent<LineRenderer>() != null)
        {
            Destroy(this.gameObject.GetComponent<LineRenderer>());
            points = null;
            Destroy(handler.gameObject);
            Destroy(raycastPlane.gameObject);
            initialScale = transform.localScale.x;
            initialRotation = transform.rotation;
        }
    }

    void DrawCircle()
    {
        Vector3 cameraDirection = transform.position - Camera.main.transform.position;

        radius = bounds.extents.magnitude * this.transform.localScale.x * 1.2f;
        for (int i = 0; i < pointCount; i++)
        {
            float rad = Mathf.Deg2Rad * (i * 360f / segments);
            points[i] = new Vector3(Mathf.Sin(rad) * radius, Mathf.Cos(rad) * radius, zPosition) + this.transform.position;
            points[i] = RotatePointAroundPivot(points[i], transform.position, cameraDirection);
        }

        line.SetPositions(points);
        line.alignment = LineAlignment.View;
    }

    Vector3 RotatePointAroundPivot(Vector3 point, Vector3 pivot, Vector3 angles){
        Vector3 dir = point - pivot; // get point direction relative to pivot
        dir = Quaternion.LookRotation(angles, Vector3.up) * dir; // rotate it
        point = dir + pivot; // calculate rotated point
        return point; // return it
    }

    public void StopResizing()
    {
        isBeingResized = false;
        RemoveLineRenderer();
    }
}

ResizeHandler.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ResizeHandler : MonoBehaviour
{
    public Vector3 parentCenter = Vector3.zero;
    private Camera mainCam;
    private float startDistance, currentScale, currentAngle;
    private Vector3 startPosition;

    private void Start()
    {
        mainCam = Camera.main;
        startDistance = (transform.position - parentCenter).magnitude;
        currentScale = (transform.position - parentCenter).magnitude / startDistance;
        startPosition = transform.position;

        startPosition = transform.position;
    }

    private void OnMouseDrag()
    {
        Ray castPoint = Camera.main.ScreenPointToRay(Input.mousePosition);
        RaycastHit hit;
        LayerMask mask = LayerMask.GetMask("UI");
        if (Physics.Raycast(castPoint, out hit, Mathf.Infinity, mask))
        {
            transform.position = hit.point;
        }
        currentScale = (transform.position - parentCenter).magnitude / startDistance;
        currentAngle = CalculateAngle();
    }

    public float Scale()
    {
        return currentScale;
    }
    public float Angle()
    {
        return currentAngle;
    }

    private float CalculateAngle()
    {
        Vector3 startVector = startPosition - parentCenter;
        Vector3 endVector = transform.position - parentCenter;
        float angle = Vector3.Angle(startVector, endVector);

        float sign = Mathf.Sign(Vector3.Dot(mainCam.transform.forward, Vector3.Cross(startVector, endVector)));
        return angle * sign;
    }


}

Good work!

\$\endgroup\$
0
\$\begingroup\$

I'll give a brief answer to complement Fabio's great but in-depth answer: you need to set target.transform.localScale of the object to change its size.

To determine the scale, you need some kind of frame to work within: locations for the start and end of the handle track so you can determine what handle position means tiny and where is full size. (Looks like you're changing the size of the circle to match the scale.)

Then you can use the ratio between where you are within the track and the limits of the track to determine the scale:

var centre = target.transform.position;
var handle = dragHandle.transform.position;
var max_radius = 2f;
var distance = Vector3.Distance(centre, handle);
target.transform.localScale = Mathf.InverseLerp(0.001f, max_radius, distance);
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .