terrain_collision.adoc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. = terrain_collision
  2. :revnumber: 2.1
  3. :revdate: 2020/07/24
  4. :keywords: terrain, collision
  5. == Terrain Collision
  6. This tutorial expands the HelloTerrain tutorial and makes the terrain solid. You combine what you learned in xref:tutorials:beginner/hello_terrain.adoc[Hello Terrain] and xref:tutorials:beginner/hello_collision.adoc[Hello Collision] 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.
  7. == Sample Code
  8. [source,java]
  9. ----
  10. package jme3test.helloworld;
  11. import com.jme3.app.SimpleApplication;
  12. import com.jme3.bullet.BulletAppState;
  13. import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
  14. import com.jme3.bullet.collision.shapes.CollisionShape;
  15. import com.jme3.bullet.control.CharacterControl;
  16. import com.jme3.bullet.control.RigidBodyControl;
  17. import com.jme3.bullet.util.CollisionShapeFactory;
  18. import com.jme3.input.KeyInput;
  19. import com.jme3.input.controls.ActionListener;
  20. import com.jme3.input.controls.KeyTrigger;
  21. import com.jme3.material.Material;
  22. import com.jme3.math.Vector3f;
  23. import com.jme3.renderer.Camera;
  24. import com.jme3.scene.Node;
  25. import com.jme3.terrain.geomipmap.TerrainLodControl;
  26. import com.jme3.terrain.heightmap.AbstractHeightMap;
  27. import com.jme3.terrain.geomipmap.TerrainQuad;
  28. import com.jme3.terrain.heightmap.ImageBasedHeightMap;
  29. import com.jme3.texture.Texture;
  30. import com.jme3.texture.Texture.WrapMode;
  31. import java.util.ArrayList;
  32. import java.util.List;
  33. import jme3tools.converters.ImageToAwt;
  34. /**
  35. * This demo shows a terrain with collision detection,
  36. * that you can walk around in with a first-person perspective.
  37. * This code combines HelloCollision and HelloTerrain.
  38. */
  39. public class HelloTerrainCollision extends SimpleApplication
  40. implements ActionListener {
  41. private BulletAppState bulletAppState;
  42. private RigidBodyControl landscape;
  43. private CharacterControl player;
  44. private Vector3f walkDirection = new Vector3f();
  45. private boolean left = false, right = false, up = false, down = false;
  46. private TerrainQuad terrain;
  47. private Material mat_terrain;
  48. public static void main(String[] args) {
  49. HelloTerrainCollision app = new HelloTerrainCollision();
  50. app.start();
  51. }
  52. @Override
  53. public void simpleInitApp() {
  54. /** Set up Physics */
  55. bulletAppState = new BulletAppState();
  56. stateManager.attach(bulletAppState);
  57. //Uncomment for debugging.
  58.   //bulletAppState.setDebugEnabled(true);
  59. flyCam.setMoveSpeed(100);
  60. setUpKeys();
  61. /** 1. Create terrain material and load four textures into it. */
  62. mat_terrain = new Material(assetManager,
  63. "Common/MatDefs/Terrain/Terrain.j3md");
  64. /** 1.1) Add ALPHA map (for red-blue-green coded splat textures) */
  65. mat_terrain.setTexture("Alpha", assetManager.loadTexture(
  66. "Textures/Terrain/splat/alphamap.png"));
  67. /** 1.2) Add GRASS texture into the red layer (Tex1). */
  68. Texture grass = assetManager.loadTexture(
  69. "Textures/Terrain/splat/grass.jpg");
  70. grass.setWrap(WrapMode.Repeat);
  71. mat_terrain.setTexture("Tex1", grass);
  72. mat_terrain.setFloat("Tex1Scale", 64f);
  73. /** 1.3) Add DIRT texture into the green layer (Tex2) */
  74. Texture dirt = assetManager.loadTexture(
  75. "Textures/Terrain/splat/dirt.jpg");
  76. dirt.setWrap(WrapMode.Repeat);
  77. mat_terrain.setTexture("Tex2", dirt);
  78. mat_terrain.setFloat("Tex2Scale", 32f);
  79. /** 1.4) Add ROAD texture into the blue layer (Tex3) */
  80. Texture rock = assetManager.loadTexture(
  81. "Textures/Terrain/splat/road.jpg");
  82. rock.setWrap(WrapMode.Repeat);
  83. mat_terrain.setTexture("Tex3", rock);
  84. mat_terrain.setFloat("Tex3Scale", 128f);
  85. /** 2. Create the height map */
  86. AbstractHeightMap heightmap = null;
  87. Texture heightMapImage = assetManager.loadTexture(
  88. "Textures/Terrain/splat/mountains512.png");
  89. heightmap = new ImageBasedHeightMap(heightMapImage.getImage());
  90. heightmap.load();
  91. /** 3. We have prepared material and heightmap.
  92. * Now we create the actual terrain:
  93. * 3.1) Create a TerrainQuad and name it "my terrain".
  94. * 3.2) A good value for terrain tiles is 64x64 -- so we supply 64+1=65.
  95. * 3.3) We prepared a heightmap of size 512x512 -- so we supply 512+1=513.
  96. * 3.4) As LOD step scale we supply Vector3f(1,1,1).
  97. * 3.5) We supply the prepared heightmap itself.
  98. */
  99. terrain = new TerrainQuad("my terrain", 65, 513, heightmap.getHeightMap());
  100. /** 4. We give the terrain its material, position & scale it, and attach it. */
  101. terrain.setMaterial(mat_terrain);
  102. terrain.setLocalTranslation(0, -100, 0);
  103. terrain.setLocalScale(2f, 1f, 2f);
  104. rootNode.attachChild(terrain);
  105. /** 5. The LOD (level of detail) depends on were the camera is: */
  106. List<Camera> cameras = new ArrayList<Camera>();
  107. cameras.add(getCamera());
  108. TerrainLodControl control = new TerrainLodControl(terrain, cameras);
  109. terrain.addControl(control);
  110. /** 6. Add physics:
  111. * We set up collision detection for the scene by creating a static
  112. * RigidBodyControl with mass zero.
  113. */
  114. terrain.addControl(new RigidBodyControl(0));
  115. /**
  116. * We set up collision detection for the player by creating
  117. * a capsule collision shape and a CharacterControl.
  118. * The CharacterControl offers extra settings for
  119. * size, stepheight, jumping, falling, and gravity.
  120. * We also put the player in its starting position.
  121. */
  122. CapsuleCollisionShape capsuleShape = new CapsuleCollisionShape(1.5f, 6f, 1);
  123. player = new CharacterControl(capsuleShape, 0.05f);
  124. player.setJumpSpeed(20);
  125. player.setFallSpeed(30);
  126. player.setPhysicsLocation(new Vector3f(-10, 10, 10));
  127. // We attach the scene and the player to the rootnode and the physics space,
  128. // to make them appear in the game world.
  129. bulletAppState.getPhysicsSpace().add(terrain);
  130. bulletAppState.getPhysicsSpace().add(player);
  131. // You can change the gravity of individual physics objects after they are
  132. // added to the PhysicsSpace.
  133. player.setGravity(new Vector3f(0,-30f,0));
  134. }
  135. /** We over-write some navigational key mappings here, so we can
  136. * add physics-controlled walking and jumping: */
  137. private void setUpKeys() {
  138. inputManager.addMapping("Left", new KeyTrigger(KeyInput.KEY_A));
  139. inputManager.addMapping("Right", new KeyTrigger(KeyInput.KEY_D));
  140. inputManager.addMapping("Up", new KeyTrigger(KeyInput.KEY_W));
  141. inputManager.addMapping("Down", new KeyTrigger(KeyInput.KEY_S));
  142. inputManager.addMapping("Jump", new KeyTrigger(KeyInput.KEY_SPACE));
  143. inputManager.addListener(this, "Left");
  144. inputManager.addListener(this, "Right");
  145. inputManager.addListener(this, "Up");
  146. inputManager.addListener(this, "Down");
  147. inputManager.addListener(this, "Jump");
  148. }
  149. /** These are our custom actions triggered by key presses.
  150. * We do not walk yet, we just keep track of the direction the user pressed. */
  151. public void onAction(String binding, boolean value, float tpf) {
  152. if (binding.equals("Left")) {
  153. if (value) { left = true; } else { left = false; }
  154. } else if (binding.equals("Right")) {
  155. if (value) { right = true; } else { right = false; }
  156. } else if (binding.equals("Up")) {
  157. if (value) { up = true; } else { up = false; }
  158. } else if (binding.equals("Down")) {
  159. if (value) { down = true; } else { down = false; }
  160. } else if (binding.equals("Jump")) {
  161. player.jump(new Vector3f(0,20f,0));
  162. }
  163. }
  164. /**
  165. * This is the main event loop--walking happens here.
  166. * We check in which direction the player is walking by interpreting
  167. * the camera direction forward (camDir) and to the side (camLeft).
  168. * The setWalkDirection() command is what lets a physics-controlled player walk.
  169. * We also make sure here that the camera moves with player.
  170. */
  171. @Override
  172. public void simpleUpdate(float tpf) {
  173. Vector3f camDir = cam.getDirection().clone().multLocal(0.6f);
  174. Vector3f camLeft = cam.getLeft().clone().multLocal(0.4f);
  175. walkDirection.set(0, 0, 0);
  176. if (left) { walkDirection.addLocal(camLeft); }
  177. if (right) { walkDirection.addLocal(camLeft.negate()); }
  178. if (up) { walkDirection.addLocal(camDir); }
  179. if (down) { walkDirection.addLocal(camDir.negate()); }
  180. player.setWalkDirection(walkDirection);
  181. cam.setLocation(player.getPhysicsLocation());
  182. }
  183. }
  184. ----
  185. To try this code, create a `menu:New Project[JME3 > BasicGame]` using the default settings. Paste the sample code over the pregenerated Main.java class. Change the package to '`mygame`' if necessary. Open the `menu:File[Project Properties > Libraries]` and add the `jme3-test-data` library to make certain you have all the files.
  186. 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.
  187. == Understanding the Code
  188. === The Terrain Code
  189. Read xref:tutorials:beginner/hello_terrain.adoc[Hello Terrain] for details of the following parts that we reuse:
  190. . The `AbstractHeightMap` is an efficient way to describe the shape of the terrain.
  191. . The `Terrain.j3md`-based Material and its texture layers let you colorize rocky mountain, grassy valleys, and a paved path criss-crossing over the landscape.
  192. . The TerrainQuad is the finished `terrain` Spatial that you attach to the rootNode.
  193. === The Collision Detection Code
  194. Read xref:tutorials:beginner/hello_collision.adoc[Hello Collision] for details of the following parts that we reuse:
  195. . The `BulletAppState` lines activate physics.
  196. . The `ActionListener` (`onAction()`) lets you reconfigure the input handling for the first-person player, so it takes collision detection into account.
  197. . The custom `setUpKeys()` method loads your reconfigured input handlers. They now don't just walk blindly, but calculate the `walkDirection` vector that we need for collision detection.
  198. . `simpleUpdate()` uses the `walkDirection` vector and makes the character walk, while taking obstacles and solid walls/floor into account.
  199. [source,java]
  200. ----
  201. player.setWalkDirection(walkDirection);
  202. ----
  203. . The RigidBodyControl `landscape` is the CollisionShape of the terrain.
  204. . The physical first-person player is a CapsuleCollisionShape with a CharacterControl.
  205. === Combining the Two
  206. Here are the changed parts to combine the two:
  207. . You create a static (zero-mass) RigidBodyControl.
  208. . Add the control to the `terrain` to make it physical.
  209. [source,java]
  210. ----
  211. /** 6. Add physics: */
  212. terrain.addControl(new RigidBodyControl(0));
  213. ----
  214. You attach the `terrain` and the first-person `player` to the rootNode, and to the physics space, to make them appear in the game world.
  215. [source,java]
  216. ----
  217. bulletAppState.getPhysicsSpace().add(terrain);
  218. bulletAppState.getPhysicsSpace().add(player);
  219. ----
  220. == Conclusion
  221. 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 something new.
  222. 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.
  223. '''
  224. See also:
  225. * xref:tutorials:beginner/hello_terrain.adoc[Hello Terrain],
  226. * xref:terrain/terrain.adoc[Terrain]