Move object with the mouse in a fluid way keeping the physics

Hi everybody !

I have recently been developing a 3D furniture program with Unity, I had never developed under Unity before but I understand the concept rather well ^^
I block on a feature (fundamental in my project) that I would like to develop:
I wish I could move a GameObject on the floor of a room (Axes X and Z so, Y remains fixed) with the mouse, for that I use the ScreenPointToRay function to convert the position of my mouse to a Ray, I check then if it touches an obstacle (in this case the ground) to then recover the position of the impact, I then move the GameObject following the position of point.x and point.z with MovePosition

Here is my code:

void OnMouseDrag()
    {
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        RaycastHit hit;
        if (Physics.Raycast(ray, out hit, Camera.main.farClipPlane, 1 << LayerMask.NameToLayer("Floor")))
        {
                GetComponent<Rigidbody>().MovePosition(new Vector3(hit.point.x, 0.12f, hit.point.z));
        }
    }

My concern is that no matter what function I use for my move, be it transform.position, MovePosition, Rigidbody.position etc … none of these moves respect physics and my GameObjects can thus penetrate one into the other or cross the walls …

I tried to do the same function with AddForce but the problem is that the GameObject’s movement is not as smooth as with a MovePosition

Here is the code:

void OnMouseDrag()
    {
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        RaycastHit hit;
        if (Physics.Raycast(ray, out hit, Camera.main.farClipPlane, 1 << LayerMask.NameToLayer("Floor")))
        {
                screenPoint = new Vector3(hit.point.x, 0.12f, hit.point.z);
                GetComponent<Rigidbody>().AddForce((screenPoint - transform.position).normalized * 5000 * Time.deltaTime);
        }

(I obviously tried to change Mass or Drag my GameObject, but it’s either too fast or too slow, since the speed of the mouse varies of course …)

Someone would have an idea to solve my problem?

thank you in advance

I can kind of understand wanting to not go through things, even if the game is moving furniture.
You might try raycasting from your current object’s position to its next position to see if the space is clear (or a box cast maybe if that sounds better).
This could probably work decently, but in case you ever had an issue, you could maybe store the last valid position and revert if a trigger occurs against some colliders that shouldn’t.

If you are looking to make it so they can go through other things (excluding probably walls), but that they come back out on their own, that’s something else. Like could I drag a chair through a couch, but just not leave it stuck in there, for example? I don’t know your goals. :slight_smile:

My objective is simple, I want to be able to move a piece of furniture with the mouse in a fluid way but respecting a maximum the laws of the physics, so no piece of furniture must cross neither the walls nor anything else
But how would you go about it to know the next position of the object?
you say that you have to set up a trigger to return to the last valid position but I do not think it is very correct visually no?
Thanks

Well, no restoring the position wouldn’t be perfect (but it would only be one frame behind).
That was an additional option to add on, though, not the entire thing of my post.

As for how you’d know where you’re moving, it’s right there in your code. You’re setting it in MovePosition with the drag. So, if you do an additional cast of some sort between where you are and where you want to go, you can maybe find out if you’ve hit something, and if so, abort the drag/not move the furniture.

what i need to do is put an if that will do a raycast on each side on the position of my hit.point.x and hit.point.z to find out if i am going to encounter an obstacle, if where is my mouse is an object close then no MovePosition, otherwise yes
That’s right ?

Sorry for the French :smile:

I’m 99% sure. Did you forget we were speaking English before? :slight_smile: smiles.

I’m going to try this and I’m coming back to you
Thank you !

Ah, okay. I read your french and I missed a little bit of the translation in my head. I would suggest that you use a raycast between where you are and where you would be (the raycast spot). If you hit something, don’t allow a move.

First just test that that stops “some moves”. I say some, because obviously a ray is like a line so it won’t detect everything. But just try it to start, then move to a different cast, like box or sphere or something.

I already use OverlapSphere later in my code but the problem is that it does not tell me how far the collision is, while with the raycast I can get the distance, do you have any advice for that?

Well, you only need the area between where you are and where you’re going (not going to be very big/far!).
OverlapSphere is another option. Basically just use whichever one sounds the most sensible to you and that works best.

Were you able to test anything with just the raycast to prevent movement?

I test it and I come back to you! :slight_smile:

So I tried a few things just for fun …

I used 4 cubes as “furniture” and 1 flattened + scaled up cube for the “floor” :slight_smile:
Here is the code I used. It’s pretty decent, based on the sample test scene.
The code could use a bit of polish, for instance caching the BoxCollider or even the extents.

public LayerMask floorMask;
public LayerMask furnitureMask;
public void OnDrag(PointerEventData eventData)
 {
   Vector3 mp = Input.mousePosition;
   mp.z = Mathf.Abs(transform.position.z - Camera.main.transform.position.z);
   Ray ray = Camera.main.ScreenPointToRay(mp);
   RaycastHit hit;
   if (!Physics.Raycast(ray, out hit, 1000, floorMask))
   {
   return;
   }
   if(Physics.BoxCast(transform.position, GetComponent<BoxCollider>().bounds.extents,hit.point - transform.position,transform.rotation,Vector3.Distance(hit.point, transform.position),furnitureMask))
  {
     return;
   }
   // set the position, offset for the test by .5f so it's flat on the ground;)
   transform.position = hit.point + new Vector3(0,.5f,0);
 }
void OnMouseDrag()
    {
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        RaycastHit hit;
        RaycastHit obstacle;

        if (Physics.Raycast(ray, out hit, Camera.main.farClipPlane, 1 << LayerMask.NameToLayer("Floor")))
        {
            if (Physics.Raycast(new Vector3(hit.point.x, 0.5f, hit.point.z), new Vector3(((GetComponent<BoxCollider>().size.x / 2) + 0.05f) * (-1), 0, 0), out obstacle, 0.6f) || Physics.Raycast(new Vector3(hit.point.x, 0.5f, hit.point.z), new Vector3((GetComponent<BoxCollider>().size.x / 2) + 0.05f, 0, 0), out obstacle, 0.6f) || Physics.Raycast(new Vector3(hit.point.x, 0.5f, hit.point.z), new Vector3(0, 0, ((GetComponent<BoxCollider>().size.z / 2) + 0.05f) * (-1)), out obstacle, 0.6f) || Physics.Raycast(new Vector3(hit.point.x, 0.5f, hit.point.z), new Vector3(0, 0, (GetComponent<BoxCollider>().size.z / 2) + 0.05f), out obstacle, 0.6f))
            {
                if (obstacle.collider.gameObject.name == GetComponent<Rigidbody>().name)
                    GetComponent<Rigidbody>().MovePosition(new Vector3(hit.point.x, 0.43f, hit.point.z));
            }
        }
    }

I had thought of a code similar to this one rather
The concern is that the move of the furniture has become very slow with that :confused:
I did not understand everything in your code, I will try to analyze it a bit if maybe it is more suitable than mine :slight_smile:

I’m not sure, did you try the code I posted to see how it works for you?