hello_physics.adoc 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509
  1. = jMonkeyEngine 3 Tutorial (13) - Hello Physics
  2. :revnumber: 2.0
  3. :revdate: 2020/07/24
  4. :keywords: beginner, intro, physics, documentation, input, model, control
  5. Do you remember the xref:beginner/hello_collision.adoc[Hello Collision] tutorial where you made the model of a town solid and walked through it in a first-person perspective? Then you may remember that, for the simulation of physical forces, jME3 integrates the link:http://jbullet.advel.cz/[jBullet] library.
  6. Apart from making models "`solid`", the most common use cases for physics in 3D games are:
  7. * Driving vehicles with suspensions, tyre friction, ramp jumping, drifting – Example: car racers
  8. * Rolling and bouncing balls – Example: pong, pool billiard, bowling
  9. * Sliding and falling boxes – Example: Breakout, Arkanoid
  10. * Exposing objects to forces and gravity – Example: spaceships or zero-g flight
  11. * Animating ragdolls – Example: "`realistic`" character simulations
  12. * Swinging pendulums, rope bridges, flexible chains, and much more…
  13. All these physical properties can be simulated in JME3. Let's have a look at a simulation of physical forces in this example where you shoot cannon balls at a brick wall.
  14. image::beginner/beginner-physics.png[beginner-physics.png,360,291,align="center"]
  15. include::partial$add-testdata-tip.adoc[]
  16. == Sample Code
  17. [source,java]
  18. ----
  19. package jme3test.helloworld;
  20. import com.jme3.app.SimpleApplication;
  21. import com.jme3.asset.TextureKey;
  22. import com.jme3.bullet.BulletAppState;
  23. import com.jme3.bullet.control.RigidBodyControl;
  24. import com.jme3.font.BitmapText;
  25. import com.jme3.input.MouseInput;
  26. import com.jme3.input.controls.ActionListener;
  27. import com.jme3.input.controls.MouseButtonTrigger;
  28. import com.jme3.material.Material;
  29. import com.jme3.math.Vector2f;
  30. import com.jme3.math.Vector3f;
  31. import com.jme3.scene.Geometry;
  32. import com.jme3.scene.shape.Box;
  33. import com.jme3.scene.shape.Sphere;
  34. import com.jme3.scene.shape.Sphere.TextureMode;
  35. import com.jme3.texture.Texture;
  36. import com.jme3.texture.Texture.WrapMode;
  37. /**
  38. * Example 12 - how to give objects physical properties so they bounce and fall.
  39. * @author base code by double1984, updated by zathras
  40. */
  41. public class HelloPhysics extends SimpleApplication {
  42. public static void main(String args[]) {
  43. HelloPhysics app = new HelloPhysics();
  44. app.start();
  45. }
  46. /** Prepare the Physics Application State (jBullet) */
  47. private BulletAppState bulletAppState;
  48. /** Prepare Materials */
  49. private Material wall_mat;
  50. private Material stone_mat;
  51. private Material floor_mat;
  52. /** Prepare geometries for bricks and cannonballs. */
  53. private static final Box box;
  54. private static final Sphere sphere;
  55. private static final Box floor;
  56. /** dimensions used for bricks and wall */
  57. private static final float brickLength = 0.48f;
  58. private static final float brickWidth = 0.24f;
  59. private static final float brickHeight = 0.12f;
  60. static {
  61. /** Initialize the cannon ball geometry */
  62. sphere = new Sphere(32, 32, 0.4f, true, false);
  63. sphere.setTextureMode(TextureMode.Projected);
  64. /** Initialize the brick geometry */
  65. box = new Box(brickLength, brickHeight, brickWidth);
  66. box.scaleTextureCoordinates(new Vector2f(1f, .5f));
  67. /** Initialize the floor geometry */
  68. floor = new Box(10f, 0.1f, 5f);
  69. floor.scaleTextureCoordinates(new Vector2f(3, 6));
  70. }
  71. @Override
  72. public void simpleInitApp() {
  73. /** Set up Physics Game */
  74. bulletAppState = new BulletAppState();
  75. stateManager.attach(bulletAppState);
  76. /** Configure cam to look at scene */
  77. cam.setLocation(new Vector3f(0, 4f, 6f));
  78. cam.lookAt(new Vector3f(2, 2, 0), Vector3f.UNIT_Y);
  79. /** Initialize the scene, materials, inputs, and physics space */
  80. initInputs();
  81. initMaterials();
  82. initWall();
  83. initFloor();
  84. initCrossHairs();
  85. }
  86. /** Add InputManager action: Left click triggers shooting. */
  87. private void initInputs() {
  88. inputManager.addMapping("shoot",
  89. new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
  90. inputManager.addListener(actionListener, "shoot");
  91. }
  92. /**
  93. * Every time the shoot action is triggered, a new cannon ball is produced.
  94. * The ball is set up to fly from the camera position in the camera direction.
  95. */
  96. final private ActionListener actionListener = new ActionListener() {
  97. @Override
  98. public void onAction(String name, boolean keyPressed, float tpf) {
  99. if (name.equals("shoot") && !keyPressed) {
  100. makeCannonBall();
  101. }
  102. }
  103. };
  104. /** Initialize the materials used in this scene. */
  105. public void initMaterials() {
  106. wall_mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
  107. TextureKey key = new TextureKey("Textures/Terrain/BrickWall/BrickWall.jpg");
  108. key.setGenerateMips(true);
  109. Texture tex = assetManager.loadTexture(key);
  110. wall_mat.setTexture("ColorMap", tex);
  111. stone_mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
  112. TextureKey key2 = new TextureKey("Textures/Terrain/Rock/Rock.PNG");
  113. key2.setGenerateMips(true);
  114. Texture tex2 = assetManager.loadTexture(key2);
  115. stone_mat.setTexture("ColorMap", tex2);
  116. floor_mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
  117. TextureKey key3 = new TextureKey("Textures/Terrain/Pond/Pond.jpg");
  118. key3.setGenerateMips(true);
  119. Texture tex3 = assetManager.loadTexture(key3);
  120. tex3.setWrap(WrapMode.Repeat);
  121. floor_mat.setTexture("ColorMap", tex3);
  122. }
  123. /** Make a solid floor and add it to the scene. */
  124. public void initFloor() {
  125. Geometry floor_geo = new Geometry("Floor", floor);
  126. floor_geo.setMaterial(floor_mat);
  127. floor_geo.setLocalTranslation(0, -0.1f, 0);
  128. this.rootNode.attachChild(floor_geo);
  129. /* Make the floor physical with mass 0.0f! */
  130. RigidBodyControl floor_phy = new RigidBodyControl(0.0f);
  131. floor_geo.addControl(floor_phy);
  132. bulletAppState.getPhysicsSpace().add(floor_phy);
  133. }
  134. /** This loop builds a wall out of individual bricks. */
  135. public void initWall() {
  136. float startX = brickLength / 4;
  137. float height = 0;
  138. for (int j = 0; j < 15; j++) {
  139. for (int i = 0; i < 6; i++) {
  140. Vector3f vt =
  141. new Vector3f(i * brickLength * 2 + startX, brickHeight + height, 0);
  142. makeBrick(vt);
  143. }
  144. startX = -startX;
  145. height += 2 * brickHeight;
  146. }
  147. }
  148. /** Creates one physical brick. */
  149. private void makeBrick(Vector3f loc) {
  150. /** Create a brick geometry and attach to scene graph. */
  151. Geometry brick_geo = new Geometry("brick", box);
  152. brick_geo.setMaterial(wall_mat);
  153. rootNode.attachChild(brick_geo);
  154. /** Position the brick geometry */
  155. brick_geo.setLocalTranslation(loc);
  156. /* Make brick physical with a mass > 0. */
  157. RigidBodyControl brick_phy = new RigidBodyControl(2f);
  158. /** Add physical brick to physics space. */
  159. brick_geo.addControl(brick_phy);
  160. bulletAppState.getPhysicsSpace().add(brick_phy);
  161. }
  162. /** Creates one physical cannonball.
  163. * By default, the ball is accelerated and flies
  164. * from the camera position in the camera direction.*/
  165. public void makeCannonBall() {
  166. /** Create a cannon ball geometry and attach to scene graph. */
  167. Geometry ball_geo = new Geometry("cannon ball", sphere);
  168. ball_geo.setMaterial(stone_mat);
  169. rootNode.attachChild(ball_geo);
  170. /** Position the cannon ball */
  171. ball_geo.setLocalTranslation(cam.getLocation());
  172. /* Make the ball physical with a mass > 0.0f */
  173. RigidBodyControl ball_phy = new RigidBodyControl(1f);
  174. /** Add physical ball to physics space. */
  175. ball_geo.addControl(ball_phy);
  176. bulletAppState.getPhysicsSpace().add(ball_phy);
  177. /* Accelerate the physical ball to shoot it. */
  178. ball_phy.setLinearVelocity(cam.getDirection().mult(25));
  179. }
  180. /** A plus sign used as crosshairs to help the player with aiming.*/
  181. protected void initCrossHairs() {
  182. setDisplayStatView(false);
  183. //guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt");
  184. BitmapText ch = new BitmapText(guiFont);
  185. ch.setSize(guiFont.getCharSet().getRenderedSize() * 2);
  186. ch.setText("+"); // fake crosshairs :)
  187. ch.setLocalTranslation( // center
  188. settings.getWidth() / 2,
  189. settings.getHeight() / 2, 0);
  190. guiNode.attachChild(ch);
  191. }
  192. }
  193. ----
  194. You should see a brick wall. Click to shoot cannon balls. Watch the bricks fall and bounce off one another!
  195. == A Basic Physics Application
  196. In the previous tutorials, you used static Geometries (boxes, spheres, and models) that you placed in the scene. Depending on their translation, Geometries can "`float`" in mid-air and even overlap – they are not affected by "`gravity`" and have no physical mass. This tutorial shows how to add physical properties to Geometries.
  197. As always, start with a standard com.jme3.app.SimpleApplication. To activate physics, create a com.jme3.bullet.BulletAppState, and and attach it to the SimpleApplication's AppState manager.
  198. [source,java]
  199. ----
  200. public class HelloPhysics extends SimpleApplication {
  201. private BulletAppState bulletAppState;
  202. public void simpleInitApp() {
  203. bulletAppState = new BulletAppState();
  204. stateManager.attach(bulletAppState);
  205. ...
  206. }
  207. ...
  208. }
  209. ----
  210. The BulletAppState gives the game access to a PhysicsSpace. The PhysicsSpace lets you use com.jme3.bullet.control.PhysicsControls that add physical properties to Nodes.
  211. == Creating Bricks and Cannon Balls
  212. === Geometries
  213. In this "`shoot`" at the wall example, you use Geometries such as cannon balls and bricks. Geometries contain meshes, such as Shapes. Let's create and initialize some Shapes: Boxes and Spheres.
  214. [source,java]
  215. ----
  216. /** Prepare geometries for bricks and cannonballs. */
  217. private static final Box box;
  218. private static final Sphere sphere;
  219. private static final Box floor;
  220. /** dimensions used for bricks and wall */
  221. private static final float brickLength = 0.48f;
  222. private static final float brickWidth = 0.24f;
  223. private static final float brickHeight = 0.12f;
  224. static {
  225. /** Initialize the cannon ball geometry */
  226. sphere = new Sphere(32, 32, 0.4f, true, false);
  227. sphere.setTextureMode(TextureMode.Projected);
  228. /** Initialize the brick geometry */
  229. box = new Box(brickLength, brickHeight, brickWidth);
  230. box.scaleTextureCoordinates(new Vector2f(1f, .5f));
  231. /** Initialize the floor geometry */
  232. floor = new Box(10f, 0.1f, 5f);
  233. floor.scaleTextureCoordinates(new Vector2f(3, 6));
  234. }
  235. ----
  236. === RigidBodyControl: Brick
  237. We want to create brick Geometries from those boxes. The custom `makeBrick(loc)` methods creates individual bricks at the location `loc`. A brick has the following properties:
  238. * It has a visible Geometry `brick_geo` (Box Shape Geometry).
  239. * It has physical properties `brick_phy` (RigidBodyControl). Since this is a Geometry with physical properties you create a RigidBodyControl.
  240. [source,java]
  241. ----
  242. private void makeBrick(Vector3f loc) {
  243. /** Create a brick geometry and attach to scene graph. */
  244. Geometry brick_geo = new Geometry("brick", box);
  245. brick_geo.setMaterial(wall_mat);
  246. rootNode.attachChild(brick_geo);
  247. /** Position the brick geometry */
  248. brick_geo.setLocalTranslation(loc);
  249. /* Make brick physical with a mass > 0. */
  250. RigidBodyControl brick_phy = new RigidBodyControl(2f);
  251. /** Add physical brick to physics space. */
  252. brick_geo.addControl(brick_phy);
  253. bulletAppState.getPhysicsSpace().add(brick_phy);
  254. }
  255. ----
  256. This code sample does the following:
  257. . You create a brick Geometry brick_geo. A Geometry describes the shape and look of an object.
  258. ** brick_geo has a box shape
  259. ** brick_geo has a brick-colored material.
  260. . You attach brick_geo to the rootNode
  261. . You position brick_geo at `loc`.
  262. . You create a RigidBodyControl brick_phy for brick_geo.
  263. ** brick_phy has a mass of 2f.
  264. ** You add brick_phy to brick_geo.
  265. ** You register brick_phy to the PhysicsSpace.
  266. === RigidBodyControl: Cannonball
  267. You notice that the cannon ball is created in the same way, using the custom `makeCannonBall()` method. The cannon ball has the following properties:
  268. * It has a visible Geometry `ball_geo` (Sphere Shape Geometry)
  269. * It has physical properties `ball_phy` (RigidBodyControl)
  270. [source,java]
  271. ----
  272. /** Create a cannon ball geometry and attach to scene graph. */
  273. Geometry ball_geo = new Geometry("cannon ball", sphere);
  274. ball_geo.setMaterial(stone_mat);
  275. rootNode.attachChild(ball_geo);
  276. /** Position the cannon ball */
  277. ball_geo.setLocalTranslation(cam.getLocation());
  278. /* Make the ball physical with a mass > 0.0f */
  279. RigidBodyControl ball_phy = new RigidBodyControl(1f);
  280. /** Add physical ball to physics space. */
  281. ball_geo.addControl(ball_phy);
  282. bulletAppState.getPhysicsSpace().add(ball_phy);
  283. /* Accelerate the physical ball to shoot it. */
  284. ball_phy.setLinearVelocity(cam.getDirection().mult(25));
  285. ----
  286. This code sample does the following:
  287. . You create a ball Geometry ball_geo. A Geometry describes the shape and look of an object.
  288. ** ball_geo has a sphere shape
  289. ** ball_geo has a stone-colored material.
  290. . You attach ball_geo to the rootNode
  291. . You position ball_geo at the camera location.
  292. . You create a RigidBodyControl ball_phy for ball_geo.
  293. ** ball_phy has a mass of 1f.
  294. ** You add ball_phy to ball_geo.
  295. ** You register ball_phy to the PhysicsSpace.
  296. Since you are shooting cannon balls, the last line accelerates the ball in the direction the camera is looking, with a speed of 25f.
  297. === RigidBodyControl: Floor
  298. The (static) floor has one important difference compared to the (dynamic) bricks and cannonballs: *Static objects have a mass of zero.*
  299. As before, you write a custom `initFloor()` method that creates a flat box with a rock texture that you use as floor. The floor has the following properties:
  300. * It has a visible Geometry `floor_geo` (Box Shape Geometry)
  301. * It has physical properties `floor_phy` (RigidBodyControl)
  302. [source,java]
  303. ----
  304. public void initFloor() {
  305. Geometry floor_geo = new Geometry("Floor", floor);
  306. floor_geo.setMaterial(floor_mat);
  307. floor_geo.setLocalTranslation(0, -0.1f, 0);
  308. this.rootNode.attachChild(floor_geo);
  309. /* Make the floor physical with mass 0.0f! */
  310. RigidBodyControl floor_phy = new RigidBodyControl(0.0f);
  311. floor_geo.addControl(floor_phy);
  312. bulletAppState.getPhysicsSpace().add(floor_phy);
  313. }
  314. ----
  315. This code sample does the following:
  316. . You create a floor Geometry floor_geo. A Geometry describes the shape and look of an object.
  317. ** floor_geo has a box shape
  318. ** floor_geo has a pebble-colored material.
  319. . You attach floor_geo to the rootNode
  320. . You position floor_geo a bit below y=0 (to prevent overlap with other PhysicControl'ed Spatials).
  321. . You create a RigidBodyControl floor_phy for floor_geo.
  322. ** floor_phy has a mass of 0f
  323. ** You add floor_phy to floor_geo.
  324. ** You register floor_phy to the PhysicsSpace.
  325. == Creating the Scene
  326. Let's have a quick look at the custom helper methods:
  327. * `initMaterial()` – This method initializes all the materials we use in this demo.
  328. * `initWall()` – A double loop that generates a wall by positioning brick objects: 15 rows high with 6 bricks per row. It's important to space the physical bricks so they do not overlap.
  329. * `initCrossHairs()` – This method simply displays a plus sign that you use as crosshairs for aiming. Note that screen elements such as crosshairs are attached to the `guiNode`, not the `rootNode`!
  330. * `initInputs()` – This method sets up the click-to-shoot action.
  331. These methods are each called once from the `simpleInitApp()` method at the start of the game. As you see, you can write any number of custom methods to set up your game's scene.
  332. == The Cannon Ball Shooting Action
  333. In the `initInputs()` method, you add an input mapping that triggers a shoot action when the left mouse button is pressed.
  334. [source,java]
  335. ----
  336. private void initInputs() {
  337. inputManager.addMapping("shoot",
  338. new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
  339. inputManager.addListener(actionListener, "shoot");
  340. }
  341. ----
  342. You define the actual action of shooting a new cannon ball as follows:
  343. [source,java]
  344. ----
  345. final private ActionListener actionListener = new ActionListener() {
  346. @Override
  347. public void onAction(String name, boolean keyPressed, float tpf) {
  348. if (name.equals("shoot") && !keyPressed) {
  349. makeCannonBall();
  350. }
  351. }
  352. };
  353. ----
  354. In the moment the cannonball appears in the scene, it flies off with the velocity (and in the direction) that you specified using `setLinearVelocity()` inside `makeCannonBall()`. The newly created cannon ball flies off, hits the wall, and exerts a _physical force_ that impacts individual bricks.
  355. == Moving a Physical Spatial
  356. The location of the dynamic Spatial is controlled by its RigidBodyControl. Move the RigidBodyControl to move the Spatial. If it's a dynamic PhysicsControl, you can use setLinearVelocity() and apply forces and torques to it. Other RigidBodyControl'led objects can push the dynamic Spatial around (like pool/billiard balls).
  357. You can make Spatials that are not dynamic: Switch the RigidBodyControl to setKinematic(true) to have it move along with its Spatial.
  358. * A kinematic is unaffected by forces or gravity, which means it can float in mid-air and cannot be pushed away by dynamic "`cannon`" balls etc.
  359. * A kinematic RigidBody has a mass.
  360. * A kinematic can be moved and can exert forces on dynamic RigidBodys. This means you can use a kinematic node as a billiard cue or a remote-controlled battering ram.
  361. Learn more about static versus kinematic versus dynamic in the xref:ROOT:jme3/advanced/physics[advanced physics doc].
  362. == Exercises
  363. === Exercise 1: Debug Shapes
  364. Add the following line after the bulletAppState initialization.
  365. [source,java]
  366. ----
  367. // For older versions up to JME sdk 3.0.10
  368. bulletAppState.getPhysicsSpace().enableDebug(assetManager);
  369. ----
  370. or
  371. [source,java]
  372. ----
  373. // For new versions thereafter
  374. bulletAppState.setDebugEnabled(true);
  375. ----
  376. Now you see the collisionShapes of the bricks and spheres, and the floor highlighted.
  377. === Exercise 2: No Mo' Static
  378. What happens if you give a static node, such as the floor, a mass of more than 0.0f?
  379. === Exercise 3: Behind the Curtain
  380. Fill your scene with walls, bricks, and cannon balls. When do you begin to see a performance impact?
  381. Popular AAA games use a clever mix of physics, animation and prerendered graphics to give you the illusion of a real, "`physical`" world. Think of your favorite video games and try to spot where and how the game designers trick you into believing that the whole scene is physical. For example, think of a building "`breaking`" into 4-8 parts after an explosion. The pieces most likely fly on predefined (so called kinematic) paths and are only replaced by dynamic Spatials after they touch the ground… Now that you start to implement game physics yourself, look behind the curtain!
  382. Using physics everywhere in a game sounds like a cool idea, but it is easily overused. Although the physics nodes are put to "`sleep`" when they are not moving, creating a world solely out of dynamic physics nodes will quickly bring you to the limits of your computer's capabilities.
  383. == Conclusion
  384. You have learned how to activate the jBullet PhysicsSpace in an application by adding a `BulletAppState`. You have created PhysicsControls for simple Shape-based Geometries (for more complex shapes, read up on xref:physics:physics.adoc[CollisionShapes]). You have learned that physical objects are not only attached to the rootNode, but also registered to the PhysicsSpace. You know that it makes a difference whether a physical object has a mass (dynamic) or not (static). You are aware that overusing physics has a huge performance impact.
  385. [TIP]
  386. ====
  387. Congratulations! – You have completed the last beginner tutorial. Now you are ready to start combining what you have learned, to create a cool 3D game of your own. Show us what you can do, and feel free to share your demos, game videos, and screenshots on the link:http://hub.jmonkeyengine.org/c/user-code-projects[User Code &amp; Projects Forum]!
  388. ====