Top down 3D Raycast to Mouse Point - unity3d

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)

Related

Unity2D raycast pointing at the wrong target

I am working on a script which makes a raycast going in the direction of the mouse. But for some reason it doesn't follow the mouse, it goes directly forward.
void Update()
{
if (Input.GetMouseButtonDown(0))
{
CastRay();
}
void CastRay()
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit2D hit = Physics2D.Raycast(ray.origin, ray.direction, Mathf.Infinity);
if (hit.collider != null)
{
Debug.Log(hit.collider.gameObject.name);
}
}
}
The way I understand this question is that you'd like to cast a ray from the player position towards mouse position in 2D. If that is not the case then you should edit your question and give more context on what exactly you want to achieve.
When I understood correctly then the answer is that you construct the wrong ray. What you're doing is casting a ray from the cameras perspective, you can think of it as casting a ray through your monitor towards the game world.
To get a ray from the player characters perspective, the ray should be from the players position towards the mouse position in screen space.
// get the player position in screen space
var playerScreenSpacePosition = Camera.main.WorldToScreenPoint(playerPosition);
// calculate ray direction (to - from = direction)
var rayDirection = Input.mousePosition - playerScreenSpacePosition;
// if direction vectors length should be ignored then normalize
rayDirection.Normalize();
// construct the ray with the player being the origin
// (if needed could be Ray2D as well by ignoring 'Z' axis, needs converting position and direction to Vector2)
var ray = new Ray(playerPosition, rayDirection);
// cast for a hit
var hit = Physics2D.Raycast(ray.origin, ray.direction, Mathf.Infinity);
// alternatively: if the length of the raycast shall be defined by the directions magnitude
// (only if direction not normalized, otherwise the rays length will always be one)
hit = Physics2D.Raycast(ray.origin, ray.direction, ray.direction.magnitude);

2D Object Not Detecting When Hit by Raycast

I'm creating a top down 2D game, where the player has to break down trees. I made it so the player casts a ray toward the mouse, and when the ray hits a tree, it should lower the tree's health. I don't get any errors when I run the game or click, but it seems like the tree isn't detecting the hits.
void Update()
{
...
if (Input.GetMouseButtonDown(0))
{
RaycastHit2D hit = Physics2D.Raycast(playerRb.transform.position, mousePosition - playerRb.transform.position, 2.0f);
if (hit.collider != null)
{
if (hit.collider == GameObject.FindWithTag("Tree"))
{
hit.collider.GetComponent<TreeScript>().treeHealth--;
}
}
}
}
Still pretty new to coding and I'm teaching myself, so please make your answer easy to understand to help me learn.
Input.mousePosition is equal to the pixel your mouse is on. This is very different than the location your mouse is pointing at in the scene. To explain further, Input.mousePosition is where the mouse is. Think about it. If the camera was facing up, the mouse positon would be the same, but where they are clicking is different.
Instead of using Input.mousePosition, You should pass this into a function called Ray Camera.ScreenPointToRay();
You just input the mouse position and then use this new ray to do the raycast.
ANOTHER EXTREMELY IMPORTANT THING 1: Do not use Camera.main in Update(), as it uses a GetComponet call, which can cause perormance decreases. Store a reference of it in your script and use that.
Extremely important thing 2: I notice you are using GetComponent to change the tree's health. This is fine, but do not use GetComponent if you don't have to.
Like this:
Camera cam;
void Start()
{
cam = Camera.main; //it is fine to use this in start,
//because it is only being called once.
}
void Update()
{
...
if (Input.GetMouseButtonDown(0))
{
Ray ray = cam.ScreenPointToRay(Input.mousePosition);
RaycastHit2D hit = Physics2D.Raycast(ray);
...
}
}
You need to convert your mouse position from screen point to world point with Z value same as the other 2D objects.
Vector3 Worldpos=Camera.main.ScreenToWorldPoint(mousePos);
Also use a Debug.DrawRay to check the Raycast
Debug.DrawRay(ray.origin, ray.direction*10000f,Color.red);
Source

Precision difference between a raycast and OnMouseOver

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?

Vector3.MoveTowards while also following cursor?

I'm making a character that follows my mouse position.
I also have enemies that are being instantiated and would like that character to move towards the location of the enemy but be a few feet higher than the enemy.
Since my character is a flying enemy I'm unsure how to use move towards in unity.
When my enemies are destroyed I would also like the character to continue following the cursor.
public class FollowCursor : MonoBehaviour
{
// Use this for initialization
void Start ()
{
}
// Update is called once per frame
void Update ()
{
if(GameObject.FindWithTag("Enemy"))
{
transform.position = Vector3.MoveTowards.GameObject.FindWithTag("Enemy").transform.position;
}
else
{
transform.position = Camera.main.ScreenToWorldPoint( new Vector3(Input.mousePosition.x,Input.mousePosition.y,8.75f));
}
}
}
I understand you wish to move towards a flying enemy and/or have the flying enemy move towards your character.
I also understand that you wish to use the MoveTowards method to do this.
You should be able to do this by ignoring the Y position or setting it to a fixed value.
Like this.
//Method used: Vector3 MoveTowards(Vector3 current, Vector3 target, float maxDistanceDelta);
//Set movespeed/steps
float speed = 5f;
float step = speed * Time.deltaTime;
//Define y position
float yourFixedYValue = 8.75f;
//Find target
Vector3 enemyPosition = GameObject.FindWithTag("Enemy").transform.position;
Vector3 target = new Vector3(enemyPosition.x, yourFixedYValue, enemyPosition.z);
//Move from current position towards target with step increment.
transform.position = Vector3.MoveTowards(transform.position, target, step);
Please elaborate what you mean if this didn't answer your question.
EDIT:
To move towards the mouse you could use a Raycast something like this inside your Update method.
if (Input.GetMouseButtonDown(0))
{ //If left mouse clicked
RaycastHit hit;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); //Fire ray towards where mouse is clicked, from camera.
if (Physics.Raycast(ray, out hit)) //If hit something
target = hit.point; //point is a vector3 //hit.point becomes your target
}
That "something" can be any collider, also an enemy one. So can be used to move around in general and to move towards enemies.

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);