New Help with Mouse Drag C#

I have a problem making a rigidbody move with the mouse in 3d space.
My mouse drag script works fine in the X and Y, but I also need to make
it move in the Z since the scene is 3d. What I am doing is to cast a ray
against a table that the objects rest upon so that when the ray hits the
table, the rigidbody should move to the mouse position. When the
pointer is above the table, it will only move in the X and Y again.

Here is the code that I have so far for the Drag function:

// Drag selected rigid body with mouse
private void DragObject(GameObject go, float distance) {
		
		// Freeze rotation of the selected object
		selectedObject.rigidbody.freezeRotation = true;
		
		// If ray collides with the stone table layer
		// (This is how the stone moves when the ray hits the table)
		if (Physics.Raycast(ray, out hit, 100, stoneTableMask)) {
			
			// Get vector to mouse position (change to world coordinates)
			mouseVector = mainCamera.ScreenToWorldPoint(Input.mousePosition);
				
			// Translate the selected object to the new vector using velocity
			go.rigidbody.velocity = mouseVector * Time.deltaTime * .5f;
		}
		
		// If ray does NOT collide with the stone table layer
		// (This is how the object moves when the ray is not hitting the table)
		else if (Physics.Raycast(ray, out hit, 100)) {
			
			// Get vector to mouse position
			forceVector = (ray.GetPoint(fixedDistance) - go.transform.position);
			
			// Translate object to the mouse position with velocity
			go.rigidbody.velocity = forceVector * Time.deltaTime * 150;
		}
	}

The code sort of works, but instead of moving to the mouse position, the object raises up in the Y
for some reason. I hope someone can help me work this out because I don’t have much hair left.

I want the objects to slide along the surface of the table when the ray is casting against it.
But when the mouse is high enough that the ray is no longer hitting the table, I want the
object to stop moving along the surface of the table and only move up/down and side to side.
So, I guess I want it to only move in screen coordinates when the ray is not hitting the
table, and world coordinates when it is? I even tried this for the mouseVector:

// Get vector to mouse position (change to world coordinates)
mouseVector = new Vector3(ray.origin.x, ray.origin.y, ray.origin.z);

But it still does the same thing which makes me think that maybe there is something missing
in the translation to world point? Or, maybe the local rotation is off and the axis isn’t set right.

I need help, thanks.

Anyone?

Still need help with this.

Without some of the definitions of the variables, I’m taking a guess here. I think I can see what you are trying to do, but this may be wildly wrong. Not tested in Unity, but please let me know how it goes.

// Drag selected rigid body with mouse
private void DragObject(GameObject go, float distance)
{
   // Freeze rotation of the selected object
   selectedObject.rigidbody.freezeRotation = true;

   // Get vector to mouse position (change to world coordinates)
   Vector3 direction = mainCamera.ScreenToWorldPoint(Input.mousePosition) - mainCamera.transform.position;

   // Used for the hit
   RaycastHit hit;
        
   // If ray collides with the stone table layer
   // (This is how the stone moves when the ray hits the table)
   if (Physics.Raycast(mainCamera.transform.position, direction, out hit, 100, stoneTableMask))
   {
      // Translate the selected object to the new vector using velocity
      go.rigidbody.velocity = hit.point * Time.deltaTime * 0.5f;
   } 
   // If ray does NOT collide with the stone table layer
   // (This is how the object moves when the ray is not hitting the table)
   else
   {
      // Get vector to mouse position
      Vector3 forceVector = (direction.normalized * fixedDistance) - go.transform.position);
            
      // Translate object to the mouse position with velocity
      go.rigidbody.velocity = forceVector * Time.deltaTime * 150;
   }
}

LordAelfric
Starleaf Labs

First of all, thanks for your help.

I kind of see what you are trying to do there but that code returned an error:

Assets/_Scripts/DragRigidbody.cs(106,35): error CS1061: Type
UnityEngine.RaycastHit' does not contain a definition for position’ and no extension method position' of type UnityEngine.RaycastHit’ could be found (are you missing a using directive or an assembly reference?)

When I changed it to hit.point, it behaved erratically. I’m not sure what it was trying to do,
but the object either sat and wiggled or it jumped off the screen somewhere and would
not move with the mouse.

Sorry, the code was untested. “hit.point” is the correct member I intended. I updated the above code. The Raycast will output the RaycastHit variable and the hit.point is the point on the collider where the Raycast hit the object. In theory this should be the point on the table you are talking about, where you want the object to move to. The issue with my code is that the…

go.rigidbody.velocity = hit.point * Time.deltaTime * 0.5f;

… is forcing it to move based on a velocity. Looking back over it I think it should be.

// Drag selected rigid body with mouse
private void DragObject(GameObject go, float distance)
{
   // Freeze rotation of the selected object
   selectedObject.rigidbody.freezeRotation = true;
 
   // Get vector to mouse position (change to world coordinates)
   Vector3 direction = mainCamera.ScreenToWorldPoint(Input.mousePosition) - mainCamera.transform.position;
 
   // Used for the hit
   RaycastHit hit;
       
   // If ray collides with the stone table layer
   // (This is how the stone moves when the ray hits the table)
   if (Physics.Raycast(mainCamera.transform.position, direction, out hit, 100, stoneTableMask))
   {
      // Get the direction of the point hit on the table
      Vector3 hitDirection = hit.point - go.transform.position;

      // Translate the selected object to the new vector using velocity
      go.rigidbody.velocity = hitDirection.normalized * Time.deltaTime * 0.5f;
   }
   // If ray does NOT collide with the stone table layer
   // (This is how the object moves when the ray is not hitting the table)
   else
   {
      // Get vector to mouse position
      Vector3 forceVector = (direction.normalized * fixedDistance) - go.transform.position);
           
      // Translate object to the mouse position with velocity
      go.rigidbody.velocity = forceVector * Time.deltaTime * 150;
   }
}

Let me know how it goes.

Everything about dragging should be first, relative to the desired direction of travel. This can be a fixed direction or dynamic direction. If it is fixed then you need to express your location’s based on the planar field of that direction, dynamic usually refers to the dynamics of the camera.

Example:

A fixed direction creates a Plane 1 unit above the table * the highest stack count. Your Mouse then raycasts to that plane, giving you a location.

A dynamic direction creates a Plane X units from the camera’s facing, in the direction of camera.forward. This gives a flat surface to drag the stone across, albeit never in the direction needed to “stack” anything.

The whole point is that the mouse only has X and Y, you have to choose how to use those two to get your Vector3.

At no point are we using physics.raycast, or masks. This makes the job easier.

Thank you both for your responses.

@ LordAelfric,
That code didn’t produce any errors, but the object either just wiggled or it acted as though it
were shot from a cannon. I don’t understand what is happening there. I don’t know if it thinks the
point is somewhere off in space or what.

@ BigMisterB,
This movement should follow the mouse. When the ray cast at the mouse position hits the table
(which is sort of rounded like a large stone), the object should move to a spot on that table where the
ray hits it. (Well, it should follow the mouse, which I believe is what I don’t understand how to make it
do). When the ray is not hitting the table (which is when the stone is picked up far enough above the
table), the stone will no longer move backward and forward but instead move like it were in 2d (right
and left only). The idea is to get the objects to a point near the center of the table so they can be
more easily stacked on each other.

To clarify a couple things, I have to use raycast because of the platform that this app is designed for,
and the mask should make it easier to discern where the mouse pointer is. I have to determine that
since the table is the only point of reference to determine how the objects should move while the
mouse is dragging them.

This sentence sounds a little like my original approach which didn’t work out for me:

“The whole point is that the mouse only has X and Y, you have to choose how to use those
two to get your Vector3”.

Can you expand on that a little? I’m not quite sure what you meant.

OK, simply put, you get 2 directions out of a mouse (or touch) X and Y. You need to convert that into something that is “relative” to the user. The generalization is that you would use a plane based off of the camera in some sort or fashion.

If you are specifically wanting a point on the “table” you change that to the table’s platform. So build a plane based on the top side fo the table:

var plane = new Plane(table.transform.up, table.transform.position + tableTopFromPosition);

This then gives you a plane where the table top is. You then cast a ray to that table and get the point where it hit. (remember that the raycast is like this: Plane.Raycast(ray, out float) )

Simply get the hit point as such:

ray.GetPoint(distanceOfHit)

So if you are then looking at it from a camera’s perspective then you would move the object back some to adjust for your object’s size.

either: position = ray.GetPoint(distanceOfHit - object.transform.localScale.magnitude * 0.5);
or: position = ray.GetPoint(distanceOfHit) + object.transform.localScale.y * 0.5;

Both methods give you a point to place your object and adjust it for the size of the object.

There are about a billion ways to do this stuff, so there is no exact answer.

I would agree with BigMisterB here. There is a ton of ways to accomplish this and what he is saying is what I was trying to put into code. I have a feeling that the reason the object is shooting away is because it has a rigidbody that is colliding with the table, causing physics to take over. You could set the object to “IsKinematic” (= false) in the Rigidbody to disable physics moving the object around (unless you want that to happen).

It’s hard to completely answer your question without seeing what your project is doing first hand.

@ BigMisterB,
I’m not fully digesting conceptually how the a plane will work in this case. The reason being is that the table
has a convex curvature to it and unless the plane is being constantly redrawn as the mouse moves over it, I
would think that the object would try to translate through the surface of the table to reach the transform of the
plane. I’m a little slow… am I missing something?

@ LordAelfric,
I have the table set to kinematic and the objects themselves are non-kinematic which makes sense in
this application. The stones move dynamically and the table is stationary. Unfortunately, I won’t be able
to share the actual application because it isn’t mine, but I can post my entire drag script and hope that it
helps:

using UnityEngine;

public class DragRigidbody1: MonoBehaviour {
	
	private GameObject selectedObject;		// The object that is selected with the mouse
	private float fixedDistance;			// The set distance of the ray cast
	private Camera mainCamera;				// Cache the main camera
	private Vector3 mouseVector;			// Vector to mouse position when over table
	private Vector3 forceVector;			// Vector to mouse position when not over table
	private RaycastHit hit;					// Check all collisions from the raycast
	private Ray ray;						// Stores the ray to test collision with the stones
	private Ray ray2;						// Stores the ray to test collision with the stones
	private Vector3 target;					// Target created by the raycast hit point
	
	private int stoneTableMask;				// Cache the layer mask for the stone table
	public int stoneTable;					// Stores the layer that the stone table is on
		
	/// <summary>
	/// Start this instance.
	/// </summary>
	void Start() {
		
		// Cache the stone table layer mask
		stoneTableMask = 1 << stoneTable;
		
		// Find the main camera position
		mainCamera = FindCamera();
	}
	
	/// <summary>
	/// Update this instance.
	/// </summary>
	void LateUpdate () {
		
		// Cast a ray from the camera to the mouse position
		ray = mainCamera.ScreenPointToRay(Input.mousePosition);
		
		// Enable rotation if the stone is no longer dragged
		if (Input.GetMouseButtonUp(0)) {
			
			if (selectedObject != null) {
				selectedObject.rigidbody.freezeRotation = false;
			}
		}
		
		// Check that the mouse button is down
		if (Input.GetMouseButtonDown(0)) {
		
			if (Physics.Raycast(ray, out hit, 100)  (hit.rigidbody  !hit.rigidbody.isKinematic)) {
	
				// Select the object to be dragged
				selectedObject = hit.rigidbody.gameObject;
				
				// Turn off gravity for the selected object
				selectedObject.rigidbody.useGravity = false;
				
				// Change selected stone to ignore raycast layer
				selectedObject.layer = 2;
				
				// Set the fixed distance
				fixedDistance = Vector3.Distance(mainCamera.transform.position, hit.transform.position);
			}
			else return;
		}
		
		// If the drag button is pressed and there is a selected object, drag the object
		if (Input.GetButton("Drag Object")) {
		
			if (selectedObject != null) {
				
				// Drag the object
				DragObject(selectedObject, fixedDistance);
			}
		}
		else if (selectedObject != null) {
			
			// Turn on gravity
			selectedObject.rigidbody.useGravity = true;
			
			// Switch back to stones layer
			selectedObject.layer = 0;
			
			// Clear the selected object
			selectedObject = null;
		}
	}
	
	// Drag selected rigid body with mouse
	private void DragObject(GameObject go, float distance) {
		
		// Freeze rotation of the selected object
		selectedObject.rigidbody.freezeRotation = true;
		
		// If ray collides with the stone table layer
		// (This is how the stone moves when the ray hits the table)
		if (Physics.Raycast(ray, out hit, 100, stoneTableMask)) {
			
//			float x = Input.mousePosition.x;
//			float y = Input.mousePosition.y;
//			float zDepth = hit.point.z;
//			Vector3 position = new Vector3(x, y, zDepth);
			
//			Vector3 position = new Vector3(Input.mousePosition.x, Input.mousePosition.y, hit.point.z);
			
			// Get vector to mouse position (change to world coordinates)
			mouseVector = mainCamera.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, hit.point.z));
			
			// Translate the selected object to the new vector using velocity
			go.rigidbody.velocity = mouseVector * Time.deltaTime * .5f;
			
//			go.rigidbody.velocity = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, hit.point.z));
		}
		
		// If ray does NOT collide with the stone table layer
		// (This is how the object moves when the ray is not hitting the table)
		else if (Physics.Raycast(ray, out hit, 100)) {
			
			// Get vector to mouse position
			forceVector = (ray.GetPoint(fixedDistance) - go.transform.position);
			
			// Translate object to the mouse position with velocity
			go.rigidbody.velocity = forceVector * Time.deltaTime * 150;
		}
		
			Debug.Log(Input.mousePosition);
	}
	
	// Returns the main camera
	private Camera FindCamera () {
		
		return Camera.main;
	}
}

Note that I left in some commented lines that show how I tried to solve the problem.
I think what I need to understand essentially is how to make an object move along the
surface of a stone table while it is being dragged and while the mouse is over the table.
I don’t know how to capture that point where the mouse ray is hitting the table, and also
tell the object to move to it.

Okay, I tested this out in a project using what I believe is the same setup you have. I created a sphere and flattened it, giving it a smoothed round rock look- very flat. It had a rigidbody and a mesh collider. The rigidbody was set to “IsKinematic = false”. Then I created a cube with a simple box collider and also a rigidbody.

Bad code - deleted.

Let me know if this works for you.

Thanks again…
That is doing the same thing with the wiggling and the jumping.

Sorry, I spoke too soon. I actually fixed it, but overwrote it with my old bad code. Silly me. One moment.

This one should work. Sorry for that mixup.

// Drag selected rigid body with mouse
private void DragObject(GameObject go, float distance)
{
    // Freeze rotation of the selected object
    selectedObject.rigidbody.freezeRotation = true;

    // If ray collides with the stone table layer
    // (This is how the stone moves when the ray hits the table)
    if (Physics.Raycast(ray, out hit, 100, stoneTableMask))
    {
        // Get the direction of the point hit on the table
        forceVector = hit.point - go.transform.position;

        // Translate the selected object to the new vector using velocity
        go.rigidbody.velocity = forceVector * Time.deltaTime * 150.0f;
    }

    // If ray does NOT collide with the stone table layer
    // (This is how the object moves when the ray is not hitting the table)
    else if (Physics.Raycast(ray, out hit, 100))
    {
        // Get vector to mouse position
        forceVector = (ray.GetPoint(fixedDistance) - go.transform.position);

        // Translate object to the mouse position with velocity
        go.rigidbody.velocity = forceVector * Time.deltaTime * 150;
    }

    //Debug.Log(Input.mousePosition);
}

That works exactly as I asked.
Thank you very much for taking your time to help me.
Thank you also for your time BigMisterB. You are both
very helpful and I appreciate it very much. I hope that I
can return the favors someday.

No problem, glad I could help. Good luck!

oops, posted too late