This is my script which moves a game object on mouse click towards the mouse clicked position.
using UnityEngine;
using UnityEngine.InputSystem;
[RequireComponent(typeof(Collider))]
[RequireComponent(typeof(Rigidbody))]
public class Interact : MonoBehaviour
{
// Important stuff here.
private Rigidbody rb;
private Camera cam;
// The position to move to on mouse click.
private Vector3 moveToPosition = Vector3.zero;
// Get references.
private void Awake()
{
cam = Camera.main;
rb = GetComponent<Rigidbody>();
}
// The callback from the InputsManager Player Input asset that is triggered from the "MouseLClick" action.
private void OnMouseLClick(InputValue value)
{
if (value.isPressed)
{
// ============================================================
// METHOD A - THAT MAKES MOVEMENT "SMOOTH"
// ============================================================
// We create a ray
//Ray ray = cam.ScreenPointToRay(Input.mousePosition);
//RaycastHit hit;
//// If the ray hits
//if (Physics.Raycast(ray, out hit))
//{
// if (hit.collider.GetComponent<Interactable>() == null)
// {
// Debug.Log("We hit " + hit.collider.name + " at " + hit.point + " let's move there.");
// moveToPosition = new Vector3(hit.point.x, 0, hit.point.z);
// }
// else
// {
// Debug.Log("We hit " + hit.collider.name + " at " + hit.point + " an interactable object.");
// }
//}
// ============================================================
// ============================================================
// ============================================================
// METHOD B - THE RIGHT METHOD. NO "SMOOTH"
// ============================================================
Plane playerPlane = new Plane(Vector3.up, transform.position);
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
float hitdist = 0.0f;
if (playerPlane.Raycast(ray, out hitdist))
{
Vector3 targetPoint = ray.GetPoint(hitdist);
moveToPosition = ray.GetPoint(hitdist);
Quaternion targetRotation = Quaternion.LookRotation(targetPoint - transform.position);
transform.rotation = targetRotation;
}
// ============================================================
// ============================================================
}
}
private void Update()
{
if (moveToPosition != Vector3.zero)
{
if (Vector3.Distance(moveToPosition, transform.position) > 0.5f)
{
float speed = 6;
Vector3 newPosition = Vector3.MoveTowards(transform.position, moveToPosition, speed * Time.deltaTime);
rb.MovePosition(newPosition);
}
else
{
moveToPosition = Vector3.zero;
}
}
}
}
Method A - From a youtube tutorial.
The turret moves “smoothly” when it’s about to stop. This is not my desired effect.
Method B - Snippet from a script I (luckily) found online.
This is what I want. Movement is crisp.
(Note: Ignore the rotation. I’m only concern about the movement.)
Question 1:
Why does using Physics raycast gives a totally different movement result compared to Plane raycast?
Question 2:
Do I also have the option to get the data of which game object I clicked on using Plane raycast like how it’s possible when using Physics raycast?
Question 1 - are you certain that it does? The two methods only return a target position; this doesn’t change anything about the way the object should move towards that point. Are you certain you didn’t change any other code around them?
Question 2 - You can’t use the Plane raycast, but you can get the clicked object without using a raycast by going the other way around - rather than casting the mouse pointer into 3D space, you can transform the object’s position into screen space and compare distance to the mouse cursor.
Qn1) Yes I’m sure. That’s the script I added as a component. I commented out everything in the other method:
//====
//Method x
…
//=====
/=====
When testing out each of the methods. I added gifs to show you the results.
Qn2) Sounds confusing… Sorry man my brain is fried trying to figure out the problem in (Qn1). I’d love to use physics raycast method as that’s how the YouTube tutorial flows. Now I’m stuck and I can’t carry on with the tutorial.
In version B you create a virtual plane to raycast against, and in this case, the plane is at “transform.position” so to what gameobject have you attached this script to?
OH, I see one thing that might be a factor: what’s the tank’s Y position?
The physics raycast method uses y=0 for the Y position, while the plane raycast method uses the tank’s own Y position. If the tank’s Y position isn’t 0, then the physics version would be moving towards coordinates that aren’t on its own plane, which would use up some of MoveTowards’s movement.
It’s not about physics moveposition; that’s the same in both versions. Literally the only change is the value of the “movetoPosition” variable. On line 40, the Y value of that variable is set to 0; the plane version doesn’t have that, so the Y value of it just remains as whatever the hit point was.
@StarManta * “It’s not about physics moveposition”
I don’t know what you mean by that so I clarify what I meant, I meant this case - if the moving units pivot isn’t on ground level, move direction should be adjusted…
There’s another weirdness I’m encountering now:
Method A - Physics raycast:
See the mouse position on the turret. It’s always at a weird position (not centered).
Method B - Plane raycast:
The mouse position is always on the center, which is the desired effect.
“See the mouse position on the turret. It’s always at a weird position (not centered).”
Do you mean that when comparing two gifs, position of mouse cursor in screen space relative to tank is different?
What is you pivot location? Show a side view of the tank rig / wireframe.
IMO It is not very realistic to even expect mouse cursor to be somehow exactly centered to object when you are dealing with arbitrary pivot location and perspective image.
Yes, the position of the mouse cursor in screen space relative to the tank.
Side view of my tank?
I understand. I’m not too concerned about the mouse position as the major issue of the “smooth” movement if finally solved after 4 hours… But since I’m new to unity and learning, I don’t understand why Physics/Plane gives deferent results. Like personally, I love how plane raycast is giving me the desired feel (centered). But physics raycast has collider info which the tutorial I’m watching on YouTube is using.
At this point, it’s because you’re raycasting at different things. In method B, You set the Plane’s Y position to the Y position of the tank, while the ground for method A is effectively a plane at the Y position of the bottom of the tank, whatever that is. When you think of method B, think about a waist-high pool of water that the tank is wading through. Now, shine a laser pointer into that pool from the sideline.
A: Shine the laser pointer. Where the laser pointer hits the water is the point you move to
B: Shine the laser pointer. Take the point where it hits the floor, then move directly upwards to the surface of the water.
Hopefully that helps you see that these give you two different locations in the XZ plane. B will be further away from the camera.
If you have that plane on transform.position, well you can see where your pivot is… but your ground isn’t on the same level. This is what I already pointed out on my initial reply.
@StarManta & @eses thank you guys for taking the time explaining to me. I really appreciate it. I feel bad to admit that I still find it hard to visualise it. But I’m going to slowly digest it.
Keep in mind that these two functions are completely and totally unrelated and different.
Physics.Raycast casts a ray into the actual scene full of GameObjects, looking for the first Collider that it hits.
UnityEngine.Plane.Raycast is entirely a mathematical construct and does NOT involve scenes or GameObjects at all: a Plane describes a mathematical plane, and raycasting against it may hit that plane, or not.
The Unity “Plane” construct you can create under Create->3D Object->Plane does not have a Plane object in it. It is a mesh of a plane, but it does not possess or interoperate in any way with the UnityEngine.Plane structure.