HiddenMonk / Unity3DRuntimeTransformGizmo

A runtime transform gizmo similar to unitys editor so you can translate (move, rotate, scale) objects at runtime.
MIT License
703 stars 129 forks source link

Help collision #8

Closed craftercis closed 5 years ago

craftercis commented 5 years ago

I am impressed how well this gizmo works.

I just want to know how I can detect a collision with this gizmo. As you see in the video, my game object vibrates when it hits the wall or the ground. Also, the gizmo itself does not stop moving when the game object touches a wall or the ground. Do you know a solution for both problems? Thanks in advance!

Video: https://gph.is/2QE3RBr

HiddenMonk commented 5 years ago

Since it seems like you are using a rigidbody, I think we would need to do a Rigidbody.SweepTest before moving, see if we hit anything and then move no more than that amount. This would only work for moving the object, not rotating or scaling. I dont know a easy way of handling this for rotation, and I have no idea how I would handle scaling =/

So for example, in the IEnumerator TransformSelected method, in the "if(type == TransformType.Move)" part, we would have something like...

if(type == TransformType.Move)
{
    float moveAmount = ExtVector3.MagnitudeInDirection(mousePosition - previousMousePosition, projectedAxis) * moveSpeedMultiplier;

        float largestMoveAmount = moveAmount;

    for(int i = 0; i < targetRootsOrdered.Count; i++)
    {
        Transform target = targetRootsOrdered[i];

                Vector3 movement = axis * moveAmount;

                Rigidbody targetRB = target.GetComponent<Rigidbody>();
                if(targetRB != null)
                {
                        RaycastHit hitInfo;
                        if(targetRB.SweepTest(axis, out hitInfo, moveAmount))
                        {
                                movement = axis * hitInfo.distance;

                                //This is so if we are moving multiple objects, the gizmo will follow the object that moved the most.
                                if(hitInfo.distance > largestMoveAmount)
                                {
                                        largestMoveAmount = hitInfo.distance;
                                }
                        }
                }

        target.Translate(movement, Space.World);
    }

    SetPivotPointOffset(axis * largestMoveAmount);
}

I wrote that code here without testing it, but I think thats the general idea. I dont think I am going to put this in the code though since this wasnt really made with physics in mind. Things will probably get really messy when bringing physics into the mix =/

craftercis commented 5 years ago

do I have to place the rigidbody on the selected object or on the camera with the gizmo script? I have now put it on the selected object but unfortunately he does not do anything else

HiddenMonk commented 5 years ago

The selected object is what should have a rigidbody.

I tested the code and I had some issues that I fixed below. Keep in mind that I think there will still be some cases where this would fail, causing the object to still possibly go through the walls.

if(type == TransformType.Move)
{
    float moveAmount = ExtVector3.MagnitudeInDirection(mousePosition - previousMousePosition, projectedAxis) * moveSpeedMultiplier;

    float largestMoveAmount = moveAmount;

    for(int i = 0; i < targetRootsOrdered.Count; i++)
    {
        Transform target = targetRootsOrdered[i];

        Vector3 movement = axis * moveAmount;

        Rigidbody targetRB = target.GetComponent<Rigidbody>();
        if(targetRB != null)
        {
            //We need to add a small offset such as .001 and remove a small offset such as .002 from the distances to account for float point imprecisions.
            //Although this might work for many cases, I think there will still be some cases where the SweepTest will fail even though we expected it to succeed...

            RaycastHit hitInfo;
            if(targetRB.SweepTest(axis * Mathf.Sign(moveAmount), out hitInfo, Mathf.Abs(moveAmount) + .001f))
            {
                float safeMoveAmount = Mathf.Max(0f, hitInfo.distance - .002f) * Mathf.Sign(moveAmount);

                movement = axis * safeMoveAmount;

                //This is so if we are moving multiple objects, the gizmo will follow the object that moved the most.
                if(safeMoveAmount > largestMoveAmount)
                {
                    largestMoveAmount = safeMoveAmount;
                }
            }
        }

        target.Translate(movement, Space.World);
    }

    SetPivotPointOffset(axis * largestMoveAmount);
}
craftercis commented 5 years ago

This works! Only the problem now is it works with 2 walls. The cube stands still by every wall but the gyro moves further by 2 walls.

https://gph.is/2yb5waP

HiddenMonk commented 5 years ago

Ah, I think the problem is the line "if(safeMoveAmount > largestMoveAmount)" Try changing that to if(Mathf.Abs(safeMoveAmount) > Mathf.Abs(largestMoveAmount))

HiddenMonk commented 5 years ago

I think this is the fix for that... had to move some code around

if(type == TransformType.Move)
{
    float moveAmount = ExtVector3.MagnitudeInDirection(mousePosition - previousMousePosition, projectedAxis) * moveSpeedMultiplier;

    float largestMoveAmount = 0f;

    for(int i = 0; i < targetRootsOrdered.Count; i++)
    {
        Transform target = targetRootsOrdered[i];

        float safeMoveAmount = moveAmount;

        Rigidbody targetRB = target.GetComponent<Rigidbody>();
        if(targetRB != null)
        {
            //We need to add a small offset such as .001 and remove a small offset such as .002 from the distances to account for float point imprecisions.
            //Although this might work for many cases, I think there will still be some cases where the SweepTest will fail even though we expected it to succeed...

            RaycastHit hitInfo;
            if(targetRB.SweepTest(axis * Mathf.Sign(moveAmount), out hitInfo, Mathf.Abs(moveAmount) + .001f))
            {
                safeMoveAmount = Mathf.Max(0f, hitInfo.distance - .002f) * Mathf.Sign(moveAmount);
            }
        }

        //This is so if we are moving multiple objects, the gizmo will follow the object that moved the most.
        if(Mathf.Abs(safeMoveAmount) > Mathf.Abs(largestMoveAmount))
        {
            largestMoveAmount = safeMoveAmount;
        }

        target.Translate(axis * safeMoveAmount, Space.World);
    }

    SetPivotPointOffset(axis * largestMoveAmount);
}
craftercis commented 5 years ago

Hey I again =P.

How can I do this without the Gizmo's? I can with this code drag a gameobject but when it hits an wall or something it gose threw it in stat of stop dragging. How could I fix this?

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

public class MouseDrag : MonoBehaviour {

private Vector3 screenPoint;
private Vector3 offset;

void Start()
{
}

void Update()
{

}

void OnMouseDown()
{
    screenPoint = Camera.main.WorldToScreenPoint(transform.position);
    offset = transform.position - Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, screenPoint.z));
}

void OnMouseDrag()
{
    Vector3 curScreenPoint = new Vector3(Input.mousePosition.x, Input.mousePosition.y, screenPoint.z);
    Vector3 curPosition = Camera.main.ScreenToWorldPoint(curScreenPoint) + offset;
    transform.position = curPosition;
}
}`
HiddenMonk commented 5 years ago

Something like this

using System;
using UnityEngine;

[RequireComponent(typeof(Rigidbody))]
public class MouseDrag : MonoBehaviour
{
    Vector3 screenPoint;
    Vector3 offset;

    Rigidbody myRigidbody;

    void Awake()
    {
        myRigidbody = GetComponent<Rigidbody>();

        myRigidbody.isKinematic = true; //You dont need to do this
    }

    void OnMouseDown()
    {
        screenPoint = Camera.main.WorldToScreenPoint(transform.position);
        offset = transform.position - Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, screenPoint.z));
    }

    void OnMouseDrag()
    {
        Vector3 curScreenPoint = new Vector3(Input.mousePosition.x, Input.mousePosition.y, screenPoint.z);
        Vector3 curPosition = Camera.main.ScreenToWorldPoint(curScreenPoint) + offset;

        float moveAmount = Vector3.Distance(transform.position, curPosition);
        Vector3 direction = (curPosition - transform.position).normalized;

        float safeMoveAmount = moveAmount;
        RaycastHit hitInfo;
        if(myRigidbody.SweepTest(direction * Mathf.Sign(moveAmount), out hitInfo, Mathf.Abs(moveAmount) + .001f))
        {
            safeMoveAmount = Mathf.Max(0f, hitInfo.distance - .002f) * Mathf.Sign(moveAmount);
        }

        transform.Translate(direction * safeMoveAmount, Space.World);
    }
}