- Home /
The question is answered, right answer was accepted
Raycasting on Navmesh?
I'm trying to get my click to move setup working with Navmesh, but I'm having some problems and not entirely sure I'm doing it right. As it stands the raycast doesn't seem to hit anything.
What I want to do is when I click on my terrain, send a ray from the camera to the navmesh, and set the destination of that point to where the raycast hit the navmesh. Below is my code - also I don't know quite how to use the passableMask parameter (I put 0 as my integer, hoping to access "Built in Layer 0"):
// Moves the Player if the Left Mouse Button was clicked
if (Input.GetMouseButtonDown(0)&& GUIUtility.hotControl ==0) {
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
NavMeshHit hit;
if (NavMesh.Raycast(ray.origin, ray.direction, out hit, 0)) {
Vector3 targetPoint = hit.position;
destinationPosition = hit.position;
targetRotation = Quaternion.LookRotation(targetPoint - transform.position);
Debug.Log ("NavMesh hit!");
}
Debug.Log (destinationPosition);
}
I wish this thread wasn't closed, since the accepted answer didn't really answer the question. In any case, I wrote a function that'll basically do a pseudo sphere/raycast onto the actual navmesh ins$$anonymous$$d of some other object.
public static bool Nav$$anonymous$$eshRaycast (Ray ray, float distance, float radius, out Vector3 hitPoint){
int samples = $$anonymous$$athf.RoundToInt (distance / (radius * 2));
for (int i = 0; i < samples; i++) {
Vector3 samplePos = ray.GetPoint(radius + radius * 2 * i);
Nav$$anonymous$$eshHit hit;
bool didHit = Nav$$anonymous$$esh.SamplePosition( samplePos out hit, radius, Nav$$anonymous$$esh.AllAreas );
if(didHit){
hitPoint = hit.position;
return true;
}
}
hitPoint = Vector3.zero;
return false;
}
Acegikmo's answer worked for me! This question should definitely be re-opened and his answer/comment converted into an actual Reply so people can find it.
Thanks a 1000 times, Acegikmo! Your final suggestion brilliantly solves the issue for me - simple, elegant and flexible. Working as expected.
I was facing the same problem, where I wanted to click on the navmesh to send my player to that specific position.
For some reason the Physics.Raycast is not able to raycast onto a navmesh, but only onto Colliders (such as $$anonymous$$esh colliders).
The way I fixed this was by creating a new $$anonymous$$esh Collider in the scene at runtime, containing the Nav$$anonymous$$esh and placing it on the Clickable Layer i use for the raycast.
//Copies the Nav$$anonymous$$esh triangles to a $$anonymous$$esh Collider on a new GameObject
void GenerateCollision$$anonymous$$esh()
{
var triangulation = Nav$$anonymous$$esh.CalculateTriangulation();
$$anonymous$$esh m = new $$anonymous$$esh();
m.vertices = triangulation.vertices;
m.triangles = triangulation.indices;
m.RecalculateNormals();
GameObject collisionHolder = new GameObject("NavmeshCollision");
collisionHolder.layer = 9; //The physics layer you want to be able to click on
collisionHolder.AddComponent<$$anonymous$$eshFilter>().mesh = m;
collisionHolder.AddComponent<$$anonymous$$eshCollider>();
}
This function can be placed in any $$anonymous$$onobehaviour script. Just make sure to call it in the Start() or in the Awake() function.
When you use Runtime NavmeshGeneration using the Nav$$anonymous$$eshBuilder, $$anonymous$$ake sure to call this after the function Nav$$anonymous$$esh.AddNav$$anonymous$$eshData(Nav$$anonymous$$eshData nav$$anonymous$$eshData).
Answer by fafase · Oct 01, 2012 at 07:26 PM
Why don't you use a regular raycast? I am not familiar with this NavMesh.Raycast but by the look at the docs, I am confused with your ray.direction. It seems NavMesh needs a start and end point but you are giving a start and direction. I would assume that if your direction is Vector3.forward(0,0,1) your ray is from start to that point and then not getting through the ground.
I would recommend raycast hit instead
Camera other = GameObject.FindObjectOfType(typeof(Camera)) as Camera;
Raycast hit;
if(Physics.Raycast(other.ScreenPointToRay(Vector3(Input.mousePosition)), out hit)){
Vector3 targetPoint = hit.position;
// other codes
}
Yes that makes sense - but how do I make sure that I am hitting the Navmesh and not some other collider/terrain (as there are places on the terrain that are not pathable etc.
Weird... got an email that there was a comment: "You check what you are hitting with. if(hit.collider.tag == "ground") // It hits the ground."
This works for objects I can tag, but I haven't seen a way to tag a Nav$$anonymous$$esh layer specifically (that I know of)
Yes that was me but for the reason you mention, I removed the comment which was wrong.
Ah ok! Well personally I have moved on to A* as I've been finding the stock unity navmesh cumbersome and difficult to work with - and having much more success with that.
I think it would be useful to know how to specifically target the navmesh with a raycast (seems like a basic thing) for people who want to use the system - but I'm happy to close out the post if people don't think it would be helpful.
Thanks!
I'm also interested to know how I can collide a ray with the navmesh. Using Physics.Recast does not seem to work for the navmesh.
Answer by thereal_mpellegr · Oct 28, 2013 at 01:09 PM
If anyone wants to know how to do a raycast to the navmesh and is having trouble using the answer here, here's the code that works for me (my navmesh lies on the xz plane):
function raycastToNavmesh(screenPosition : Vector3) : NavMeshHitResult{
var ray : Ray = Camera.mainCamera.ScreenPointToRay(screenPosition);
var positionOnGroundPlane : Vector3 = ray.origin - ray.direction / ray.direction.y * ray.origin.y; //collide with plane at y=0
var navMeshHit : NavMeshHit;
var result = NavMesh.SamplePosition(raycastHit.point, navMeshHit, 1, 1);
return new NavMeshHitResult(result, navMeshHit);
}
Or if you have a terrain or other 3D geometry on which your navmesh lies on top of (such as hills, etc) you can use Physics collisions after adding your collider components, but you need to map that collision point to a point on the navmesh:
function raycastToNavmesh(screenPosition : Vector3) : NavMeshHitResult{
var ray : Ray = Camera.mainCamera.ScreenPointToRay(screenPosition);
var raycastHit : RaycastHit;
if(Physics.Raycast(ray.origin, ray.direction, raycastHit)){
var navMeshHit : NavMeshHit;
var result = NavMesh.SamplePosition(raycastHit.point, navMeshHit, 1, 1);
return new NavMeshHitResult(result, navMeshHit);
}
}
Where NavMeshHitResult is just a script I made to contain the results:
class NavMeshHitResult{
var result : boolean;
var navMeshHit : NavMeshHit;
function NavMeshHitResult(result : boolean, navMeshHit : NavMeshHit){
this.result = result;
this.navMeshHit = navMeshHit;
}
}
I've solved the problem by approaching the problem on a different.. "path" :)
/// <summary>
/// check if the point is inside the Nav$$anonymous$$esh
/// </summary>
/// <param name="startPoint">where the unit sit</param>
/// <param name="endPoint">where user click</param>
/// <returns></returns>
public static bool isReachable(Vector3 startPoint, Vector3 endPoint)
{
bool result = false;
Nav$$anonymous$$eshPath path = new Nav$$anonymous$$eshPath();
if (Nav$$anonymous$$esh.CalculatePath(startPoint, endPoint, Nav$$anonymous$$esh.AllAreas, path))
return true;
return result;
}
Follow this Question
Related Questions
How to queue NavMeshAgents on entering a tile in Unity (based on path distance to tile)? 0 Answers
How to prevent NavMeshAgents from colliding when warped to same position? 0 Answers
How to make enemy AI with NavMesh 2 Answers
How to make NavMeshAgents traverse two possible paths 1 Answer
NavmeshAgent.remainingDistance remains at 0 but the agent still moves and behaves correctly 4 Answers