Precision difference between a raycast and OnMouseOver - unity3d

I'm really surprised by what I see in my Unity3D project and was wondering if anyone has an explanation as I didn't find one after looking for it on google.
So I have a project where I instantiate multiples objects, which are basically cubes, with their colliders (not triggered) and where I want to know if the mouse is over one of them.
So I attached a very basic script taken from the unity documentation:
void OnMouseEnter()
{
Debug.Log("enter block " + this.name);
// Change the color of the GameObject to red when the mouse is over GameObject
m_Renderer.material.color = m_MouseOverColor;
}
void OnMouseOver()
{
Debug.Log("over block " + this.name);
// Change the color of the GameObject to red when the mouse is over GameObject
m_Renderer.material.color = m_MouseOverColor;
}
void OnMouseExit()
{
Debug.Log("exit block " + this.name);
// Reset the color of the GameObject back to normal
m_Renderer.material.color = m_OriginalColor;
}
And I don't seem to see any result. While looking for options I scaled the collider box to way bigger and can get some results, but they are not precise, the mouse needs to be slightly on the side of the cubes to work. It looks like a perspective effect caused by hte size of the collider by that would be annoying for the final user.
So I tried to use a simple raycast test in a script attached to the camera:
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, 100000000, LayerMask.GetMask("TrackPartEnd")))
{
Debug.DrawRay(ray.origin, ray.direction * 100000000, Color.blue, 3);
myCursorObject.transform.position = hit.point;
}
else
{
if (Physics.Raycast(ray, out hit, 100000000, LayerMask.GetMask("Floor")))
{
Debug.DrawRay(ray.origin, ray.direction * 100000000, Color.green, 3);
myCursorObject.transform.position = hit.point;
}
else
{
Debug.DrawRay(ray.origin, ray.direction * 100000000, Color.red, 3);
}
}
And with this one I get a really good result, it's fast and really precise.
But I didn't find any explanation on why? To my knowledge, OnMouseOver uses raycast as well. I've double-checked and I don't see anything blocking the rays neither.
Does anyone have an explanation?

Related

How do I make a raycast end slightly before hitting an object in Unity3d?

I'm making a block-building system, and sometimes the blocks get offset or even clip inside each other when I place them. I'm using a ray-cast to a place where I look, and I'd like to make the ray-cast end slightly before hitting a block to prevent blocks from merging and becoming offset. The blocks I build with are also coded to be snapped to a grid.
Raycast block placement code:
if (Input.GetMouseButtonDown(0))
{
RaycastHit hit;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
//Make Whatever a Raycast layer or if you don't want it just exclude it
if (Physics.Raycast(ray, out hit, Whatever.value))
{
Point = hit.point;
Instantiate(stoneBricks, Point, UnityEngine.Quaternion.identity);
}
}
Block grid-snap code:
Vector3 b = new Vector3(Mathf.Floor(a.x + 0.0f), Mathf.Floor(a.y + 0.0f), Mathf.Floor(a.z - 0.0f));
You might be looking for Physics.ComputePenetartion
Compute the minimal translation required to separate the given colliders apart at specified poses.
So you could do something like e.g.
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit, Whatever.value))
{
Point = hit.point;
// store your new bricks reference
var newBrick = Instantiate(stoneBricks, Point, UnityEngine.Quaternion.identity);
// Now check if there is an overlap
if(Physics.ComputePenetartion(newBrick.GetComponent<Collider>(), newBrick.transfom.position, newBrick.transform.rotation, hit.collider, hit.transform.position, hit.transform.rotation, out var direction, out var distance))
{
// If so move the new brick just far enough away so there is no overlapping anymore
newBrick.position += direction * distance;
}
}
Hi try increasing the offset, or try maybe increasing the collider's dimension a bit (Very tiny tiny bit).

Top down 3D Raycast to Mouse Point

I am casting a ray from an empty transform at the tip of a gun. The cast is to head towards the mouse position on Right Click. There is a unexpected result of the hit point being half way from expected.
I have attempted to rework the Raycasting and check out the Unity documents and forums, perhaps i am missing so simple. I have included the code of my Fire Function. Note "Raycaster" is the empty gameobject it fires from.
public void Fire(Vector3 point)
{
RaycastHit hit;
if(Physics.Raycast(Raycaster.transform.position, point, out hit))
{
Debug.DrawLine(Raycaster.transform.position, point, Color.green); //expected
Debug.DrawLine(Raycaster.transform.position, hit.point, Color.red); //actual
if(hit.rigidbody != null)
{
hit.rigidbody.AddForce(-hit.normal * weapon.Damage);
}
GameObject Impact = Instantiate(ImpactParticle, hit.point, Quaternion.LookRotation(hit.normal));
Destroy(Impact, 1f);
}
}
The first Debug.Drawline in Green is what I expect, but the second Debug.Drawline is the actual. I must be the hit.point from the Raycast but it always is half way.
Issue example, Green is expected and red is actual
I think you use world position(point). Try convert your point argument to direction.
Vector3 dir = point - Raycaster.transform.position;
Physics.Raycast(Raycaster.transform.position, dir, out hit)

Unity3D Raycast not destroying gameobject every time

I am working on a Minecraft like game and trying to get block deletion to work properly. I have a function that returns the proper coordinates of the block and then calls DestroyBlock (below) which casts 6 rays in each direction of the face of a cube and is supposed to destroy any quad it encounters. I then have another function that is called after the block is destroyed that then updates the surrounding blocks and renders the appropriate faces. My problem is that this DestroyBlock function doesn't always work. As you can see in the images on the first click, everything works properly and the correct faces are destroyed and then rendered, but when I do the second click, it only destroys one face of the new block...
void DestroyBlocks(int x, int y, int z) {
Vector3 origin = new Vector3(x, y, z);
Ray Up = new Ray(origin + Vector3.up, Vector3.down);
Ray Down = new Ray(origin + Vector3.down, Vector3.up);
Ray North = new Ray(origin + Vector3.forward, Vector3.back);
Ray East = new Ray(origin+ Vector3.right, Vector3.left);
Ray South = new Ray(origin + Vector3.back, Vector3.forward);
Ray West = new Ray(origin + Vector3.left, Vector3.right);
RaycastHit markedQuad;
if(Physics.Raycast(Up, out markedQuad, 1f)) {
Destroy(markedQuad.transform.gameObject);
Debug.Log("Destroyed Up");
}
if(Physics.Raycast(Down, out markedQuad, 1f)) {
Destroy(markedQuad.transform.gameObject);
Debug.Log("Destroyed Down");
}
if(Physics.Raycast(North, out markedQuad, 1f)) {
Destroy(markedQuad.transform.gameObject);
Debug.Log("Destroyed North");
}
if(Physics.Raycast(East, out markedQuad, 1f)) {
Destroy(markedQuad.transform.gameObject);
Debug.Log("Destroyed East");
}
if(Physics.Raycast(South, out markedQuad, 1f)) {
Destroy(markedQuad.transform.gameObject);
Debug.Log("Destroyed South");
}
if(Physics.Raycast(West, out markedQuad, 1f)) {
Destroy(markedQuad.transform.gameObject);
Debug.Log("Destroyed West");
}
}
And I know that the raycast is hitting the quads because of the debug messages but for some reason it is not actually destroying all the sides when it hits them. Any help or advice would be greatly appreciated.
Found the problem. When I was updating the adjacent blocks next to the one I clicked, I was rerendering the faces of that adjacent block without first checking to see if a quad already existed there, effectively instantiating two faces at the exact same position. Which is why the raycast and destroy was working, only there were just two quads there so it only appeared it didn't. Thanks for the help.
Can't see any images and don't really take why it isn't work after first interaction but I'm curious:
1: Why are you controlling each face instead of cube/block itself?
2: I'm thinking in mine craft right now because you mentioned it and I would approach creating an Destroyable interface for blocks to call on trigger or collision with any "destruction tool" or something...
Maybe if you provide more information on your approach and reasons to go your way schould become easier to help...

raycast not showing hit

I have a raycast, however it goes through the enemies as it is suppose to but it hits nothing else. If I remove the mask it hits the enemies layer. If I remove the layer and use raycastall it hits only the enemies.
If I use raycast it goes clear through a wall and hits the player but does not show as a hit, in fact i get the error
NullReferenceException: Object reference not set to an instance of an object
EnemyAI.OnTriggerEnter2D (UnityEngine.Collider2D other) (at Assets/Scripts/EnemyAI.cs:32)
Line 32 is Debug.Log (hit.transform.gameObject);. If I remove it, nothing happens at all. No error, and no hit.
Heres is the code
void OnTriggerEnter2D(Collider2D other){
if (other.gameObject.tag == "Player") {
myStats.inRange = true;
Vector2 direction = other.transform.position - transform.position;
hit = Physics2D.Raycast(transform.position, direction, myCircle.radius + 1, LayerMask.GetMask("enemies"));
Debug.Log("Radius size is " + (int)myCircle.radius);
Debug.Log("donthit value " + LayerMask.GetMask("Enemies"));
Debug.Log("direction " + (myStats.player.transform.position - transform.position));
Debug.DrawRay(transform.position, other.transform.position - transform.position, Color.white);
Debug.DrawLine(transform.position, myStats.player.transform.position, Color.white);
//Destroy(hit.transform.gameObject);
Debug.Log (hit.transform.gameObject);
if(hit != null && hit.transform.gameObject != null){
if (hit.transform.gameObject.tag == "INDESTRUCTIBLE") {
Debug.Log("WALL");
// Destroy the Tag "Enemy" here
}
if (hit.transform.gameObject.tag == "Player") {
Debug.Log("player");
// Destroy the Tag "Enemy" here
}
Debug.Log("Tag name is " + hit.collider.tag);
}
Debug.DrawRay(transform.position, myStats.player.transform.position - transform.position, Color.white);
}
}
So it seems there are two things at work here. First, according to the documentation, Raycast 2D will also detect collider(s) at the start of the ray. If you don't use raycastAll then the source enemy will stop your ray before it goes out into the world. To prevent this you could use a layer mask. Just to be sure, a layer mask signifies the layer that you want to hit, not the layers you want to ignore. So to make sure the enemy layer is the only layer you ignore you can use this:
var layerMask = Physics2D.DefaultRaycastLayers & ~LayerMask.GetMask("Enemies");
And then use this mask in your raycast.
Second, there must be a reason you're not hitting anything else. If you applied the layermask in the wrong way then you filter out any hits against the wall or player. It looks like you both have an enemy tag and an enemy layer so make sure to get this straight. But if you used raycastAll without mask and still didn't hit anything then make sure your objects meet all the requirements. They must have 2D colliders of course. If they have colliders, make sure they either aren't triggers or that "Raycasts hit Triggers" is enabled in Edit -> project settings -> physics2d.
A final thing to check is whether or not your ray goes far enough. I noticed your debug draw does not reflect your raycast perfectly. Use this to draw the actual ray:
Debug.DrawLine(transform.position, transform.position + direction * (myCircle.radius + 1) / direction.magnitude, Color.white);

Can't cast a ray from the camera down to objects based on mouse position

I have a camera looking down on my game world. I want to allow the user to click on objects. The following script is attached to the MainCamera.
void Update () {
if (holdingSomething)
{
if (Input.GetMouseButtonUp(0))
{
holdingSomething = false;
}
}
else if (Input.GetMouseButtonDown(0))
{
RaycastHit hit;
Vector3 mousePos = new Vector3(Input.mousePosition.x,Input.mousePosition.y,0);
Vector3 rayOrigin = new Vector3(Camera.main.ScreenToWorldPoint(mousePos).x,Camera.main.ScreenToWorldPoint(mousePos).y,Camera.main.transform.position.z);
Vector3 rayDirection = new Vector3(Camera.main.transform.rotation.x,Camera.main.transform.rotation.y,Camera.main.transform.rotation.z);
Debug.Log("Mouse button is down" + mousePos);
Debug.Log("rayOrigin " + rayOrigin);
Debug.Log("rayDirection " + rayDirection);
Ray ray = new Ray(rayOrigin,rayDirection);
if (Physics.Raycast(ray, out hit))
{
Debug.Log("We just touched " + hit.collider);
}
}
}
The line Debug.Log("We just touched " + hit.collider); is never called. I don't think my ray is being built right. Here is the output of the other debug messages. At each message they rayOrigin is the same, even though the mousePos changes.
Mouse button is down(418.7, 195.1, 0.0)
rayOrigin (0.0, 6.2, -7.3)
rayDirection (0.3, 0.0, 0.0)
Mouse button is down(417.7, 278.0, 0.0)
rayOrigin (0.0, 6.2, -7.3)
rayDirection (0.3, 0.0, 0.0)
So where am I going wrong?
I would define your Ray like this:
Ray ray = Camera.Main.ScreenPointToRay(Input.mousePosition);
As for your code, I can't test your code right now so I don't know what exactly goes wrong. Because you should be able to do as you described, even though my answer is an easier alternative.
You can use Debug.DrawLine to draw a line of the raycast you use. Then, you can see what your raycast looks like in the debugger, during runtime. You can see then how your raycast is positioned. You'd be able to see the start and end point of the ray. If one of them is off, you can check your code while knowing which part is wrong.