News › Forums › RAIN › General Discussion and Troubleshooting › Pathfinding on dynamic voxel block terrain
This topic contains 13 replies, has 2 voices, and was last updated by Sigil 8 months, 3 weeks ago.
-
AuthorPosts
-
August 9, 2022 at 10:08 am #38822
Hi!
Ill try to explain my problem briefly.
1. Ive a terrain which is consisting of Chunk (GameObject)with a mesh that holds a set of blocks, what basically is a lowest unit in terrain. Those blocks can be created and destroyed at runtime. This terrain is generated at runtime and blocks can be edited at runtime.
Image
As You can probably see - ive added a Convex mesh collider, but it seems that convex wont do the thing here.
Here i have the same chunk with meshcollider, but its not convex anymore. Ive emphasized two blocks with cycles.
2. This terrain uses custom Physics and with all that - no Unity raycast support (custom intersection logic / raycasts). For example, Player and AIs use custom character controller;
3. As Terrain is generated at runtime and can be updated at runtime (each chunk can be changed in realtime) - AFAIK there is no way to actually use RAIN Navmesh for navigation (runtime navmesh gen is not an option because on terrain create it will work, but at runtime chunk updates it would freeze the game as Navmeshes should be re-generated);
For example, i want to suddenly create a hole - AI should be immediately be aware of that and avoid it.
4. This Meshcollider that im adding to each chunk is a unused feature - its added specially for RAIN (currently it uses memory for no reason - if there is a way to not use it - i would love it and remove it);
What i have done so far?
1. Added CustomCharacterMotor and managed to use the custom CharacterController that this VoxelEngine uses. See code here -> http://hastebin.com/oludobejak.avrasm
Movement is working fine.
2. Added Custom Navigator, but havent got any functionality working - thats why i am writing here. Current version: http://hastebin.com/poxinirofi.csI would love to somehow get AIs to be aware of current Terrain(the same layer where they are)
do i need to create custom pathfinding or somehow register my blocks (they are not gameObjects) for RAIN as obstacles or somehow build a navmesh-like mesh for each chunk that AI uses for pathfinding ?Any ideas? Sigil, perhaps You can help me out with this.
Please dont hesitate to ask more questions or correct me regarding this problem. I need to lauch this game ASAP.
Best regards,
Sacristan- This topic was modified 9 months ago by Sacristan.
August 9, 2022 at 10:22 am #38824The problem here is that Navigator (and many other things) lack documentation.
I pretty much have read through many forum topics - here are few of those:
http://rivaltheory.com/forums/topic/custom-navigator-for-a-pathfinding-project/
http://rivaltheory.com/forums/topic/custom-nav-mesh/
http://rivaltheory.com/forums/topic/destructable-voxel-terrain-and-navmeshes/Btw, as ill be using WebGL i cant use Threads
August 10, 2022 at 8:47 pm #38836Hi @Sacristan, thanks for coming to the forums,
I know we’ve spoken a couple times (through Jester) about your problem, but I’m glad you’re here as I think we can put together a solution for it, as I have a few ideas (better than the Unity Navigation Mesh one that I mentioned in email).
I’ll look over the code you posted, and see if I can’t put something together. My initial thoughts are this: The only real static content in any RAIN navigation mesh are the vertices, which should line up very well with your voxel data (I’m assuming). After I add a RemoveNode function to the Navigation Mesh, I see no reason why we can’t setup a way to add and remove polygons from the Navigation Mesh as voxels are added and removed from your mesh. I need to look into improving our path smoothing as well though, to handle the movement across polys with lots of interior vertices, but I think that is doable, and necessary anyhow.
I’ll start by setting up some code to add voxel data to a custom navigation mesh and create an initial set of walkable polys.
August 10, 2022 at 10:54 pm #38854Ty for Your awesome response @Sigil!
I have been talking with @Jester mostly, but its a lot better to post a problem here - perhaps someone has something similar.
Sac
August 11, 2022 at 11:21 am #38874So here is a first pass at something, just to start feeling things out. This script generates a voxel like Navigation Mesh based on the parameters it has. You can put this on a Navigation Mesh and try it out (hit Create Vertices, then Add Polys in the component).
If you leave this in the root of the scene it will create the Navigation Mesh at (0, 0, 0), but if you parent it to a GameObject you can actually move it around. You can kinda see how you could parent one of these to each of your voxel blocks, so that each one has it’s own setup.
This really is just a first pass to start seeing how you can work it into your code. It doesn’t have a way of turning off any of the squares, which I will mess around with next. Try this in a blank scene first just to see how it works, make sure it has a Navigation Mesh on the object too, let me know if there are issues.
using RAIN.Navigation.NavMesh; using UnityEngine; [ExecuteInEditMode] public class CubeNavMesh : MonoBehaviour { [SerializeField] private bool _createVertices = false; [SerializeField] private bool _addPolys = false; [SerializeField] private int _cubeCount = 5; [SerializeField] private float _cubeSize = 2; private NavMeshRig _rig = null; public void Update() { if (_rig == null) _rig = gameObject.GetComponent<NavMeshRig>(); if (_rig == null) return; if (_createVertices) { _createVertices = false; // Clear the graph so that we start fresh _rig.NavMesh.ClearNavigationGraph(); // We need one more vertex to handle our cubes properly int tVertexPerRow = _cubeCount + 1; int tVertexPerPlane = tVertexPerRow * tVertexPerRow; // Our Navigation Mesh will be multiple levels of squares Vector3[] tVertices = new Vector3[tVertexPerRow * tVertexPerRow * tVertexPerRow]; for (int i = 0; i < tVertices.Length; i++) { tVertices[i] = new Vector3((i % tVertexPerRow) - (_cubeCount / 2f), (i / tVertexPerPlane) - (_cubeCount / 2f), (i / tVertexPerRow) % tVertexPerRow - (_cubeCount / 2f)); tVertices[i] *= _cubeSize; } _rig.NavMesh.Graph.AddVertices(tVertices); _rig.NavMesh.RegisterNavigationGraph(); // Editor has to serialize to save if (!Application.isPlaying) _rig.Serialize(); } if (_addPolys) { _addPolys = false; if (_rig.NavMesh.Graph.Vertices.Count == 0) return; int tPolyCount = _cubeCount * _cubeCount * _cubeCount; int tPolyPerPlane = _cubeCount * _cubeCount; int tVertexPerRow = _cubeCount + 1; int tVertexPerPlane = tVertexPerRow * tVertexPerRow; for (int i = 0; i < tPolyCount; i++) { int tFirstRow = (i / tPolyPerPlane) * tVertexPerPlane + (i / _cubeCount) % _cubeCount * tVertexPerRow; int tSecondRow = (i / tPolyPerPlane) * tVertexPerPlane + ((i / _cubeCount) % _cubeCount + 1) * tVertexPerRow; // Our quad int[] tPoly = new int[] { (i % _cubeCount) + tFirstRow, (i % _cubeCount) + tSecondRow, ((i % _cubeCount) + 1) + tSecondRow, ((i % _cubeCount) + 1) + tFirstRow, }; // Our triangle int[] tTriangles = new int[] { (i % _cubeCount) + tFirstRow, (i % _cubeCount) + tSecondRow, ((i % _cubeCount) + 1) + tSecondRow, ((i % _cubeCount) + 1) + tSecondRow, ((i % _cubeCount) + 1) + tFirstRow, (i % _cubeCount) + tFirstRow, }; // Our edges int[] tEdges = new int[tPoly.Length * 2]; for (int j = 0; j < tEdges.Length; j += 2) { tEdges[j] = tPoly[j / 2]; tEdges[j + 1] = tPoly[(j / 2 + 1) % tPoly.Length]; } // Create our navigation mesh poly NavMeshPoly tNavMeshPoly = new NavMeshPoly(_rig.NavMesh.Graph, tPoly, tTriangles); _rig.NavMesh.Graph.AddNode(tNavMeshPoly); // And create all of our navigation mesh edges for (int j = 0; j < tEdges.Length; j += 2) { NavMeshEdge tNavMeshEdge = new NavMeshEdge(_rig.NavMesh.Graph, tEdges[j], tEdges[j + 1]); _rig.NavMesh.Graph.AddNode(tNavMeshEdge); tNavMeshPoly.AddEdgeNode(tNavMeshEdge); tNavMeshEdge.AddPolyNode(tNavMeshPoly); } // And connect them tNavMeshPoly.ConnectAllEdges(); } // Turn our display on _rig.NavMesh.DisplayMode = RAIN.Navigation.NavMesh.NavMesh.DisplayModeEnum.NavigationMesh; // Editor has to serialize to save if (!Application.isPlaying) _rig.Serialize(); } } }
August 11, 2022 at 12:18 pm #38875Great!
Already had some fun with it today. Ill check it in more depth tomorrow.
August 12, 2022 at 4:11 am #38880It looks like currently this is only Editor time option.
// Editor has to serialize to save if (!Application.isPlaying) _rig.Serialize();
Ill have to update this navmesh at runtime.
Currently this greatly convers layers——-
I would need to set up this for every and each of chunk AFAIK. Here i would need to write the cube count and cube size grid, it would be great that a square within square layer grid could be turned off (as You mentioned)It pretty much have to work on terrain like
Here it can be seen that there is 3, 2 and 1 block depth hole between two even “platforms”. It would be great to ignore these holes during navmesh generation, so when AI needs to get from even platform 1 to even platform 2, it would not go through the hole, but around it.August 12, 2022 at 9:01 pm #38887As for the option that is editor only, serialization only matters when you are in the editor, so that is why that check is there. If you use it at runtime it will work all the same. I only put the line there so that if you generated the Navigation Mesh while in the editor it would save properly.
So of course, this is just a first pass and is incomplete. What I would like to see is if you can start integrating this into your code and what problems you run into. Can you get it to line up properly with your chunks and blocks?
I’ve already realized there is an issue with the way the Navigation Mesh is generated that will cause problems: it isn’t taking agent size into account. So I’m going to take a look at that while I add in the adding/removing of sections.
No worries though, I’ll be working on this for awhile, I like to see how RAIN is being used and what I can do to improve it.
August 12, 2022 at 10:47 pm #38891Here is an updated version with some AddSquare/RemoveSquare functions. These functions mark sections as unwalkable, but not in a voxel way. This may not work properly for pathing, as the walkable/unwalkable setup doesn’t work quite right with the current Navigation Meshes, I think. Still working on it:
using RAIN.Navigation.NavMesh; using UnityEngine; [ExecuteInEditMode] public class CubeNavMesh : MonoBehaviour { [SerializeField] private bool _createVertices = false; [SerializeField] private bool _addPolys = false; [SerializeField] private int _cubeCount = 5; [SerializeField] private float _cubeSize = 2; private NavMeshRig _rig = null; public void Update() { if (_rig == null) _rig = gameObject.GetComponent<NavMeshRig>(); if (_rig == null) return; if (_createVertices) { _createVertices = false; // Clear the graph so that we start fresh _rig.NavMesh.ClearNavigationGraph(); // We need one more vertex to handle our cubes properly int tVertexPerRow = _cubeCount + 1; int tVertexPerPlane = tVertexPerRow * tVertexPerRow; // Our Navigation Mesh will be multiple levels of squares Vector3[] tVertices = new Vector3[tVertexPerRow * tVertexPerRow * tVertexPerRow]; for (int i = 0; i < tVertices.Length; i++) { tVertices[i] = new Vector3((i % tVertexPerRow) - (_cubeCount / 2f), (-i / tVertexPerPlane) + (_cubeCount / 2f), (i / tVertexPerRow) % tVertexPerRow - (_cubeCount / 2f)); tVertices[i] *= _cubeSize; } _rig.NavMesh.Graph.AddVertices(tVertices); _rig.NavMesh.RegisterNavigationGraph(); // Editor has to serialize to save if (!Application.isPlaying) _rig.Serialize(); } if (_addPolys) { _addPolys = false; if (_rig.NavMesh.Graph.Vertices.Count == 0) return; int tPolyCount = _cubeCount * _cubeCount * _cubeCount; int tPolyPerPlane = _cubeCount * _cubeCount; int tVertexPerRow = _cubeCount + 1; int tVertexPerPlane = tVertexPerRow * tVertexPerRow; for (int i = 0; i < tPolyCount; i++) { int tFirstRow = (i / tPolyPerPlane) * tVertexPerPlane + (i / _cubeCount) % _cubeCount * tVertexPerRow; int tSecondRow = (i / tPolyPerPlane) * tVertexPerPlane + ((i / _cubeCount) % _cubeCount + 1) * tVertexPerRow; // Our quad int[] tPoly = new int[] { (i % _cubeCount) + tFirstRow, (i % _cubeCount) + tSecondRow, ((i % _cubeCount) + 1) + tSecondRow, ((i % _cubeCount) + 1) + tFirstRow, }; // Our triangle int[] tTriangles = new int[] { (i % _cubeCount) + tFirstRow, (i % _cubeCount) + tSecondRow, ((i % _cubeCount) + 1) + tSecondRow, ((i % _cubeCount) + 1) + tSecondRow, ((i % _cubeCount) + 1) + tFirstRow, (i % _cubeCount) + tFirstRow, }; // Our edges int[] tEdges = new int[tPoly.Length * 2]; for (int j = 0; j < tEdges.Length; j += 2) { tEdges[j] = tPoly[j / 2]; tEdges[j + 1] = tPoly[(j / 2 + 1) % tPoly.Length]; } // Create our navigation mesh poly NavMeshPoly tNavMeshPoly = new NavMeshPoly(_rig.NavMesh.Graph, tPoly, tTriangles); _rig.NavMesh.Graph.AddNode(tNavMeshPoly); // And create all of our navigation mesh edges for (int j = 0; j < tEdges.Length; j += 2) { NavMeshEdge tNavMeshEdge = new NavMeshEdge(_rig.NavMesh.Graph, tEdges[j], tEdges[j + 1]); _rig.NavMesh.Graph.AddNode(tNavMeshEdge); tNavMeshPoly.AddEdgeNode(tNavMeshEdge); tNavMeshEdge.AddPolyNode(tNavMeshPoly); } // And connect them tNavMeshPoly.ConnectAllEdges(); } // Turn our display on _rig.NavMesh.DisplayMode = RAIN.Navigation.NavMesh.NavMesh.DisplayModeEnum.NavigationMesh; // Let's mark all cubes under our first layer as unwalkable, if you remove top portions the rest will show up for (int i = _cubeCount * _cubeCount; i < _cubeCount * _cubeCount * _cubeCount; i++) RemoveSquare(i); // Editor has to serialize to save if (!Application.isPlaying) _rig.Serialize(); } } public void AddSquare(int aX, int aY, int aZ) { AddSquare(aX + aY * _cubeCount * _cubeCount + aZ * _cubeCount); } public void AddSquare(int aSquare) { // There is a cube every five nodes, so that'll be the node we add ((NavMeshPoly)_rig.NavMesh.Graph.GetNode(aSquare * 5)).MarkWalkable(); } public void RemoveSquare(int aX, int aY, int aZ) { RemoveSquare(aX + aY * _cubeCount * _cubeCount + aZ * _cubeCount); } public void RemoveSquare(int aSquare) { ((NavMeshPoly)_rig.NavMesh.Graph.GetNode(aSquare * 5)).MarkUnwalkable(); } }
August 12, 2022 at 10:49 pm #38892Oh, forgot to say that it now removes all the squares beneath the top layer, as if it was a solid cube… it expects you to call AddSquare/RemoveSquare to add and remove squares from this point on. Perhaps that is a bad default, maybe all squares should be removed to begin with and you add them as needed… easy enough to change.
August 15, 2022 at 12:39 pm #38920With this im adding CubeNavMesh -> http://hastebin.com/kajiqatevi.avrasm
This is current CubeNavMesh -> http://hastebin.com/ikopocokig.cs
The error is when trying to draw the navmesh graphNullReferenceException: Object reference not set to an instance of an object RAINEditor.Navigation.NavMesh.NavMeshEditor.DrawNavMeshForScene (RAIN.Serialization.FieldWalker aWalker) RAINEditor.Navigation.NavMesh.NavMeshEditor.DrawGizmo (GizmoType aType, RAIN.Serialization.FieldWalker aWalker) RAINEditor.TypeEditors.RAINTypeEditor.DrawFieldForGizmo (GizmoType aType, RAIN.Serialization.FieldWalker aWalker) RAINEditor.Core.RAINComponentEditor.OnGizmoGUI (RAIN.Core.RAINComponent aComponent, GizmoType aType) UnityEditor.DockArea:OnGUI()
I get this when manually adding CubeNavMesh to chunk during play (no cubemesh added previously)
The little detail i missed - This voxel terrain is runtime only.
August 15, 2022 at 12:51 pm #38921August 16, 2022 at 9:05 am #38923When CubeNavmesh has been added With AddComponent<Type>() and i try to create vertices and polys - same RAIN GUI error persists - nothing is displayed.
August 18, 2022 at 8:27 am #38928Sorry @Sacristan, was pretty busy this weekend and wasn’t able to check on this. I will take another pass and try to improve some more on it. I know one of my next steps was to get it working properly with agent size, I am also curious about how hard this is going to hit memory wise.
Sharing the project may work as well, I will talk to Jester about it.
-
AuthorPosts
You must be logged in to reply to this topic.