jme3_ai.html 65 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536
  1. <!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=edge"><![endif]--><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="Asciidoctor 1.5.4"><title>jMonkeyEngine Artificial Intelligence</title><link rel="stylesheet" href="./asciidoctor.css">
  2. <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.5.0/css/font-awesome.min.css">
  3. <link rel="stylesheet" href="./coderay-asciidoctor.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/docsearch.js/2/docsearch.min.css"><link rel="stylesheet" href="/home/travis/build/jMonkeyEngine/wiki/build/asciidoc/html5/jme3/advanced/twemoji-awesome.css"></head><body class="article toc2 toc-left"><div id="header"><div id="toolbar"><a href="https://github.com/jMonkeyEngine/wiki/edit/master/src/docs/asciidoc/jme3/advanced/jme3_ai.adoc"><i class="fa fa-pencil-square" aria-hidden="true"></i></a><a href="https://github.com/jMonkeyEngine/wiki/new/master/src/docs/asciidoc/jme3/advanced/"><i class="fa fa-plus-square" aria-hidden="true"></i></a><input dir="auto" style="position: relative; vertical-align: top;" spellcheck="false" autocomplete="off" class="searchbox__input aa-input" id="doc-search" name="search" placeholder="Search in the doc" required="required" type="search"></div><h1>jMonkeyEngine Artificial Intelligence</h1><div class="details"><span class="author" id="author"></span><br><span id="revnumber">version ,</span> <span id="revdate">2017/04/15 13:30</span></div><div id="toc" class="toc2"><div id="toctitle">Table of Contents</div><ul class="sectlevel1"><li><a href="#requirements">Requirements</a></li><li><a href="#use-example">Use Example</a></li><li><a href="#navmesh-creation">NavMesh Creation</a><ul class="sectlevel2"><li><a href="#from-the-sdk">From the SDK</a></li><li><a href="#procedural-method">Procedural Method</a></li></ul></li><li><a href="#pathfinding">Pathfinding</a><ul class="sectlevel2"><li><a href="#loading-the-navmesh">Loading the NavMesh</a></li><li><a href="#communicating-with-navigationcontrol">Communicating with NavigationControl</a></li><li><a href="#navigationcontrol">NavigationControl</a></li><li><a href="#pathfinding-thread">Pathfinding Thread</a></li></ul></li><li><a href="#character-movement">Character Movement</a></li><li><a href="#conclusion">Conclusion</a></li><li><a href="#other-ai-options">Other AI Options</a></li><li><a href="#further-reading">Further Reading</a></li></ul></div></div><div id="content"><div id="preamble"><div class="sectionbody"><div class="paragraph"><p>Most games written need some type of <a href="https://en.wikipedia.org/wiki/Artificial_intelligence_(video_games)">Artificial Intelligence</a> to deliver a feeling of realism, excitement or challenge to the player. AI can be as simple as having an NPC (Non Player Character) respond to some action taken by a player or as complicated as smoothly navigating your way through a scene full of obstacles without getting stuck. It&#8217;s a time-consuming and significant challenge to develop these systems so its much easier to use an existing library to do the heavy lifting for you.</p></div>
  4. <div class="paragraph"><p>Unfortunately, the jMonkeyEngine comes with no official library for dealing with AI. There is, however, the jme3 Artificial Intelligence library that is probably the closest there is to an official release. Although it never made it into any official releases, it was designed, in part, by core team members. It consists of two separate AI models, a <a href="https://en.wikipedia.org/wiki/Navigation_mesh">Navigation Mesh</a> library using <a href="https://en.wikipedia.org/wiki/Pathfinding">path-finding</a>, and a simple Steering Behaviours library that uses path-following.</p></div>
  5. <div class="paragraph"><p>You can read about the introduction of the library in the forum thread: <a href="https://hub.jmonkeyengine.org/t/ai-plugin-now-with-navmesh-pathfinding/24644">AI plugin now with NavMesh path-finding</a>.</p></div></div></div>
  6. <div class="sect1"><h2 id="requirements">Requirements</h2><div class="sectionbody"><div class="ulist"><ul><li><p><a href="https://github.com/MeFisto94/jme3-artificial-intelligence/releases">jme3 Artificial Intelligence Library</a> - The library and javaDocs for jme3AI. This is also where you can report problems or help in maintaining the library.</p></li><li><p><a href="https://github.com/stevefsp/critterai/releases">CritterAI</a> - Stephen Pratt&#8217;s <a href="http://www.critterai.org/projects/nmgen_study/">NMGen Study</a> project files to generate the navmesh.</p></li><li><p>To get the assets (3D models) used in this example, add the <a href="../../sdk/sample_code.html#jme3testdata-assets">jME3-testdata.jar</a> to your classpath.</p></li><li><p>Java SDK 8+.</p></li></ul></div>
  7. <div class="paragraph"><p>Stephen Pratt explains in detail the configuration parameters of CritterAI/Jme3AI in a easy to follow format and is suggested reading.</p></div>
  8. <div class="ulist"><ul><li><p><a href="http://www.critterai.org/projects/nmgen_study/config.html">Configuration Parameters</a></p></li></ul></div></div></div>
  9. <div class="sect1"><h2 id="use-example">Use Example</h2><div class="sectionbody"><div class="paragraph"><p>The jme3 Artificial Intelligence Library contains:</p></div>
  10. <div class="ulist"><ul><li><p>NavMesh - A Navigation Mesh path-finding AI system using the <a href="https://en.wikipedia.org/wiki/A*_search_algorithm">A*</a> algorithm.<span class="footnote">[<a class="footnote" id="_footnoteref_1" href="#_footnote_1" title="View footnote.">1</a>]</span></p></li><li><p>Steering - The foundations of an <a href="http://natureofcode.com/book/chapter-6-autonomous-agents/">Autonomous Agent</a> system that uses path-following and forces to move a character through its environment. Includes a test case as well.<span class="footnote">[<a class="footnote" id="_footnoteref_2" href="#_footnote_2" title="View footnote.">2</a>]</span> </p></li></ul></div>
  11. <div class="admonitionblock note"><table><tr><td class="icon"><i class="fa icon-note" title="Note"></i></td><td class="content"><div class="paragraph"><p>This scope of this tutorial is restricted to the NavMesh part of the library and expands upon the lessons taught in the <a href="../../jme3.html#tutorials-for-beginners">tutorials</a>. It demonstrates the use of some classes and methods found in the Medium and Advanced topics of the wiki as well. You can find the source code for this tutorial in the <a href="https://github.com/jMonkeyEngine/doc-examples/tree/master/src/com/jme3/examples/jme3ai">jMonkeyEngine/docs-examples</a> repository.</p></div></td></tr></table></div>
  12. <div class="paragraph"><p>Moving a character through your scene requires three things.</p></div>
  13. <div class="ulist"><ul><li><p>A navigation mesh.</p></li><li><p>A path-finding component that uses that navigation mesh to calculate a path.</p></li><li><p>A way to move the character.</p></li></ul></div></div></div>
  14. <div class="sect2"><h3 id="navmesh-creation">NavMesh Creation</h3><div class="paragraph"><p>The first thing you need for path-finding is a navigation mesh. There are two ways to generate the NavMesh, procedural or the jMonkey <a href="https://github.com/jMonkeyEngine/sdk/releases">SDK</a>.</p></div>
  15. <div class="ulist"><ul><li><p>The SDK has a built-in command, but comes with a trade-off that no parameter exceptions are thrown. This means you are flying blind when the NavMesh fails generation.</p></li><li><p>If you choose procedural, you see any generation exceptions, but you will have to do a little more work like saving, loading and/or displaying the NavMesh.</p></li></ul></div>
  16. <div class="paragraph"><p>Both methods produce exactly the same NavMesh and both will be covered in this tutorial.</p></div>
  17. <div class="sect2"><h3 id="from-the-sdk">From the SDK</h3><div class="ulist"><ul><li><p>Open your scene in the Terrain Editor or Scene Explorer by <b class="button">RMB</b> selecting the file in your assets folder and choosing <code>Edit Terrain</code> or <code>Edit in SceneComposer</code>.</p></li><li><p>Once open, <b class="button">RMB</b> select the root node in the <code>SceneExplorer</code> and then select <code><span class="menuseq"><span class="menu">Spatial</span>&#160;&#9656; <span class="menuitem">NavMesh</span></span></code>.</p></li></ul></div>
  18. <div class="paragraph"><p>This will open the <code>Create NavMesh</code> dialog with default settings. You can read in depth about each parameter by following the <code>Configuration Parameters</code> link under <a href="../../jme3/advanced/jme3_ai.html#requirements">Requirements</a>.</p></div>
  19. <div class="paragraph"><div class="title">Parameter Insight</div>
  20. <p>The jme3AI system uses CritterAI, which is based off <a href="https://github.com/recastnavigation/recastnavigation">Recast and Detour</a> navigation. The author of Recast lays out a few specific rules for NavMesh creation in this <a href="http://digestingduck.blogspot.dk/2009/08/recast-settings-uncovered.html">blog post</a>, which logically apply to jme3AI. Below is a translation of this post as it pertains to jme3AI.</p></div>
  21. <div class="ulist"><ul><li><p>First you should decide the size of your character "capsule". For example, if you are using meters as units in your game world, a good size of human sized character might be (r)adius=0.4, (h)eight=2.0.</p></li><li><p>Next the voxelization cell size (cs) will be derived from that. Usually good value for cs is r/2 or r/3. In outdoor environments, r/2 might be enough, indoors you sometimes want the extra precision and you might choose to use r/3 or smaller.</p></li><li><p>The voxelization cell height (ch) is defined separately in order to allow greater precision in height tests. Good starting point for ch is cs/2. If you get small holes where there are discontinuities in the height (steps), you may want to decrease cell height.</p></li><li><p>Next up is the character definition values. First up is <code>minTraversableHeight</code>, which defines the height of the agent.</p></li><li><p>The <code>maxTraversableStep</code> defines how high steps the character can climb.</p></li><li><p>The parameter <code>traversableAreaBorderSize</code> defines the agent radius. If this value is greater than zero, the navmesh will be shrunken by the <code>traversableAreaBorderSize</code>. If you want to have tight fit navmesh, use zero radius.</p></li><li><p>The parameter <code>maxTraversableSlope</code> is used before voxelization to check if the slope of a triangle is too high and those polygons will be given a non-walkable flag. The parameter is in radians.</p></li><li><p>In certain cases really long outer edges may decrease the triangulation results. Sometimes this can be remedied by just tessellating the long edges. The parameter <code>maxEdgeLength</code> defines the max
  22. edge length. A good value for <code>maxEdgeLength</code> is something like <code>traversableAreaBorderSize*8</code>. A good way to tweak this value is to first set it really high and see if your data creates long edges. If so, then try to find as big value as possible which happen to create those few extra vertices which makes the tessellation better.</p></li><li><p>When the rasterized areas are converted back to vectorized representation the <code>edgeMaxDeviation</code> describes how loosely the simplification is done. Good values are between 1.1-1.5 (1.3 usually yield good results). If the value is less, some stair-casing starts to appear at the edges and if it is more than that, the simplification starts to cut some corners.</p></li></ul></div>
  23. <div class="admonitionblock note"><table><tr><td class="icon"><i class="fa icon-note" title="Note"></i></td><td class="content">A summary of the parameter effects is included in the comments of the <a href="https://github.com/jMonkeyEngine/doc-examples/blob/master/src/com/jme3/examples/jme3ai/ai/NavMeshState.java">NavMeshState.java</a> file and discussed in the Procedural code examples that follow this section.</td></tr></table></div>
  24. <div class="paragraph"><p>If there are problems with your parameter settings, you will only know if the NavMesh doesn&#8217;t appear under the Node you selected and there is no task running in the status area located at the bottom right of the SDK.</p></div>
  25. <div class="paragraph"><p>If the NavMesh doesn&#8217;t appear, then you will have to make adjustments to the <code>Configuration Parameters</code> until it completes successfully. Minor adjustments to cell size will usually work.</p></div>
  26. <div class="admonitionblock caution"><table><tr><td class="icon"><i class="fa icon-caution" title="Caution"></i></td><td class="content">Cell size has the greatest impact on your NavMesh. The smaller the cell size, the more accurate the NavMesh, the longer it takes to generate. Generating a 1024x1024 NavMesh can take anywhere from 30 seconds to ten minutes to complete, depending on terrain complexity. Even larger NavMeshes can take many hours.</td></tr></table></div>
  27. <div class="admonitionblock tip"><table><tr><td class="icon"><i class="fa icon-tip" title="Tip"></i></td><td class="content">Selecting the NavMesh node in the SceneExplorer will show the NavMesh in the Terrain Editor or SceneComposer view-port. If it doesn&#8217;t show, with the NavMesh node selected, change the <code>Cull Hint</code> to <code>Never</code> in the <code>NavMesh - Properties</code> panel.</td></tr></table></div></div>
  28. <div class="sect2"><h3 id="procedural-method">Procedural Method</h3><div class="paragraph"><p>There are many ways to create a NavMesh. If you look at the constructor for the <a href="https://github.com/jMonkeyEngine/doc-examples/blob/master/src/com/jme3/examples/jme3ai/Jme3AI.java">Jme3AI.java</a> file, you will see I use a <a href="../../jme3/advanced/application_states.html#baseappstate">BaseAppState</a> named <a href="https://github.com/jMonkeyEngine/doc-examples/blob/master/src/com/jme3/examples/jme3ai/ai/NavMeshState.java">NavMeshState.java</a> which creates a <code>generator</code> object and builds the <code>NavMesh</code> new every time the program is ran.</p></div>
  29. <div class="listingblock"><div class="title">Jme3AI constructor</div>
  30. <div class="content"><pre class="CodeRay highlight"><code data-lang="java"><span class="directive">public</span> Jme3AI() {
  31. <span class="local-variable">super</span>(<span class="keyword">new</span> StatsAppState(), <span class="keyword">new</span> DebugKeysAppState(), <span class="keyword">new</span> TerrainState(),
  32. <span class="keyword">new</span> NavMeshState(), <span class="keyword">new</span> PCState(), <span class="keyword">new</span> KeyboardRunState());
  33. }</code></pre></div></div>
  34. <div class="paragraph"><p>It can take from seconds to hours to build a NavMesh, depending on how complicated it is. Therefore, you would normally build the NavMesh or meshes, add them to your <code>Assets</code> folder and load them at startup. The <code>NavMeshState</code> and <code>NavMeshGenerator</code> classes are both convenience classes and are not required to create a NavMesh. If you wish to keep your game minimalist, you can set the variables for the CritterAI NavmeshGenerator (note the lower case 'm' in mesh) in the method call directly or by variable, and pass the IndexBuffer and VertexBuffer of your mesh into the CritterAI NavmeshGenerator object.</p></div>
  35. <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java">NavmeshGenerator nmgen = <span class="keyword">new</span> NavmeshGenerator(cellSize, cellHeight, minTraversableHeight,
  36. maxTraversableStep, maxTraversableSlope,
  37. clipLedges, traversableAreaBorderSize,
  38. smoothingThreshold, useConservativeExpansion,
  39. minUnconnectedRegionSize, mergeRegionSize,
  40. maxEdgeLength, edgeMaxDeviation, maxVertsPerPoly,
  41. contourSampleDistance, contourMaxDeviation);
  42. ...
  43. Get mesh buffers and set IntermediateData
  44. ...
  45. <span class="comment">//Pass buffers and IntermediateData to build process</span>
  46. TriangleMesh triMesh = nmgen.build(positions, indices, intermediateData);
  47. ...
  48. Process trimesh
  49. ...</code></pre></div></div>
  50. <div class="paragraph"><p>Let&#8217;s examine what it takes to create the <code>NavMesh</code> using the <code>NavMeshState</code> and <code>NavMeshGenerator</code> helper classes.</p></div>
  51. <div class="listingblock"><div class="title">NavMeshState NavMesh generation method</div>
  52. <div class="content"><pre class="CodeRay highlight"><code data-lang="java"><span class="comment">/**
  53. * creates the NavMesh
  54. */</span>
  55. <span class="directive">private</span> <span class="type">void</span> createNavMesh() {
  56. generator = <span class="keyword">new</span> NavMeshGenerator();
  57. <span class="comment">//The width and depth resolution used when sampling the source geometry.</span>
  58. <span class="comment">//outdoors = agentRadius/2, indoors = agentRadius/3, cellSize =</span>
  59. <span class="comment">//agentRadius for very small cells.</span>
  60. <span class="comment">//Constraints &gt; 0 , default=1</span>
  61. generator.setCellSize(<span class="float">.25f</span>);
  62. <span class="comment">//The height resolution used when sampling the source geometry.</span>
  63. <span class="comment">//minTraversableHeight, maxTraversableStep, and contourMaxDeviation</span>
  64. <span class="comment">//will need to be greater than the value of cellHeight in order to</span>
  65. <span class="comment">//function correctly. maxTraversableStep is especially susceptible to</span>
  66. <span class="comment">//impact from the value of cellHeight.</span>
  67. <span class="comment">//cellSize/2</span>
  68. <span class="comment">//Constraints &gt; 0, default=1.5</span>
  69. generator.setCellHeight(<span class="float">.125f</span>);
  70. <span class="comment">//Represents the minimum floor to ceiling height that will still allow</span>
  71. <span class="comment">//the floor area to be considered traversable.</span>
  72. <span class="comment">//minTraversableHeight should be at least two times the value of</span>
  73. <span class="comment">//cellHeight in order to get good results. Max spatial height.</span>
  74. <span class="comment">//Constraints &gt; 0, default=7.5</span>
  75. generator.setMinTraversableHeight(<span class="float">2f</span>);
  76. <span class="comment">//Represents the maximum ledge height that is considered to still be</span>
  77. <span class="comment">//traversable.</span>
  78. <span class="comment">//maxTraversableStep should be greater than two times cellHeight.</span>
  79. <span class="comment">//Constraints &gt;= 0, default=1</span>
  80. generator.setMaxTraversableStep(<span class="float">0.3f</span>);
  81. <span class="comment">//The maximum slope that is considered traversable. (In degrees.)</span>
  82. <span class="comment">//Constraints &gt;= 0, default=48</span>
  83. generator.setMaxTraversableSlope(<span class="float">50.0f</span>);
  84. <span class="comment">//Indicates whether ledges should be considered un-walkable.</span>
  85. <span class="comment">//Constraints None, default=false</span>
  86. generator.setClipLedges(<span class="predefined-constant">false</span>);
  87. <span class="comment">//Represents the closest any part of a mesh can get to an obstruction in</span>
  88. <span class="comment">//the source geometry.</span>
  89. <span class="comment">//traversableAreaBorderSize value must be greater than the cellSize to</span>
  90. <span class="comment">//have an effect. Radius of the spatial.</span>
  91. <span class="comment">//Constraints &gt;= 0, default=1.2</span>
  92. generator.setTraversableAreaBorderSize(<span class="float">0.6f</span>);
  93. <span class="comment">//The amount of smoothing to be performed when generating the distance</span>
  94. <span class="comment">//field used for deriving regions.</span>
  95. <span class="comment">//Constraints &gt;= 0, default=2</span>
  96. generator.setSmoothingThreshold(<span class="integer">0</span>);
  97. <span class="comment">//Applies extra algorithms to help prevent malformed regions from</span>
  98. <span class="comment">//forming.</span>
  99. <span class="comment">//Constraints None, default=true</span>
  100. generator.setUseConservativeExpansion(<span class="predefined-constant">true</span>);
  101. <span class="comment">//The minimum region size for unconnected (island) regions.</span>
  102. <span class="comment">//Constraints &gt; 0, default=3</span>
  103. generator.setMinUnconnectedRegionSize(<span class="integer">8</span>);
  104. <span class="comment">//Any regions smaller than this size will, if possible, be merged with</span>
  105. <span class="comment">//larger regions.</span>
  106. <span class="comment">//Constraints &gt;= 0, default=10</span>
  107. generator.setMergeRegionSize(<span class="integer">20</span>);
  108. <span class="comment">//The maximum length of polygon edges that represent the border of</span>
  109. <span class="comment">//meshes.</span>
  110. <span class="comment">//setTraversableAreaBorderSize * 8</span>
  111. <span class="comment">//Constraints &gt;= 0, default=0</span>
  112. generator.setMaxEdgeLength(<span class="float">4.0f</span>);
  113. <span class="comment">//The maximum distance the edges of meshes may deviate from the source</span>
  114. <span class="comment">//geometry.</span>
  115. <span class="comment">//1.1 to 1.5 for best results.</span>
  116. <span class="comment">//Constraints &gt;= 0 , default=2.4</span>
  117. generator.setEdgeMaxDeviation(<span class="float">1.3f</span>);
  118. <span class="comment">//The maximum number of vertices per polygon for polygons generated</span>
  119. <span class="comment">//during the voxel to polygon conversion process.</span>
  120. <span class="comment">//Constraints &gt;= 3, default=6</span>
  121. generator.setMaxVertsPerPoly(<span class="integer">6</span>);
  122. <span class="comment">//Sets the sampling distance to use when matching the detail mesh to the</span>
  123. <span class="comment">//surface of the original geometry.</span>
  124. <span class="comment">//Constraints &gt;= 0, default=25</span>
  125. generator.setContourSampleDistance(<span class="float">5.0f</span>);
  126. <span class="comment">//The maximum distance the surface of the detail mesh may deviate from</span>
  127. <span class="comment">//the surface of the original geometry.</span>
  128. <span class="comment">//Constraints &gt;= 0, default=25</span>
  129. generator.setContourMaxDeviation(<span class="float">5.0f</span>);
  130. <span class="comment">//Time allowed before generation process times out in miliseconds.</span>
  131. <span class="comment">//default=10000</span>
  132. generator.setTimeout(<span class="integer">40000</span>);
  133. <span class="comment">//the data object to use for storing data related to building the</span>
  134. <span class="comment">//navigation mesh.</span>
  135. IntermediateData data = <span class="keyword">new</span> IntermediateData();
  136. generator.setIntermediateData(data);
  137. Mesh mesh = <span class="keyword">new</span> Mesh();
  138. GeometryBatchFactory.mergeGeometries(findGeometries(app.getRootNode(),
  139. <span class="keyword">new</span> <span class="predefined-type">LinkedList</span>&lt;&gt;(), generator), mesh);
  140. <span class="comment">//uncomment to show mesh</span>
  141. <span class="comment">// Geometry meshGeom = new Geometry(&quot;MeshGeometry&quot;);</span>
  142. <span class="comment">// meshGeom.setMesh(mesh);</span>
  143. <span class="comment">// showGeometry(meshGeom, ColorRGBA.Yellow);</span>
  144. <span class="comment">// saveNavMesh(meshGeom);</span>
  145. Mesh optiMesh = generator.optimize(mesh);
  146. navMesh.loadFromMesh(optiMesh);
  147. Geometry geom = <span class="keyword">new</span> Geometry(DataKey.NAVMESH);
  148. geom.setMesh(optiMesh);
  149. <span class="comment">//display the mesh</span>
  150. showGeometry(geom, ColorRGBA.Green);
  151. <span class="comment">//save the navmesh to Scenes/NavMesh for loading</span>
  152. exportNavMesh(geom, DataKey.NAVMESH);
  153. <span class="comment">//save geom to rootNode if you wish</span>
  154. saveNavMesh(geom);
  155. }</code></pre></div></div>
  156. <div class="paragraph"><p>First, we create the <a href="https://github.com/jMonkeyEngine/doc-examples/blob/master/src/com/jme3/examples/jme3ai/ai/NavMeshGenerator.java">NavMeshGenerator</a> object and then use it to set the parameters for the NavMesh.</p></div>
  157. <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java">generator = <span class="keyword">new</span> NavMeshGenerator();
  158. ...
  159. generator.setCellSize(<span class="float">.25f</span>);
  160. ...</code></pre></div></div>
  161. <div class="paragraph"><p>In our next step we create an IntermediateData object.</p></div>
  162. <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java"><span class="comment">//the data object to use for storing data related to building the</span>
  163. <span class="comment">//navigation mesh.</span>
  164. IntermediateData data = <span class="keyword">new</span> IntermediateData();
  165. generator.setIntermediateData(data);</code></pre></div></div>
  166. <div class="paragraph"><p>The IntermediateData object can be used to get information about the build process of the NavMesh such as build times. You query this object after building the NavMesh. If you don&#8217;t wish to see the data, set it to null.</p></div>
  167. <div class="paragraph"><p>At this point, you now have a <code>generator</code> object that you use to create the NavMesh with.</p></div>
  168. <div class="paragraph"><p>Included in the <a href="https://github.com/jMonkeyEngine/doc-examples/blob/master/src/com/jme3/examples/jme3ai/ai/NavMeshState.java">NavMeshState.java</a> file is the helper method <code>findGeometries</code>.</p></div>
  169. <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java"><span class="comment">//Gathers all geometries in supplied node into supplied List. Uses</span>
  170. <span class="comment">//NavMeshGenerator to merge found Terrain meshes into one geometry prior to</span>
  171. <span class="comment">//adding. Scales and sets translation of merged geometry.</span>
  172. <span class="directive">private</span> <span class="predefined-type">List</span>&lt;Geometry&gt; findGeometries(Node node, <span class="predefined-type">List</span>&lt;Geometry&gt; geoms,
  173. NavMeshGenerator generator)</code></pre></div></div>
  174. <div class="paragraph"><p>It is used to collect all geometries, attached to a node, into a List. If a child of the node is a Terrain instance (which can consist of many meshes), it will use the <code>generator</code> object to merge them into one mesh, then scale and set translation of the merged mesh prior to being added to the list. You then use GeometryBatchFactory to merge all the geometries in the list into a single <code>mesh</code> object.</p></div>
  175. <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java">Mesh mesh = <span class="keyword">new</span> Mesh();
  176. GeometryBatchFactory.mergeGeometries(findGeometries(app.getRootNode(),
  177. <span class="keyword">new</span> <span class="predefined-type">LinkedList</span>&lt;&gt;(), generator), mesh);</code></pre></div></div>
  178. <div class="paragraph"><p>After these methods execute, you have a single <code>mesh</code> object that is now ready to be optimized.</p></div>
  179. <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java">Mesh optiMesh = generator.optimize(mesh);</code></pre></div></div>
  180. <div class="paragraph"><p>This is where the parameters you set with the <code>generator</code> object are applied to the supplied <code>mesh</code>. The optimize method will return a new Mesh object that reflects your generator settings. Now is when any problems with your parameters will show themselves as either warnings or exceptions. You should keep changing the various parameters, one at a time and in small increments/decrements, until your <code>mesh</code> generates with no errors. See each parameter&#8217;s notes for suggestions on how to do so.</p></div>
  181. <div class="paragraph"><p>After the mesh generates, you need to link all of its cells together so it can be used as your <code>NavMesh</code> object. You do this by calling <code>loadFromMesh()</code> or <code>loadFromData()</code>, depending on your implementation, on your <code>optiMesh</code> object.</p></div>
  182. <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java">navMesh.loadFromMesh(optiMesh);</code></pre></div></div>
  183. <div class="paragraph"><p>If you look at the second contructor for the <code>NavMesh</code> class you will see this is all it does. You would use this constructor if you were loading a <code>Mesh</code> from a geometry that had already been optimized and saved into your <code>Assets</code> folder for example.</p></div>
  184. <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java"><span class="directive">public</span> NavMesh(Mesh mesh) {
  185. loadFromMesh(mesh);
  186. }</code></pre></div></div>
  187. <div class="paragraph"><p>The <code>NavMesh</code> object is now ready for use in your game, but you still need to create the geometry for it if you wish to save or view it. You do this the same as you would for any newly created mesh.</p></div>
  188. <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java">Geometry geom = <span class="keyword">new</span> Geometry(DataKey.NAVMESH);
  189. geom.setMesh(navMesh);</code></pre></div></div>
  190. <div class="paragraph"><p>Now that you have your Mesh you should save it.</p></div>
  191. <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java"><span class="comment">//save the navmesh to Scenes/NavMesh for loading</span>
  192. exportNavMesh(geom, DataKey.NAVMESH);
  193. <span class="comment">//save geom to rootNode if you wish</span>
  194. saveNavMesh(geom);</code></pre></div></div>
  195. <div class="paragraph"><p>In this instance, the object is exported to the projects <code>Assets</code> folder so it can be loaded rather than generated every time the game starts. This is the preferred method. The <code>saveNavMesh()</code> method just attaches the geometry to the <code>rootNode</code>. How and where you choose to save depends on your implementation and personal preferences.</p></div></div></div>
  196. <div class="sect2"><h3 id="pathfinding">Pathfinding</h3><div class="paragraph"><p>There are many ways to implement the <code>NavMeshPathfinder</code> class of the jme3AI library. You can create a control, instantiate the <code>NavMeshPathFinder</code> class, and query the newly created object in a thread. You could use a single AppState to calculate all your paths. You could, as in this tutorial, extend the NavMeshPathFinder class in a custom control.</p></div>
  197. <div class="paragraph"><p>You also need a way to communicate <code>Vector3f</code> changes to the <code>NavMeshPathfinder</code>. This tutorial uses an ActionListener and Interface. You could just as easily create a public method in the control, and call it from the ActionListener, or store the <code>Vector3f</code> in <code>UserData</code> and look for changes from the control itself.</p></div>
  198. <div class="paragraph"><p>These are implementation decisions that are left up to you.</p></div>
  199. <div class="sect2"><h3 id="loading-the-navmesh">Loading the NavMesh</h3><div class="paragraph"><p>In this tutorial example, the optimized mesh was exported as a geometry using the jMonkey binary format <code>.j3o</code>. Doing so means the loading of your <code>NavMeshes</code> is done the same way you load any model, by using the <code>AssetManager</code>. Once you load the <code>.j3o</code>, you grab its <code>Mesh</code> and create the <code>NavMesh</code> object to be passed to the <a href="https://github.com/jMonkeyEngine/doc-examples/blob/master/src/com/jme3/examples/jme3ai/ai/NavigationControl.java">NavigationControl</a> constructor. This tutorial uses a <a href="../../jme3/advanced/application_states.html#baseappstate">BaseAppState</a> for model loading so access to the <code>Application</code> class is built in.</p></div>
  200. <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java"><span class="comment">//load NavMesh geometry saved to assets folder</span>
  201. Geometry navGeom = (Geometry) getApplication().getAssetManager().
  202. loadModel(<span class="string"><span class="delimiter">&quot;</span><span class="content">Scenes/NavMesh/NavMesh.j3o</span><span class="delimiter">&quot;</span></span>);
  203. NavigationControl navControl = <span class="keyword">new</span> NavigationControl(<span class="keyword">new</span> NavMesh(
  204. navGeom.getMesh()), getApplication(), <span class="predefined-constant">true</span>)
  205. charNode.addControl(navControl);
  206. <span class="comment">//NavigationControl implements Pickable Interface</span>
  207. picked = navControl;</code></pre></div></div>
  208. <div class="admonitionblock note"><table><tr><td class="icon"><i class="fa icon-note" title="Note"></i></td><td class="content"><div class="paragraph"><p>This tutorial uses a custom control, <code>NavigationControl</code>, that extends the <code>NavMeshPathfinder</code> class. As this is a tutorial, some extra variables are used for dispalying the navigation path and are not needed. The constructor for <code>NavMeshPathfinder</code> requires just the the passing of the <code>NavMesh</code> object, which makes for a cleaner control.</p></div>
  209. <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java"><span class="directive">public</span> NavigationControl(NavMesh navMesh) {
  210. ...
  211. }</code></pre></div></div></td></tr></table></div></div>
  212. <div class="sect2"><h3 id="communicating-with-navigationcontrol">Communicating with NavigationControl</h3><div class="paragraph"><p>This tutorial makes use of the <a href="../../jme3/beginner/hello_picking.html">Hello Picking</a> and <a href="../../jme3/advanced/mouse_picking.html#pick-a-target-using-the-mouse-pointer">Mouse Picking</a> tutorials so you should already be familiar with this method for picking and how to add the <a href="../../jme3/beginner/hello_input_system.html">input mappings</a> to your game. How you implement your ActionListener is up to you.</p></div>
  213. <div class="listingblock"><div class="title">PCState ActionListener</div>
  214. <div class="content"><pre class="CodeRay highlight"><code data-lang="java"> <span class="directive">private</span> <span class="type">class</span> <span class="class">ClickedListener</span> <span class="directive">implements</span> <span class="predefined-type">ActionListener</span> {
  215. <span class="annotation">@Override</span>
  216. <span class="directive">public</span> <span class="type">void</span> onAction(<span class="predefined-type">String</span> name, <span class="type">boolean</span> isPressed, <span class="type">float</span> tpf) {
  217. <span class="keyword">if</span> (name.equals(ListenerKey.PICK) &amp;&amp; !isPressed) {
  218. CollisionResults results = <span class="keyword">new</span> CollisionResults();
  219. Vector2f click2d = getInputManager().getCursorPosition().clone();
  220. Vector3f click3d = app.getCamera().getWorldCoordinates(click2d,
  221. <span class="float">0f</span>).clone();
  222. Vector3f dir = app.getCamera().getWorldCoordinates(
  223. click2d, <span class="float">1f</span>).subtractLocal(click3d).normalizeLocal();
  224. Ray ray = <span class="keyword">new</span> Ray(click3d, dir);
  225. app.getRootNode().collideWith(ray, results);
  226. <span class="keyword">for</span> (<span class="type">int</span> i = <span class="integer">0</span>; i &lt; results.size(); i++) {
  227. <span class="comment">// For each hit, we know distance, impact point, name of geometry.</span>
  228. <span class="type">float</span> dist = results.getCollision(i).getDistance();
  229. Vector3f pt = results.getCollision(i).getContactPoint();
  230. <span class="predefined-type">String</span> hit = results.getCollision(i).getGeometry().getName();
  231. <span class="predefined-type">System</span>.out.println(<span class="string"><span class="delimiter">&quot;</span><span class="content">* Collision #</span><span class="delimiter">&quot;</span></span> + i);
  232. <span class="predefined-type">System</span>.out.println(
  233. <span class="string"><span class="delimiter">&quot;</span><span class="content"> You shot </span><span class="delimiter">&quot;</span></span> + hit
  234. + <span class="string"><span class="delimiter">&quot;</span><span class="content"> at </span><span class="delimiter">&quot;</span></span> + pt
  235. + <span class="string"><span class="delimiter">&quot;</span><span class="content">, </span><span class="delimiter">&quot;</span></span> + dist + <span class="string"><span class="delimiter">&quot;</span><span class="content"> wu away.</span><span class="delimiter">&quot;</span></span>);
  236. }
  237. <span class="keyword">if</span> (results.size() &gt; <span class="integer">0</span>) {
  238. <span class="comment">// The closest collision point is what was truly hit:</span>
  239. CollisionResult closest = results.getClosestCollision();
  240. <span class="comment">// Let's interact - we mark the hit with a red dot.</span>
  241. mark.setLocalTranslation(closest.getContactPoint());
  242. app.getRootNode().attachChild(mark);
  243. picked.setTarget(closest.getContactPoint());
  244. <span class="predefined-type">System</span>.out.println(<span class="string"><span class="delimiter">&quot;</span><span class="content"> Closest Contact </span><span class="delimiter">&quot;</span></span> + closest.
  245. getContactPoint());
  246. } <span class="keyword">else</span> {
  247. <span class="comment">// No hits? Then remove the red mark.</span>
  248. app.getRootNode().detachChild(mark);
  249. }
  250. }
  251. }
  252. }</code></pre></div></div>
  253. <div class="paragraph"><p>The main line of interest here is,</p></div>
  254. <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java">picked.setTarget(closest.getContactPoint());</code></pre></div></div>
  255. <div class="paragraph"><p>where <code>picked</code> is the reference object used to communicate our <code>Vector3f</code> changes to the <code>NavigationControl</code>.</p></div>
  256. <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java"><span class="comment">//NavigationControl implements Pickable Interface</span>
  257. picked = navControl;</code></pre></div></div>
  258. <div class="paragraph"><p>At this point you have loaded your <code>NavMesh</code>, added the <code>NavigationControl</code> to your spatial, and instituted a method for communicating with the <code>NavMeshPathFinder</code>. Next we will delve into the details of the <code>NavigationControl</code>.</p></div></div>
  259. <div class="sect2"><h3 id="navigationcontrol">NavigationControl</h3><div class="paragraph"><p>The <a href="https://github.com/jMonkeyEngine/doc-examples/blob/master/src/com/jme3/examples/jme3ai/ai/NavigationControl.java">NavigationControl</a> is a <a href="../../jme3/advanced/custom_controls.html">custom control</a> that extends the <a href="https://github.com/MeFisto94/jme3-artificial-intelligence/blob/master/AI/src/com/jme3/ai/navmesh/NavMeshPathfinder.java">NavMeshPathFinder</a> class of the Jme3AI library and implements the <code>Pickable</code> interface.</p></div>
  260. <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java"><span class="directive">public</span> <span class="type">class</span> <span class="class">NavigationControl</span> <span class="directive">extends</span> NavMeshPathfinder <span class="directive">implements</span> <span class="predefined-type">Control</span>,
  261. JmeCloneable, Pickable {
  262. }</code></pre></div></div>
  263. <div class="paragraph"><p>The <a href="https://github.com/jMonkeyEngine/doc-examples/blob/master/src/com/jme3/examples/jme3ai/interfaces/Pickable.java">Pickable</a> interface is straightforward and its sole purpose in this implementation is to communicate changes made to the pick target.</p></div>
  264. <div class="listingblock"><div class="title">Pickable Interface implementation</div>
  265. <div class="content"><pre class="CodeRay highlight"><code data-lang="java"><span class="comment">/**
  266. * @param target the target to set
  267. */</span>
  268. <span class="annotation">@Override</span>
  269. <span class="directive">public</span> <span class="type">void</span> setTarget(Vector3f target) {
  270. <span class="local-variable">this</span>.target = target;
  271. }</code></pre></div></div>
  272. <div class="paragraph"><p>The heartbeat of the control lies in the pathfinding thread which makes calls to the <code>computePath()</code> method. Potentially long running tasks like this should always be ran from a thread. Below, is the constructor you would normally use to instantiate your control.</p></div>
  273. <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java"><span class="directive">public</span> NavigationControl(NavMesh navMesh) {
  274. <span class="local-variable">super</span>(navMesh); <span class="comment">//sets the NavMesh for this control</span>
  275. executor = <span class="predefined-type">Executors</span>.newScheduledThreadPool(<span class="integer">1</span>);
  276. startPathFinder();
  277. }</code></pre></div></div>
  278. <div class="paragraph"><p>First, you call <code>super(navMesh)</code> to set the <code>NavMesh</code> for the control, then setup your <code>ExecutorService</code> and start the pathfinding thread.</p></div>
  279. <div class="paragraph"><p>This is a custom thread implementation so it&#8217;s up to you to handle shutting it down. This is done in the controls <code>setSpatial()</code> method.</p></div>
  280. <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">if</span> (spatial == <span class="predefined-constant">null</span>) {
  281. shutdownAndAwaitTermination(executor);
  282. ...
  283. } <span class="keyword">else</span> {
  284. ...
  285. }</code></pre></div></div>
  286. <div class="listingblock"><div class="title">Executor shutdown process</div>
  287. <div class="content"><pre class="CodeRay highlight"><code data-lang="java"><span class="comment">//standard shutdown process for executor</span>
  288. <span class="directive">private</span> <span class="type">void</span> shutdownAndAwaitTermination(<span class="predefined-type">ExecutorService</span> pool) {
  289. pool.shutdown(); <span class="comment">// Disable new tasks from being submitted</span>
  290. <span class="keyword">try</span> {
  291. <span class="comment">// Wait a while for existing tasks to terminate</span>
  292. <span class="keyword">if</span> (!pool.awaitTermination(<span class="integer">6</span>, <span class="predefined-type">TimeUnit</span>.SECONDS)) {
  293. pool.shutdownNow(); <span class="comment">// Cancel currently executing tasks</span>
  294. <span class="comment">// Wait a while for tasks to respond to being cancelled</span>
  295. <span class="keyword">if</span> (!pool.awaitTermination(<span class="integer">6</span>, <span class="predefined-type">TimeUnit</span>.SECONDS)) {
  296. LOG.log(<span class="predefined-type">Level</span>.SEVERE, <span class="string"><span class="delimiter">&quot;</span><span class="content">Pool did not terminate {0}</span><span class="delimiter">&quot;</span></span>, pool);
  297. }
  298. }
  299. } <span class="keyword">catch</span> (<span class="exception">InterruptedException</span> ie) {
  300. <span class="comment">// (Re-)Cancel if current thread also interrupted</span>
  301. pool.shutdownNow();
  302. <span class="comment">// Preserve interrupt status</span>
  303. <span class="predefined-type">Thread</span>.currentThread().interrupt();
  304. }
  305. }</code></pre></div></div>
  306. <div class="paragraph"><p>The easiest way to move a physics character is by using the <a href="../../jme3/advanced/walking_character.html#bettercharactercontrol">BetterCharacterControl</a> class. In this implementation, this is done in the <a href="https://github.com/jMonkeyEngine/doc-examples/blob/master/src/com/jme3/examples/jme3ai/controls/PCControl.java">PCControl</a> class by extending <code>BetterCharacterControl</code>. Since <code>BetterCharacterControl</code> is required to be present on the spatial for pathfinding, in the <code>setSpatial()</code> method, we throw an exception to let us know if it&#8217;s missing.</p></div>
  307. <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">if</span> (spatial == <span class="predefined-constant">null</span>) {
  308. ...
  309. } <span class="keyword">else</span> {
  310. pcControl = spatial.getControl(PCControl.class);
  311. <span class="keyword">if</span> (pcControl == <span class="predefined-constant">null</span>) {
  312. <span class="keyword">throw</span> <span class="keyword">new</span> <span class="exception">IllegalStateException</span>(
  313. <span class="string"><span class="delimiter">&quot;</span><span class="content">Cannot add NavigationControl to spatial without PCControl!</span><span class="delimiter">&quot;</span></span>);
  314. }
  315. }</code></pre></div></div></div>
  316. <div class="sect2"><h3 id="pathfinding-thread">Pathfinding Thread</h3><div class="listingblock"><div class="title">NavigationControl pathfinding thread</div>
  317. <div class="content"><pre class="CodeRay highlight"><code data-lang="java"><span class="comment">//Computes a path using the A* algorithm. Every 1/2 second checks target</span>
  318. <span class="comment">//for processing. Path will remain untill a new path is generated.</span>
  319. <span class="directive">private</span> <span class="type">void</span> startPathFinder() {
  320. executor.scheduleWithFixedDelay(() -&gt; {
  321. <span class="keyword">if</span> (target != <span class="predefined-constant">null</span>) {
  322. clearPath();
  323. setWayPosition(<span class="predefined-constant">null</span>);
  324. pathfinding = <span class="predefined-constant">true</span>;
  325. <span class="comment">//setPosition must be set before computePath is called.</span>
  326. setPosition(spatial.getWorldTranslation());
  327. <span class="comment">//warpInside(target) moves endpoint within the navMesh always.</span>
  328. warpInside(target);
  329. <span class="predefined-type">System</span>.out.println(<span class="string"><span class="delimiter">&quot;</span><span class="content">Target </span><span class="delimiter">&quot;</span></span> + target);
  330. <span class="type">boolean</span> success;
  331. <span class="comment">//comput the path</span>
  332. success = computePath(target);
  333. <span class="predefined-type">System</span>.out.println(<span class="string"><span class="delimiter">&quot;</span><span class="content">SUCCESS = </span><span class="delimiter">&quot;</span></span> + success);
  334. <span class="keyword">if</span> (success) {
  335. <span class="comment">//clear target if successful</span>
  336. target = <span class="predefined-constant">null</span>;
  337. ...
  338. }
  339. pathfinding = <span class="predefined-constant">false</span>;
  340. }
  341. }, <span class="integer">0</span>, <span class="integer">500</span>, <span class="predefined-type">TimeUnit</span>.MILLISECONDS);
  342. }</code></pre></div></div>
  343. <div class="paragraph"><p>How you setup your pathfinding thread makes a significant difference.</p></div>
  344. <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java">executor.scheduleWithFixedDelay(() -&gt; {
  345. ...
  346. }, <span class="integer">0</span>, <span class="integer">500</span>, <span class="predefined-type">TimeUnit</span>.MILLISECONDS);</code></pre></div></div>
  347. <div class="paragraph"><p>This <code>ExecutorService</code> is set to start immediately (0) with a fixed delay of (500) milliseconds. This means the task has a fixed delay of 1/2 second between the end of an execution and the start of the next execution, i.e. it doesn&#8217;t take into account the actual duration of the task. If you were to use <code>scheduleAtFixedRate()</code>, you risk that the task doesn&#8217;t complete in the time allocated.</p></div>
  348. <div class="paragraph"><p>When you use the <code>BetterCharacterControl</code>, all that&#8217;s required to move the spatial is that you <code>setWalkDirection()</code> and the spatial will continuously move in that direction. The following code breakdown explains how the <code>NavigationControl</code> takes advantage of this.</p></div>
  349. <div class="paragraph"><p>It starts by having the pathfinding thread check a <code>target</code> variable for changes.</p></div>
  350. <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">if</span> (target != <span class="predefined-constant">null</span>) {
  351. ...
  352. }</code></pre></div></div>
  353. <div class="paragraph"><p>If it finds a target, it will compute a new path to that <code>target</code>, and if successful, update the <code>NavMeshPathfinder</code> path variable. The <code>update()</code> loop of the control continuously checks this path variable, and if its non-null, takes an appropriate action.</p></div>
  354. <div class="paragraph"><p>Before you compute the path you first clear the existing path, and set wayPosition to null.</p></div>
  355. <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">if</span> (target != <span class="predefined-constant">null</span>) {
  356. clearPath();
  357. setWayPosition(<span class="predefined-constant">null</span>);
  358. pathfinding = <span class="predefined-constant">true</span>;
  359. ...
  360. }</code></pre></div></div>
  361. <div class="paragraph"><p>Doing this allows the player to select a new <code>target</code> at any time and immediately start moving along the new path. Otherwise, the character must finish the path they are on, then backtrack to the position the character was at when the <code>target</code> change was made, before then continuing on the new path.</p></div>
  362. <div class="paragraph"><p>Next, you must call <code>setPosition()</code> <strong>before</strong> calling the <code>computePath()</code> method.</p></div>
  363. <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">if</span> (target != <span class="predefined-constant">null</span>) {
  364. ...
  365. setPosition(spatial.getWorldTranslation());
  366. ...
  367. <span class="comment">//compute the path</span>
  368. success = computePath(target);
  369. ...
  370. }</code></pre></div></div>
  371. <div class="paragraph"><p>There are some things you need to know about how a path is computed.</p></div>
  372. <div class="ulist"><ul><li><p>The first waypoint on any path is the one you set with <code>setPosition()</code>.</p></li><li><p>The last waypoint on any path is always the <code>target</code> Vector3f.</p></li><li><p>computePath() adds one waypoint to the cell <strong>nearest</strong> to the target only if you are not in the goalCell (the cell target is in), and if there is a cell between first and last waypoint, and if there is no direct line of sight.</p></li><li><p>If inside the goalCell when a new target is selected, computePath() will do a direct line of sight placement of target. This means there will only be two waypoints set, <code>setPosition()</code> and <code>target</code>.</p></li><li><p>If the <code>target</code> is outside the <code>NavMesh</code>, your endpoint will be as well.</p></li></ul></div>
  373. <div class="paragraph"><p>To guarantee that <code>target</code> is always inside the <code>NavMesh</code>, call</p></div>
  374. <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">if</span> (target != <span class="predefined-constant">null</span>) {
  375. ...
  376. <span class="comment">//warpInside(target) moves endpoint within the navMesh always.</span>
  377. warpInside(target);
  378. ...
  379. <span class="comment">//compute the path</span>
  380. success = computePath(target);
  381. ...
  382. }</code></pre></div></div>
  383. <div class="paragraph"><p>before calling <code>computePath()</code> and the endpoint of the path will be moved to the closest cell to the <code>target</code> that&#8217;s inside the <code>NavMesh</code>.</p></div></div></div>
  384. <div class="sect1"><h2 id="character-movement">Character Movement</h2><div class="sectionbody"><div class="listingblock"><div class="title">NavigationControl update() loop</div>
  385. <div class="content"><pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@Override</span>
  386. <span class="directive">public</span> <span class="type">void</span> update(<span class="type">float</span> tpf) {
  387. <span class="keyword">if</span> (getWayPosition() != <span class="predefined-constant">null</span>) {
  388. Vector3f spatialPosition = spatial.getWorldTranslation();
  389. Vector2f aiPosition = <span class="keyword">new</span> Vector2f(spatialPosition.x,
  390. spatialPosition.z);
  391. Vector2f waypoint2D = <span class="keyword">new</span> Vector2f(getWayPosition().x,
  392. getWayPosition().z);
  393. <span class="type">float</span> distance = aiPosition.distance(waypoint2D);
  394. <span class="comment">//move char between waypoints untill waypoint reached then set null</span>
  395. <span class="keyword">if</span> (distance &gt; <span class="float">.25f</span>) {
  396. Vector2f direction = waypoint2D.subtract(aiPosition);
  397. direction.mult(tpf);
  398. pcControl.setViewDirection(<span class="keyword">new</span> Vector3f(direction.x, <span class="integer">0</span>,
  399. direction.y).normalize());
  400. pcControl.onAction(ListenerKey.MOVE_FORWARD, <span class="predefined-constant">true</span>, <span class="integer">1</span>);
  401. } <span class="keyword">else</span> {
  402. setWayPosition(<span class="predefined-constant">null</span>);
  403. }
  404. } <span class="keyword">else</span> <span class="keyword">if</span> (!isPathfinding() &amp;&amp; getNextWaypoint() != <span class="predefined-constant">null</span>
  405. &amp;&amp; !isAtGoalWaypoint()) {
  406. <span class="keyword">if</span> (showPath) {
  407. showPath();
  408. showPath = <span class="predefined-constant">false</span>;
  409. }
  410. <span class="comment">//advance to next waypoint</span>
  411. goToNextWaypoint();
  412. setWayPosition(<span class="keyword">new</span> Vector3f(getWaypointPosition()));
  413. <span class="comment">//set spatial physical position</span>
  414. <span class="keyword">if</span> (getPositionType() == EnumPosition.POS_STANDING.position()) {
  415. setPositionType(EnumPosition.POS_RUNNING.position());
  416. stopFeetPlaying();
  417. stopTorsoPlaying();
  418. }
  419. } <span class="keyword">else</span> {
  420. <span class="comment">//waypoint null so stop moving and set spatials physical position</span>
  421. <span class="keyword">if</span> (getPositionType() == EnumPosition.POS_RUNNING.position()) {
  422. setPositionType(EnumPosition.POS_STANDING.position());
  423. stopFeetPlaying();
  424. stopTorsoPlaying();
  425. }
  426. pcControl.onAction(ListenerKey.MOVE_FORWARD, <span class="predefined-constant">false</span>, <span class="integer">1</span>);
  427. }
  428. }</code></pre></div></div>
  429. <div class="paragraph"><p>If the <code>computePath()</code> successfully computes a new path, the path variable of the <code>NavMeshPathfinder</code> will no longer be null. The update loop of the <code>NavigationControl</code> checks this path variable, every iteration that wayPosition is null, by calling the <code>getNextWaypoint()</code> method. If the path has another waypoint, it will advance to the next position in the path and set the <code>wayPosition</code> variable of the <code>NavigationControl</code> to that position.</p></div>
  430. <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java">} <span class="keyword">else</span> <span class="keyword">if</span> (!isPathfinding() &amp;&amp; getNextWaypoint() != <span class="predefined-constant">null</span>
  431. &amp;&amp; !isAtGoalWaypoint()) {
  432. ...
  433. <span class="comment">//advance to next waypoint</span>
  434. goToNextWaypoint();
  435. setWayPosition(<span class="keyword">new</span> Vector3f(getWaypointPosition()));
  436. ...
  437. }</code></pre></div></div>
  438. <div class="admonitionblock important"><table><tr><td class="icon"><i class="fa icon-important" title="Important"></i></td><td class="content">Remember, the first waypoint in the path is always the spatials current position. This is why you always advance the position first.</td></tr></table></div>
  439. <div class="paragraph"><p>On the next iteration of the controls <code>update()</code> method, it sees that <code>wayPosition</code> is no longer null and calculates the distance from the spatials current position to the <code>wayPosition</code>.</p></div>
  440. <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">if</span> (getWayPosition() != <span class="predefined-constant">null</span>) {
  441. Vector3f spatialPosition = spatial.getWorldTranslation();
  442. Vector2f aiPosition = <span class="keyword">new</span> Vector2f(spatialPosition.x,
  443. spatialPosition.z);
  444. Vector2f waypoint2D = <span class="keyword">new</span> Vector2f(getWayPosition().x,
  445. getWayPosition().z);
  446. <span class="type">float</span> distance = aiPosition.distance(waypoint2D);
  447. ...
  448. }</code></pre></div></div>
  449. <div class="paragraph"><p>If it&#8217;s greater than the distance specified, it will <code>setViewDirection()</code> of the <code>PCControl</code> (which extends BetterCharacterControl) and then notify the <code>PCControl</code> that the spatial can move by calling the controls <code>onAction()</code> method directly.</p></div>
  450. <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">if</span> (getWayPosition() != <span class="predefined-constant">null</span>) {
  451. ...
  452. <span class="comment">//move char between waypoints untill waypoint reached then set null</span>
  453. if (distance &gt; <span class="float">.25f</span>) {
  454. Vector2f direction = waypoint2D.subtract(aiPosition);
  455. direction.mult(tpf);
  456. pcControl.setViewDirection(<span class="keyword">new</span> Vector3f(direction.x, <span class="integer">0</span>,
  457. direction.y).normalize());
  458. pcControl.onAction(ListenerKey.MOVE_FORWARD, <span class="predefined-constant">true</span>, <span class="integer">1</span>);
  459. } <span class="keyword">else</span> {
  460. ...
  461. }
  462. }</code></pre></div></div>
  463. <div class="paragraph"><p>It&#8217;s up to the <code>NavigationControl</code> to determine when the character should stop moving. Each time the spatial reaches a point that is less than the specified distance, it sets the wayPosition to null.</p></div>
  464. <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">if</span> (distance &gt; <span class="float">.25f</span>) {
  465. ...
  466. } <span class="keyword">else</span> {
  467. setWayPosition(<span class="predefined-constant">null</span>);
  468. }</code></pre></div></div>
  469. <div class="paragraph"><p>If the path position has not yet reached the end, it will once again be advance to the next waypoint in the path and update the wayPosition.</p></div>
  470. <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java">} <span class="keyword">else</span> <span class="keyword">if</span> (!isPathfinding() &amp;&amp; getNextWaypoint() != <span class="predefined-constant">null</span>
  471. &amp;&amp; !isAtGoalWaypoint()) {
  472. ...
  473. <span class="comment">//advance to next waypoint</span>
  474. goToNextWaypoint();
  475. setWayPosition(<span class="keyword">new</span> Vector3f(getWaypointPosition()));
  476. ...
  477. }</code></pre></div></div>
  478. <div class="paragraph"><p>When the last waypoint is reached, the <code>NavigationControl</code> notifies the <code>PCControl</code> that the spatial can no longer move.</p></div>
  479. <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java">} <span class="keyword">else</span> {
  480. ...
  481. pcControl.onAction(ListenerKey.MOVE_FORWARD, <span class="predefined-constant">false</span>, <span class="integer">1</span>);
  482. }</code></pre></div></div>
  483. <div class="paragraph"><p>The <a href="https://github.com/jMonkeyEngine/doc-examples/blob/master/src/com/jme3/examples/jme3ai/controls/PCControl.java">PCControl</a> class handles the actual movement of the spatial in its <code>update()</code> loop. It does this by checking the <code>forward</code> variable every iteration. This variable is set when you call the <code>onAction()</code> method from the <code>NavigationControl</code> update loop.</p></div>
  484. <div class="listingblock"><div class="title">PCControl ActionListener</div>
  485. <div class="content"><pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@Override</span>
  486. <span class="directive">public</span> <span class="type">void</span> onAction(<span class="predefined-type">String</span> name, <span class="type">boolean</span> isPressed, <span class="type">float</span> tpf) {
  487. <span class="keyword">if</span> (name.equals(ListenerKey.MOVE_FORWARD)) {
  488. forward = isPressed;
  489. }
  490. }</code></pre></div></div>
  491. <div class="listingblock"><div class="title">PCControl update() loop</div>
  492. <div class="content"><pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@Override</span>
  493. <span class="directive">public</span> <span class="type">void</span> update(<span class="type">float</span> tpf) {
  494. <span class="local-variable">super</span>.update(tpf);
  495. <span class="local-variable">this</span>.moveSpeed = <span class="integer">0</span>;
  496. walkDirection.set(<span class="integer">0</span>, <span class="integer">0</span>, <span class="integer">0</span>);
  497. <span class="keyword">if</span> (forward) {
  498. Vector3f modelForwardDir = spatial.getWorldRotation().mult(Vector3f.UNIT_Z);
  499. position = getPositionType();
  500. <span class="keyword">for</span> (EnumPosition pos : EnumPosition.values()) {
  501. <span class="keyword">if</span> (pos.position() == position) {
  502. <span class="keyword">switch</span> (pos) {
  503. <span class="keyword">case</span> POS_RUNNING:
  504. moveSpeed = EnumPosition.POS_RUNNING.speed();
  505. <span class="keyword">break</span>;
  506. <span class="keyword">default</span>:
  507. moveSpeed = <span class="float">0f</span>;
  508. <span class="keyword">break</span>;
  509. }
  510. }
  511. }
  512. walkDirection.addLocal(modelForwardDir.mult(moveSpeed));
  513. }
  514. setWalkDirection(walkDirection);
  515. }</code></pre></div></div>
  516. <div class="paragraph"><p>The <code>PCControl</code> will then set the walk direction, based off spatials world rotation, and set the speed.</p></div></div></div>
  517. <div class="sect1"><h2 id="conclusion">Conclusion</h2><div class="sectionbody"><div class="paragraph"><p>The intent of this tutorial was to give you a general breakdown of how the Jme3AI navigation system works as well as demonstrate how flexible its implementation is. All the code in this tutorial is free for your use and can be found in the <a href="https://github.com/jMonkeyEngine/doc-examples">jme3 documentation repository</a>. The implementations design is such that you can easily change each of the parameters and then visually see how they affect the NavMesh. If you have questions or suggestions on improving this tutorial you can do so in the <a href="https://hub.jmonkeyengine.org/">jMonkeyEngine forum</a>.</p></div></div></div>
  518. <div class="sect1"><h2 id="other-ai-options">Other AI Options</h2><div class="sectionbody"><div class="paragraph"><p>There are other jME3 specific options available you can read about in the wiki under the topic <a href="https://wiki.jmonkeyengine.org/jme3.html#artificial-intelligence-ai">Artificial Intelligence (AI)</a>.</p></div></div></div>
  519. <div class="sect1"><h2 id="further-reading">Further Reading</h2><div class="sectionbody"><div class="ulist"><ul><li><p><a href="http://www.policyalmanac.org/games/aStarTutorial.htm">A* path-finding for Beginners</a> by Patrick Lester</p></li><li><p><a href="http://natureofcode.com/book/">The Nature of Code</a> by Daniel Shiffman</p></li><li><p><a href="http://www.red3d.com/cwr/steer/gdc99/">Steering Behaviors For Autonomous Characters</a> by Craig W. Reynolds</p></li><li><p><a href="http://www.critterai.org/projects/nmgen_study/">Study: Navigation Mesh Generation Java</a> by Stephen Pratt</p></li></ul></div></div></div></div><div id="footnotes"><hr><div class="footnote" id="_footnote_1"><a href="#_footnoteref_1">1</a>. Path-finding means computing the shortest route between two points. Usually mazes.</div><div class="footnote" id="_footnote_2"><a href="#_footnoteref_2">2</a>. Path-following is taking a path that already exists and then following that path.</div></div><div id="footer"><div id="footer-text">Version <br>Last updated 2019-12-20 23:30:51 +00:00</div></div><script type="text/javascript" src="https://cdn.jsdelivr.net/docsearch.js/2/docsearch.min.js"></script><script>docsearch({
  520. apiKey: 'a736b6d93de805e26ec2f49b55013fbd',
  521. indexName: 'jmonkeyengine',
  522. inputSelector: '#doc-search',
  523. debug: false // Set debug to true if you want to inspect the dropdown
  524. });</script></body></html>