| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224 |
- <!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="terrain, collision"><title>terrain_collision</title><link rel="stylesheet" href="./asciidoctor.css">
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.5.0/css/font-awesome.min.css">
- <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/terrain_collision.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>terrain_collision</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="#terrain-collision">Terrain Collision</a></li><li><a href="#sample-code">Sample Code</a></li><li><a href="#understanding-the-code">Understanding the Code</a><ul class="sectlevel2"><li><a href="#the-terrain-code">The Terrain Code</a></li><li><a href="#the-collision-detection-code">The Collision Detection Code</a></li><li><a href="#combining-the-two">Combining the Two</a></li></ul></li><li><a href="#conclusion">Conclusion</a></li></ul></div></div><div id="content"><div class="sect1"><h2 id="terrain-collision">Terrain Collision</h2><div class="sectionbody"><div class="paragraph"><p>This tutorial expands the HelloTerrain tutorial and makes the terrain solid. You combine what you learned in <a href="../../jme3/beginner/hello_terrain.html">Hello Terrain</a> and <a href="../../jme3/beginner/hello_collision.html">Hello Collision</a> and add a CollisionShape to the terrain. The terrain’s CollisionShape lets the first-person player (who is also a CollisionShape) collide with the terrain, i.e. walk on it and stand on it.</p></div></div></div>
- <div class="sect1"><h2 id="sample-code">Sample Code</h2><div class="sectionbody"><div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">package</span> <span class="namespace">jme3test.helloworld</span>;
- <span class="keyword">import</span> <span class="include">com.jme3.app.SimpleApplication</span>;
- <span class="keyword">import</span> <span class="include">com.jme3.bullet.BulletAppState</span>;
- <span class="keyword">import</span> <span class="include">com.jme3.bullet.collision.shapes.CapsuleCollisionShape</span>;
- <span class="keyword">import</span> <span class="include">com.jme3.bullet.collision.shapes.CollisionShape</span>;
- <span class="keyword">import</span> <span class="include">com.jme3.bullet.control.CharacterControl</span>;
- <span class="keyword">import</span> <span class="include">com.jme3.bullet.control.RigidBodyControl</span>;
- <span class="keyword">import</span> <span class="include">com.jme3.bullet.util.CollisionShapeFactory</span>;
- <span class="keyword">import</span> <span class="include">com.jme3.input.KeyInput</span>;
- <span class="keyword">import</span> <span class="include">com.jme3.input.controls.ActionListener</span>;
- <span class="keyword">import</span> <span class="include">com.jme3.input.controls.KeyTrigger</span>;
- <span class="keyword">import</span> <span class="include">com.jme3.material.Material</span>;
- <span class="keyword">import</span> <span class="include">com.jme3.math.Vector3f</span>;
- <span class="keyword">import</span> <span class="include">com.jme3.renderer.Camera</span>;
- <span class="keyword">import</span> <span class="include">com.jme3.scene.Node</span>;
- <span class="keyword">import</span> <span class="include">com.jme3.terrain.geomipmap.TerrainLodControl</span>;
- <span class="keyword">import</span> <span class="include">com.jme3.terrain.heightmap.AbstractHeightMap</span>;
- <span class="keyword">import</span> <span class="include">com.jme3.terrain.geomipmap.TerrainQuad</span>;
- <span class="keyword">import</span> <span class="include">com.jme3.terrain.heightmap.ImageBasedHeightMap</span>;
- <span class="keyword">import</span> <span class="include">com.jme3.texture.Texture</span>;
- <span class="keyword">import</span> <span class="include">com.jme3.texture.Texture.WrapMode</span>;
- <span class="keyword">import</span> <span class="include">java.util.ArrayList</span>;
- <span class="keyword">import</span> <span class="include">java.util.List</span>;
- <span class="keyword">import</span> <span class="include">jme3tools.converters.ImageToAwt</span>;
- <span class="comment">/**
- * This demo shows a terrain with collision detection,
- * that you can walk around in with a first-person perspective.
- * This code combines HelloCollision and HelloTerrain.
- */</span>
- <span class="directive">public</span> <span class="type">class</span> <span class="class">HelloTerrainCollision</span> <span class="directive">extends</span> SimpleApplication
- <span class="directive">implements</span> <span class="predefined-type">ActionListener</span> {
- <span class="directive">private</span> BulletAppState bulletAppState;
- <span class="directive">private</span> RigidBodyControl landscape;
- <span class="directive">private</span> CharacterControl player;
- <span class="directive">private</span> Vector3f walkDirection = <span class="keyword">new</span> Vector3f();
- <span class="directive">private</span> <span class="type">boolean</span> left = <span class="predefined-constant">false</span>, right = <span class="predefined-constant">false</span>, up = <span class="predefined-constant">false</span>, down = <span class="predefined-constant">false</span>;
- <span class="directive">private</span> TerrainQuad terrain;
- <span class="directive">private</span> Material mat_terrain;
- <span class="directive">public</span> <span class="directive">static</span> <span class="type">void</span> main(<span class="predefined-type">String</span><span class="type">[]</span> args) {
- HelloTerrainCollision app = <span class="keyword">new</span> HelloTerrainCollision();
- app.start();
- }
- <span class="annotation">@Override</span>
- <span class="directive">public</span> <span class="type">void</span> simpleInitApp() {
- <span class="comment">/** Set up Physics */</span>
- bulletAppState = <span class="keyword">new</span> BulletAppState();
- stateManager.attach(bulletAppState);
- <span class="comment">//Uncomment for debugging.</span>
- <span class="error"> </span> <span class="comment">//bulletAppState.setDebugEnabled(true);</span>
- flyCam.setMoveSpeed(<span class="integer">100</span>);
- setUpKeys();
- <span class="comment">/** 1. Create terrain material and load four textures into it. */</span>
- mat_terrain = <span class="keyword">new</span> Material(assetManager,
- <span class="string"><span class="delimiter">"</span><span class="content">Common/MatDefs/Terrain/Terrain.j3md</span><span class="delimiter">"</span></span>);
- <span class="comment">/** 1.1) Add ALPHA map (for red-blue-green coded splat textures) */</span>
- mat_terrain.setTexture(<span class="string"><span class="delimiter">"</span><span class="content">Alpha</span><span class="delimiter">"</span></span>, assetManager.loadTexture(
- <span class="string"><span class="delimiter">"</span><span class="content">Textures/Terrain/splat/alphamap.png</span><span class="delimiter">"</span></span>));
- <span class="comment">/** 1.2) Add GRASS texture into the red layer (Tex1). */</span>
- Texture grass = assetManager.loadTexture(
- <span class="string"><span class="delimiter">"</span><span class="content">Textures/Terrain/splat/grass.jpg</span><span class="delimiter">"</span></span>);
- grass.setWrap(WrapMode.Repeat);
- mat_terrain.setTexture(<span class="string"><span class="delimiter">"</span><span class="content">Tex1</span><span class="delimiter">"</span></span>, grass);
- mat_terrain.setFloat(<span class="string"><span class="delimiter">"</span><span class="content">Tex1Scale</span><span class="delimiter">"</span></span>, <span class="float">64f</span>);
- <span class="comment">/** 1.3) Add DIRT texture into the green layer (Tex2) */</span>
- Texture dirt = assetManager.loadTexture(
- <span class="string"><span class="delimiter">"</span><span class="content">Textures/Terrain/splat/dirt.jpg</span><span class="delimiter">"</span></span>);
- dirt.setWrap(WrapMode.Repeat);
- mat_terrain.setTexture(<span class="string"><span class="delimiter">"</span><span class="content">Tex2</span><span class="delimiter">"</span></span>, dirt);
- mat_terrain.setFloat(<span class="string"><span class="delimiter">"</span><span class="content">Tex2Scale</span><span class="delimiter">"</span></span>, <span class="float">32f</span>);
- <span class="comment">/** 1.4) Add ROAD texture into the blue layer (Tex3) */</span>
- Texture rock = assetManager.loadTexture(
- <span class="string"><span class="delimiter">"</span><span class="content">Textures/Terrain/splat/road.jpg</span><span class="delimiter">"</span></span>);
- rock.setWrap(WrapMode.Repeat);
- mat_terrain.setTexture(<span class="string"><span class="delimiter">"</span><span class="content">Tex3</span><span class="delimiter">"</span></span>, rock);
- mat_terrain.setFloat(<span class="string"><span class="delimiter">"</span><span class="content">Tex3Scale</span><span class="delimiter">"</span></span>, <span class="float">128f</span>);
- <span class="comment">/** 2. Create the height map */</span>
- AbstractHeightMap heightmap = <span class="predefined-constant">null</span>;
- Texture heightMapImage = assetManager.loadTexture(
- <span class="string"><span class="delimiter">"</span><span class="content">Textures/Terrain/splat/mountains512.png</span><span class="delimiter">"</span></span>);
- heightmap = <span class="keyword">new</span> ImageBasedHeightMap(heightMapImage.getImage());
- heightmap.load();
- <span class="comment">/** 3. We have prepared material and heightmap.
- * Now we create the actual terrain:
- * 3.1) Create a TerrainQuad and name it "my terrain".
- * 3.2) A good value for terrain tiles is 64x64 -- so we supply 64+1=65.
- * 3.3) We prepared a heightmap of size 512x512 -- so we supply 512+1=513.
- * 3.4) As LOD step scale we supply Vector3f(1,1,1).
- * 3.5) We supply the prepared heightmap itself.
- */</span>
- terrain = <span class="keyword">new</span> TerrainQuad(<span class="string"><span class="delimiter">"</span><span class="content">my terrain</span><span class="delimiter">"</span></span>, <span class="integer">65</span>, <span class="integer">513</span>, heightmap.getHeightMap());
- <span class="comment">/** 4. We give the terrain its material, position & scale it, and attach it. */</span>
- terrain.setMaterial(mat_terrain);
- terrain.setLocalTranslation(<span class="integer">0</span>, -<span class="integer">100</span>, <span class="integer">0</span>);
- terrain.setLocalScale(<span class="float">2f</span>, <span class="float">1f</span>, <span class="float">2f</span>);
- rootNode.attachChild(terrain);
- <span class="comment">/** 5. The LOD (level of detail) depends on were the camera is: */</span>
- <span class="predefined-type">List</span><Camera> cameras = <span class="keyword">new</span> <span class="predefined-type">ArrayList</span><Camera>();
- cameras.add(getCamera());
- TerrainLodControl control = <span class="keyword">new</span> TerrainLodControl(terrain, cameras);
- terrain.addControl(control);
- <span class="comment">/** 6. Add physics: */</span>
- <span class="comment">// We set up collision detection for the scene by creating a static</span>
- RigidBodyControl with mass zero.*/
- terrain.addControl(<span class="keyword">new</span> RigidBodyControl(<span class="integer">0</span>));
- <span class="comment">/**
- * We set up collision detection for the player by creating
- * a capsule collision shape and a CharacterControl.
- * The CharacterControl offers extra settings for
- * size, stepheight, jumping, falling, and gravity.
- * We also put the player in its starting position.
- */</span>
- CapsuleCollisionShape capsuleShape = <span class="keyword">new</span> CapsuleCollisionShape(<span class="float">1.5f</span>, <span class="float">6f</span>, <span class="integer">1</span>);
- player = <span class="keyword">new</span> CharacterControl(capsuleShape, <span class="float">0.05f</span>);
- player.setJumpSpeed(<span class="integer">20</span>);
- player.setFallSpeed(<span class="integer">30</span>);
- player.setPhysicsLocation(<span class="keyword">new</span> Vector3f(-<span class="integer">10</span>, <span class="integer">10</span>, <span class="integer">10</span>));
- <span class="comment">// We attach the scene and the player to the rootnode and the physics space,</span>
- <span class="comment">// to make them appear in the game world.</span>
- bulletAppState.getPhysicsSpace().add(terrain);
- bulletAppState.getPhysicsSpace().add(player);
- <span class="comment">// You can change the gravity of individual physics objects after they are</span>
- <span class="comment">// added to the PhysicsSpace.</span>
- player.setGravity(<span class="keyword">new</span> Vector3f(<span class="integer">0</span>,-<span class="float">30f</span>,<span class="integer">0</span>));
- }
- <span class="comment">/** We over-write some navigational key mappings here, so we can
- * add physics-controlled walking and jumping: */</span>
- <span class="directive">private</span> <span class="type">void</span> setUpKeys() {
- inputManager.addMapping(<span class="string"><span class="delimiter">"</span><span class="content">Left</span><span class="delimiter">"</span></span>, <span class="keyword">new</span> KeyTrigger(KeyInput.KEY_A));
- inputManager.addMapping(<span class="string"><span class="delimiter">"</span><span class="content">Right</span><span class="delimiter">"</span></span>, <span class="keyword">new</span> KeyTrigger(KeyInput.KEY_D));
- inputManager.addMapping(<span class="string"><span class="delimiter">"</span><span class="content">Up</span><span class="delimiter">"</span></span>, <span class="keyword">new</span> KeyTrigger(KeyInput.KEY_W));
- inputManager.addMapping(<span class="string"><span class="delimiter">"</span><span class="content">Down</span><span class="delimiter">"</span></span>, <span class="keyword">new</span> KeyTrigger(KeyInput.KEY_S));
- inputManager.addMapping(<span class="string"><span class="delimiter">"</span><span class="content">Jump</span><span class="delimiter">"</span></span>, <span class="keyword">new</span> KeyTrigger(KeyInput.KEY_SPACE));
- inputManager.addListener(<span class="local-variable">this</span>, <span class="string"><span class="delimiter">"</span><span class="content">Left</span><span class="delimiter">"</span></span>);
- inputManager.addListener(<span class="local-variable">this</span>, <span class="string"><span class="delimiter">"</span><span class="content">Right</span><span class="delimiter">"</span></span>);
- inputManager.addListener(<span class="local-variable">this</span>, <span class="string"><span class="delimiter">"</span><span class="content">Up</span><span class="delimiter">"</span></span>);
- inputManager.addListener(<span class="local-variable">this</span>, <span class="string"><span class="delimiter">"</span><span class="content">Down</span><span class="delimiter">"</span></span>);
- inputManager.addListener(<span class="local-variable">this</span>, <span class="string"><span class="delimiter">"</span><span class="content">Jump</span><span class="delimiter">"</span></span>);
- }
- <span class="comment">/** These are our custom actions triggered by key presses.
- * We do not walk yet, we just keep track of the direction the user pressed. */</span>
- <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) {
- <span class="keyword">if</span> (binding.equals(<span class="string"><span class="delimiter">"</span><span class="content">Left</span><span class="delimiter">"</span></span>)) {
- <span class="keyword">if</span> (value) { left = <span class="predefined-constant">true</span>; } <span class="keyword">else</span> { left = <span class="predefined-constant">false</span>; }
- } <span class="keyword">else</span> <span class="keyword">if</span> (binding.equals(<span class="string"><span class="delimiter">"</span><span class="content">Right</span><span class="delimiter">"</span></span>)) {
- <span class="keyword">if</span> (value) { right = <span class="predefined-constant">true</span>; } <span class="keyword">else</span> { right = <span class="predefined-constant">false</span>; }
- } <span class="keyword">else</span> <span class="keyword">if</span> (binding.equals(<span class="string"><span class="delimiter">"</span><span class="content">Up</span><span class="delimiter">"</span></span>)) {
- <span class="keyword">if</span> (value) { up = <span class="predefined-constant">true</span>; } <span class="keyword">else</span> { up = <span class="predefined-constant">false</span>; }
- } <span class="keyword">else</span> <span class="keyword">if</span> (binding.equals(<span class="string"><span class="delimiter">"</span><span class="content">Down</span><span class="delimiter">"</span></span>)) {
- <span class="keyword">if</span> (value) { down = <span class="predefined-constant">true</span>; } <span class="keyword">else</span> { down = <span class="predefined-constant">false</span>; }
- } <span class="keyword">else</span> <span class="keyword">if</span> (binding.equals(<span class="string"><span class="delimiter">"</span><span class="content">Jump</span><span class="delimiter">"</span></span>)) {
- player.jump(<span class="keyword">new</span> Vector3f(<span class="integer">0</span>,<span class="float">20f</span>,<span class="integer">0</span>));
- }
- }
- <span class="comment">/**
- * This is the main event loop--walking happens here.
- * We check in which direction the player is walking by interpreting
- * the camera direction forward (camDir) and to the side (camLeft).
- * The setWalkDirection() command is what lets a physics-controlled player walk.
- * We also make sure here that the camera moves with player.
- */</span>
- <span class="annotation">@Override</span>
- <span class="directive">public</span> <span class="type">void</span> simpleUpdate(<span class="type">float</span> tpf) {
- Vector3f camDir = cam.getDirection().clone().multLocal(<span class="float">0.6f</span>);
- Vector3f camLeft = cam.getLeft().clone().multLocal(<span class="float">0.4f</span>);
- walkDirection.set(<span class="integer">0</span>, <span class="integer">0</span>, <span class="integer">0</span>);
- <span class="keyword">if</span> (left) { walkDirection.addLocal(camLeft); }
- <span class="keyword">if</span> (right) { walkDirection.addLocal(camLeft.negate()); }
- <span class="keyword">if</span> (up) { walkDirection.addLocal(camDir); }
- <span class="keyword">if</span> (down) { walkDirection.addLocal(camDir.negate()); }
- player.setWalkDirection(walkDirection);
- cam.setLocation(player.getPhysicsLocation());
- }
- }</code></pre></div></div>
- <div class="paragraph"><p>To try this code, create a <code><span class="menuseq"><span class="menu">New Project</span> ▸ <span class="submenu">JME3</span> ▸ <span class="menuitem">BasicGame</span></span></code> using the default settings. Paste the sample code over the pregenerated Main.java class. Change the package to ‘mygame’ if necessary. Open the <code><span class="menuseq"><span class="menu">File</span> ▸ <span class="submenu">Project Properties</span> ▸ <span class="menuitem">Libraries</span></span></code> and add the <code>jme3-test-data</code> library to make certain you have all the files.</p></div>
- <div class="paragraph"><p>Compile and run the code. You should see a terrain. You can use the WASD keys and the mouse to run up and down the hills.</p></div></div></div>
- <div class="sect2"><h3 id="understanding-the-code">Understanding the Code</h3><div class="sect2"><h3 id="the-terrain-code">The Terrain Code</h3><div class="paragraph"><p>Read <a href="../../jme3/beginner/hello_terrain.html">Hello Terrain</a> for details of the following parts that we reuse:</p></div>
- <div class="olist arabic"><ol class="arabic"><li><p>The <code>AbstractHeightMap</code> is an efficient way to describe the shape of the terrain.</p></li><li><p>The <code>Terrain.j3md</code>-based Material and its texture layers let you colorize rocky mountain, grassy valleys, and a paved path criss-crossing over the landscape.</p></li><li><p>The TerrainQuad is the finished <code>terrain</code> Spatial that you attach to the rootNode.</p></li></ol></div></div>
- <div class="sect2"><h3 id="the-collision-detection-code">The Collision Detection Code</h3><div class="paragraph"><p>Read <a href="../../jme3/beginner/hello_collision.html">Hello Collision</a> for details of the following parts that we reuse:</p></div>
- <div class="olist arabic"><ol class="arabic"><li><p>The <code>BulletAppState</code> lines activate physics.</p></li><li><p>The <code>ActionListener</code> (<code>onAction()</code>) lets you reconfigure the input handling for the first-person player, so it takes collision detection into account.</p></li><li><p>The custom <code>setUpKeys()</code> method loads your reconfigured input handlers. They now don’t just walk blindly, but calculate the <code>walkDirection</code> vector that we need for collision detection.</p></li><li><p><code>simpleUpdate()</code> uses the <code>walkDirection</code> vector and makes the character walk, while taking obstacles and solid walls/floor into account.</p></li></ol></div>
- <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java">player.setWalkDirection(walkDirection);</code></pre></div></div>
- <div class="olist arabic"><ol class="arabic"><li><p>The RigidBodyControl <code>landscape</code> is the CollisionShape of the terrain.</p></li><li><p>The physical first-person player is a CapsuleCollisionShape with a CharacterControl.</p></li></ol></div></div>
- <div class="sect2"><h3 id="combining-the-two">Combining the Two</h3><div class="paragraph"><p>Here are the changed parts to combine the two:</p></div>
- <div class="olist arabic"><ol class="arabic"><li><p>You create a static (zero-mass) RigidBodyControl.</p></li><li><p>Add the control to the <code>terrain</code> to make it physical.</p></li></ol></div>
- <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java"><span class="comment">/** 6. Add physics: */</span>
- terrain.addControl(<span class="keyword">new</span> RigidBodyControl(<span class="integer">0</span>));</code></pre></div></div>
- <div class="paragraph"><p>You attach the <code>terrain</code> and the first-person <code>player</code> to the rootNode, and to the physics space, to make them appear in the game world.</p></div>
- <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java"> bulletAppState.getPhysicsSpace().add(terrain);
- bulletAppState.getPhysicsSpace().add(player);</code></pre></div></div></div></div>
- <div class="sect1"><h2 id="conclusion">Conclusion</h2><div class="sectionbody"><div class="paragraph"><p>You see that you can combine snippets of sample code (such as HelloTerrain and HelloCollision), and create a new application from it that combines two features into soemthing new.</p></div>
- <div class="paragraph"><p>You should spawn high up in the area and fall down to the map, giving you a few seconds to survey the area. Then walk around and see how you like the lay of the land.</p></div>
- <hr>
- <div class="paragraph"><p>See also:</p></div>
- <div class="ulist"><ul><li><p><a href="../../jme3/beginner/hello_terrain.html">Hello Terrain</a>,</p></li><li><p><a href="../../jme3/advanced/terrain.html">Terrain</a></p></li></ul></div></div></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({
- apiKey: 'a736b6d93de805e26ec2f49b55013fbd',
- indexName: 'jmonkeyengine',
- inputSelector: '#doc-search',
- debug: false // Set debug to true if you want to inspect the dropdown
- });</script></body></html>
|