vehicles.html 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  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"><meta name="keywords" content="documentation, physics, vehicle, collision"><title>Controlling a Physical Vehicle</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/vehicles.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>Controlling a Physical Vehicle</h1><div class="details"><span class="author" id="author"></span><br><span id="revnumber">version ,</span> <span id="revdate">2016/03/17 20:48</span></div><div id="toc" class="toc2"><div id="toctitle">Table of Contents</div><ul class="sectlevel1"><li><a href="#sample-code">Sample Code</a></li><li><a href="#overview-of-this-physics-application">Overview of this Physics Application</a></li><li><a href="#creating-the-vehicle-chassis">Creating the Vehicle Chassis</a></li><li><a href="#adding-the-wheels">Adding the Wheels</a></li><li><a href="#steering-the-vehicle">Steering the Vehicle</a></li><li><a href="#detecting-collisions">Detecting Collisions</a></li><li><a href="#best-practices">Best Practices</a></li></ul></div></div><div id="content"><div id="preamble"><div class="sectionbody"><div class="paragraph"><p>For physical vehicles, jME&#8217;s uses the jBullet ray-cast vehicle. In this vehicle implementation, the physical chassis 'floats' along on four non-physical vertical rays.</p></div>
  4. <div class="paragraph"><p>Internally, each wheel casts a ray down, and using the ray&#8217;s intersection point, jBullet calculates the suspension length, and the suspension force. The suspension force is applied to the chassis, keeping it from hitting the ground. The friction force is calculated for each wheel where the ray intersects with the ground. Friction is applied as a sideways and forwards force. <span class="footnote">[<a class="footnote" id="_footnoteref_1" href="#_footnote_1" title="View footnote.">1</a>]</span></p></div>
  5. <div class="paragraph"><p>This article shows how you use this vehicle implementation in a jME3 application.</p></div>
  6. <div style="text-align: center;" class="imageblock"><div class="content"><img src="../../jme3/advanced/physics-vehicle.png" alt="physics-vehicle.png" width="" height=""></div></div></div></div>
  7. <div class="sect1"><h2 id="sample-code">Sample Code</h2><div class="sectionbody"><div class="paragraph"><p>Full code samples are here:</p></div>
  8. <div class="ulist"><ul><li><p><a href="https://github.com/jMonkeyEngine/jmonkeyengine/blob/master/jme3-examples/src/main/java/jme3test/bullet/TestPhysicsCar.java">TestPhysicsCar.java</a></p></li><li><p><a href="https://github.com/jMonkeyEngine/jmonkeyengine/blob/master/jme3-examples/src/main/java/jme3test/bullet/TestFancyCar.java">TestFancyCar.java</a></p></li></ul></div></div></div>
  9. <div class="sect1"><h2 id="overview-of-this-physics-application">Overview of this Physics Application</h2><div class="sectionbody"><div class="paragraph"><p>The goal is to create a physical vehicle with wheels that can be steered and that interacts (collides with) with the floor and obstacles.</p></div>
  10. <div class="olist arabic"><ol class="arabic"><li><p>Create a SimpleApplication with a <a href="../../jme3/advanced/physics.html">BulletAppState</a></p><div class="ulist"><ul><li><p>This gives us a PhysicsSpace for PhysicsNodes</p></li></ul></div></li><li><p>Create a VehicleControl + CompoundCollisionShape for the physical vehicle behaviour</p><div class="olist loweralpha"><ol class="loweralpha" type="a"><li><p>Set physical properties of the vehicle, such as suspension.</p></li></ol></div></li><li><p>Create a VehicleNode for the car model</p><div class="olist loweralpha"><ol class="loweralpha" type="a"><li><p>Create a box plus 4 cylinders as wheels (using <code>vehicle.addWheel()</code>).</p></li><li><p>Add the VehicleControl behaviour to the VehicleNode geometry.</p></li></ol></div></li><li><p>Create a RigidBodyControl and CollisionShape for the floor</p></li><li><p>Map key triggers and add input listeners</p><div class="ulist"><ul><li><p>Navigational commands Left, Right, Foward, Brake.</p></li></ul></div></li><li><p>Define the steering actions to be triggered by the key events.</p><div class="ulist"><ul><li><p><code>vehicle.steer()</code></p></li><li><p><code>vehicle.accelerate()</code></p></li><li><p><code>vehicle.brake()</code></p></li></ul></div></li></ol></div></div></div>
  11. <div class="sect1"><h2 id="creating-the-vehicle-chassis">Creating the Vehicle Chassis</h2><div class="sectionbody"><div class="paragraph"><p>The vehicle that we create here in the <a href="https://github.com/jMonkeyEngine/jmonkeyengine/blob/master/jme3-examples/src/main/java/jme3test/bullet/TestPhysicsCar.java">TestPhysicsCar.java</a> example is just a “box on wheels, a basic vehicle shape that you can replace with a fancy car model, as demonstrated in <a href="https://github.com/jMonkeyEngine/jmonkeyengine/blob/master/jme3-examples/src/main/java/jme3test/bullet/TestFancyCar.java">TestFancyCar.java</a>.</p></div>
  12. <div class="paragraph"><p>Every physical object must have a collision shape, that we prepare first. For the vehicle, we choose a compound collision shape that is made up of a box-shaped body of the right size for the vehicle. We will add the wheels later.</p></div>
  13. <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java">CompoundCollisionShape compoundShape = <span class="keyword">new</span> CompoundCollisionShape();
  14. BoxCollisionShape box = <span class="keyword">new</span> BoxCollisionShape(<span class="keyword">new</span> Vector3f(<span class="float">1.2f</span>, <span class="float">0.5f</span>, <span class="float">2.4f</span>));</code></pre></div></div>
  15. <div class="paragraph"><p><strong>Best Practice:</strong> We attach the BoxCollisionShape (the vehicle body) to the CompoundCollisionShape at a Vector of (0,1,0): This shifts the effective center of mass of the BoxCollisionShape downwards to 0,-1,0 and makes a moving vehicle more stable!</p></div>
  16. <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java">compoundShape.addChildShape(box, <span class="keyword">new</span> Vector3f(<span class="integer">0</span>, <span class="integer">1</span>, <span class="integer">0</span>));</code></pre></div></div>
  17. <div class="paragraph"><p>Any kind of geometry can make up the visible part of the vehicle, here we use a wireframe box. We create a node that we use to group the geometry.</p></div>
  18. <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java">Node vehicleNode=<span class="keyword">new</span> Node(<span class="string"><span class="delimiter">&quot;</span><span class="content">vehicleNode</span><span class="delimiter">&quot;</span></span>);
  19. vehicle = <span class="keyword">new</span> VehicleControl(compoundShape, <span class="integer">400</span>);
  20. vehicleNode.addControl(vehicle);</code></pre></div></div>
  21. <div class="paragraph"><p>We initialize the Vehicle Control with the compound shape, and set its mass to a heavy value, 400f. The Vehicle Control represents the car&#8217;s physical behaviour.</p></div>
  22. <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java">vehicle = <span class="keyword">new</span> VehicleControl(compoundShape, <span class="integer">400</span>);</code></pre></div></div>
  23. <div class="paragraph"><p>Finally we add the behaviour (VehicleControl) to the visible Geometry (node).</p></div>
  24. <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java">vehicleNode.addControl(vehicle);</code></pre></div></div>
  25. <div class="paragraph"><p>We configure the physical properties of the vehicle&#8217;s suspension: Compresion, Damping, Stiffness, and MaxSuspenionForce. Picking workable values for the wheel suspension can be tricky – for background info have a look at these <a href="https://docs.google.com/Doc?docid=0AXVUZ5xw6XpKZGNuZG56a3FfMzU0Z2NyZnF4Zmo&amp;hl=en">Suspension Settings Tips</a>. For now, let&#8217;s work with the following values:</p></div>
  26. <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java"><span class="type">float</span> stiffness = <span class="float">60.0f</span>;<span class="comment">//200=f1 car</span>
  27. <span class="type">float</span> compValue = <span class="float">.3f</span>; <span class="comment">//(should be lower than damp)</span>
  28. <span class="type">float</span> dampValue = <span class="float">.4f</span>;
  29. vehicle.setSuspensionCompression(compValue * <span class="float">2.0f</span> * FastMath.sqrt(stiffness));
  30. vehicle.setSuspensionDamping(dampValue * <span class="float">2.0f</span> * FastMath.sqrt(stiffness));
  31. vehicle.setSuspensionStiffness(stiffness);
  32. vehicle.setMaxSuspensionForce(<span class="float">10000.0f</span>);</code></pre></div></div>
  33. <div class="paragraph"><p>We now have a node <code>vehicleNode</code> with a visible “car geometry, which acts like a vehicle. One thing that&#8217;s missing are wheels.</p></div></div></div>
  34. <div class="sect1"><h2 id="adding-the-wheels">Adding the Wheels</h2><div class="sectionbody"><div class="paragraph"><p>We create four wheel Geometries and add them to the vehicle. Our wheel geometries are simple, non-physical discs (flat Cylinders), they are just visual decorations. Note that the physical wheel behaviour (the com.jme3.bullet.objects.VehicleWheel objects) is created internally by the <code>vehicle.addWheel()</code> method.</p></div>
  35. <div class="paragraph"><p>The <code>addWheel()</code> method sets following properties:</p></div>
  36. <div class="ulist"><ul><li><p>Vector3f connectionPoint – Coordinate where the suspension connects to the chassis (internally, this is where the Ray is casted downwards).</p></li><li><p>Vector3f direction – Wheel direction is typically a (0,-1,0) vector.</p></li><li><p>Vector3f axle – Axle direction is typically a (-1,0,0) vector.</p></li><li><p>float suspensionRestLength – Suspension rest length in world units</p></li><li><p>float wheelRadius – Wheel radius in world units</p></li><li><p>boolean isFrontWheel – Whether this wheel is one of the steering wheels.<br>
  37. Front wheels are the ones that rotate visibly when the vehicle turns.</p></li></ul></div>
  38. <div class="paragraph"><p>We initialize a few variables that we will reuse when we add the four wheels. yOff, etc, are the particular wheel offsets for our small vehicle model.</p></div>
  39. <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java">Vector3f wheelDirection = <span class="keyword">new</span> Vector3f(<span class="integer">0</span>, -<span class="integer">1</span>, <span class="integer">0</span>);
  40. Vector3f wheelAxle = <span class="keyword">new</span> Vector3f(-<span class="integer">1</span>, <span class="integer">0</span>, <span class="integer">0</span>);
  41. <span class="type">float</span> radius = <span class="float">0.5f</span>;
  42. <span class="type">float</span> restLength = <span class="float">0.3f</span>;
  43. <span class="type">float</span> yOff = <span class="float">0.5f</span>;
  44. <span class="type">float</span> xOff = <span class="float">1f</span>;
  45. <span class="type">float</span> zOff = <span class="float">2f</span>;</code></pre></div></div>
  46. <div class="paragraph"><p>We create a Cylinder mesh shape that we use to create the four visible wheel geometries.</p></div>
  47. <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java">Cylinder wheelMesh = <span class="keyword">new</span> Cylinder(<span class="integer">16</span>, <span class="integer">16</span>, radius, radius * <span class="float">0.6f</span>, <span class="predefined-constant">true</span>);</code></pre></div></div>
  48. <div class="paragraph"><p>For each wheel, we create a Node and a Geometry. We attach the Cylinder Geometry to the Node. We rotate the wheel by 90° around the Y axis. We set a material to make it visible. Finally we add the wheel (plus its properties) to the vehicle.</p></div>
  49. <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java">Node node1 = <span class="keyword">new</span> Node(<span class="string"><span class="delimiter">&quot;</span><span class="content">wheel 1 node</span><span class="delimiter">&quot;</span></span>);
  50. Geometry wheels1 = <span class="keyword">new</span> Geometry(<span class="string"><span class="delimiter">&quot;</span><span class="content">wheel 1</span><span class="delimiter">&quot;</span></span>, wheelMesh);
  51. node1.attachChild(wheels1);
  52. wheels1.rotate(<span class="integer">0</span>, FastMath.HALF_PI, <span class="integer">0</span>);
  53. wheels1.setMaterial(mat);
  54. vehicle.addWheel(node1, <span class="keyword">new</span> Vector3f(-xOff, yOff, zOff),
  55. wheelDirection, wheelAxle, restLength, radius, <span class="predefined-constant">true</span>);</code></pre></div></div>
  56. <div class="paragraph"><p>The three next wheels are created in the same fashion, only the offsets are different. Remember to set the Boolean parameter correctly to indicate whether it&#8217;s a front wheel.</p></div>
  57. <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java">...
  58. vehicle.addWheel(node2, <span class="keyword">new</span> Vector3f(xOff, yOff, zOff),
  59. wheelDirection, wheelAxle, restLength, radius, <span class="predefined-constant">true</span>);
  60. ...
  61. vehicle.addWheel(node3, <span class="keyword">new</span> Vector3f(-xOff, yOff, -zOff),
  62. wheelDirection, wheelAxle, restLength, radius, <span class="predefined-constant">false</span>);
  63. ...
  64. vehicle.addWheel(node4, <span class="keyword">new</span> Vector3f(xOff, yOff, -zOff),
  65. wheelDirection, wheelAxle, restLength, radius, <span class="predefined-constant">false</span>);</code></pre></div></div>
  66. <div class="paragraph"><p>Attach the wheel Nodes to the vehicle Node to group them, so they move together.</p></div>
  67. <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java">vehicleNode.attachChild(node1);
  68. vehicleNode.attachChild(node2);
  69. vehicleNode.attachChild(node3);
  70. vehicleNode.attachChild(node4);</code></pre></div></div>
  71. <div class="paragraph"><p>As always, attach the vehicle Node to the rootNode to make it visible, and add the Vehicle Control to the PhysicsSpace to make the car physical.</p></div>
  72. <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java">rootNode.attachChild(vehicleNode);
  73. getPhysicsSpace().add(vehicle);</code></pre></div></div>
  74. <div class="paragraph"><p>Not shown here is that we also created a Material <code>mat</code>.</p></div></div></div>
  75. <div class="sect1"><h2 id="steering-the-vehicle">Steering the Vehicle</h2><div class="sectionbody"><div class="paragraph"><p>Not shown here is the standard way how we map the input keys to actions (see full code sample). Also refer to <a href="../../jme3/advanced/input_handling.html">Input Handling</a>).</p></div>
  76. <div class="paragraph"><p>In the ActionListener, we implement the actions that control the vehicle&#8217;s direction and speed. For the four directions (accelerate=up, brake=down, left, right), we specify how we want the vehicle to move.</p></div>
  77. <div class="ulist"><ul><li><p>The braking action is pretty straightforward:<br>
  78. <code>vehicle.brake(brakeForce)</code></p></li><li><p>For left and right turns, we add a constant to <code>steeringValue</code> when the key is pressed, and subtract it when the key is released.<br>
  79. <code>vehicle.steer(steeringValue);</code></p></li><li><p>For acceleration we add a constant to <code>accelerationValue</code> when the key is pressed, and substract it when the key is released.<br>
  80. <code>vehicle.accelerate(accelerationValue);</code></p></li><li><p>Because we can and it&#8217;s fun, we also add a turbo booster that makes the vehicle jump when you press the assigned key (spacebar).<br>
  81. <code>vehicle.applyImpulse(jumpForce, Vector3f.ZERO);</code></p></li></ul></div>
  82. <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java"><span class="directive">public</span> <span class="type">void</span> onAction(<span class="predefined-type">String</span> binding, <span class="type">boolean</span> value, <span class="type">float</span> tpf) {
  83. <span class="keyword">if</span> (binding.equals(<span class="string"><span class="delimiter">&quot;</span><span class="content">Lefts</span><span class="delimiter">&quot;</span></span>)) {
  84. <span class="keyword">if</span> (value) { steeringValue += <span class="float">.5f</span>; } <span class="keyword">else</span> { steeringValue += -<span class="float">.5f</span>; }
  85. vehicle.steer(steeringValue);
  86. } <span class="keyword">else</span> <span class="keyword">if</span> (binding.equals(<span class="string"><span class="delimiter">&quot;</span><span class="content">Rights</span><span class="delimiter">&quot;</span></span>)) {
  87. <span class="keyword">if</span> (value) { steeringValue += -<span class="float">.5f</span>; } <span class="keyword">else</span> { steeringValue += <span class="float">.5f</span>; }
  88. vehicle.steer(steeringValue);
  89. } <span class="keyword">else</span> <span class="keyword">if</span> (binding.equals(<span class="string"><span class="delimiter">&quot;</span><span class="content">Ups</span><span class="delimiter">&quot;</span></span>)) {
  90. <span class="keyword">if</span> (value) {
  91. accelerationValue += accelerationForce;
  92. } <span class="keyword">else</span> {
  93. accelerationValue -= accelerationForce;
  94. }
  95. vehicle.accelerate(accelerationValue);
  96. } <span class="keyword">else</span> <span class="keyword">if</span> (binding.equals(<span class="string"><span class="delimiter">&quot;</span><span class="content">Downs</span><span class="delimiter">&quot;</span></span>)) {
  97. <span class="keyword">if</span> (value) { vehicle.brake(brakeForce); } <span class="keyword">else</span> { vehicle.brake(<span class="float">0f</span>); }
  98. } <span class="keyword">else</span> <span class="keyword">if</span> (binding.equals(<span class="string"><span class="delimiter">&quot;</span><span class="content">Space</span><span class="delimiter">&quot;</span></span>)) {
  99. <span class="keyword">if</span> (value) {
  100. vehicle.applyImpulse(jumpForce, Vector3f.ZERO);
  101. }
  102. } <span class="keyword">else</span> <span class="keyword">if</span> (binding.equals(<span class="string"><span class="delimiter">&quot;</span><span class="content">Reset</span><span class="delimiter">&quot;</span></span>)) {
  103. <span class="keyword">if</span> (value) {
  104. <span class="predefined-type">System</span>.out.println(<span class="string"><span class="delimiter">&quot;</span><span class="content">Reset</span><span class="delimiter">&quot;</span></span>);
  105. vehicle.setPhysicsLocation(Vector3f.ZERO);
  106. vehicle.setPhysicsRotation(<span class="keyword">new</span> Matrix3f());
  107. vehicle.setLinearVelocity(Vector3f.ZERO);
  108. vehicle.setAngularVelocity(Vector3f.ZERO);
  109. vehicle.resetSuspension();
  110. } <span class="keyword">else</span> {
  111. }
  112. }
  113. }</code></pre></div></div>
  114. <div class="paragraph"><p>For your reference, this is how we initialized the constants for this example:</p></div>
  115. <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java"><span class="directive">private</span> <span class="directive">final</span> <span class="type">float</span> accelerationForce = <span class="float">1000.0f</span>;
  116. <span class="directive">private</span> <span class="directive">final</span> <span class="type">float</span> brakeForce = <span class="float">100.0f</span>;
  117. <span class="directive">private</span> <span class="type">float</span> steeringValue = <span class="integer">0</span>;
  118. <span class="directive">private</span> <span class="type">float</span> accelerationValue = <span class="integer">0</span>;
  119. <span class="directive">private</span> Vector3f jumpForce = <span class="keyword">new</span> Vector3f(<span class="integer">0</span>, <span class="integer">3000</span>, <span class="integer">0</span>);</code></pre></div></div>
  120. <div class="paragraph"><p>Remember, the standard input listener code that maps the actions to keys can be found in the code samples.</p></div></div></div>
  121. <div class="sect1"><h2 id="detecting-collisions">Detecting Collisions</h2><div class="sectionbody"><div class="paragraph"><p>Read the <a href="../../jme3/advanced/physics.html#responding_to_a_physicscollisionevent">Responding to a PhysicsCollisionEvent</a> chapter in the general physics documentation on how to detect collisions. You would do this if you want to react to collisions with custom events, such as adding points or substracting health.</p></div></div></div>
  122. <div class="sect1"><h2 id="best-practices">Best Practices</h2><div class="sectionbody"><div class="paragraph"><p>This example shows a very simple but functional vehicle. For a game you would implement steering behaviour and acceleration with values that are typical for the type of vehicle that you want to simulate. Instead of a box, you load a chassis model. You can consider using an <a href="../../jme3/advanced/input_handling.html">AnalogListener</a> to respond to key events in a more sophisticated way.</p></div>
  123. <div class="paragraph"><p>For a more advanced example, look at <a href="https://github.com/jMonkeyEngine/jmonkeyengine/blob/master/jme3-examples/src/main/java/jme3test/bullet/TestFancyCar.java">TestFancyCar.java</a>.</p></div></div></div></div><div id="footnotes"><hr><div class="footnote" id="_footnote_1"><a href="#_footnoteref_1">1</a>. <a href="https://docs.google.com/Doc?docid=0AXVUZ5xw6XpKZGNuZG56a3FfMzU0Z2NyZnF4Zmo&amp;hl=en">https://docs.google.com/Doc?docid=0AXVUZ5xw6XpKZGNuZG56a3FfMzU0Z2NyZnF4Zmo&amp;hl=en</a></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({
  124. apiKey: 'a736b6d93de805e26ec2f49b55013fbd',
  125. indexName: 'jmonkeyengine',
  126. inputSelector: '#doc-search',
  127. debug: false // Set debug to true if you want to inspect the dropdown
  128. });</script></body></html>