physics_listeners.html 18 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
  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, collision, forces, interaction"><title>Physics Listeners</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/physics_listeners.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>Physics Listeners</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="#physicsghostobjects">PhysicsGhostObjects</a></li><li><a href="#physics-tick-listener">Physics Tick Listener</a><ul class="sectlevel2"><li><a href="#when-not-to-use-tick-listener">When (Not) to Use Tick Listener?</a></li><li><a href="#how-to-listen-to-physics-ticks">How to Listen to Physics Ticks</a></li></ul></li><li><a href="#physics-collision-listener">Physics Collision Listener</a><ul class="sectlevel2"><li><a href="#when-not-to-use-collision-listener">When (Not) to Use Collision Listener</a></li><li><a href="#how-to-listen-to-collisions">How to Listen to Collisions</a></li><li><a href="#reading-details-from-a-physicscollisionevent">Reading Details From a PhysicsCollisionEvent</a></li><li><a href="#collision-groups">Collision Groups</a></li></ul></li></ul></div></div><div id="content"><div id="preamble"><div class="sectionbody"><div class="paragraph"><p>You can control physical objects (push them around) by applying physical forces to them. Typically, you also want to respond to the resulting collisions, e.g. by substracting health points or by playing a sound. To specify how the game responds to such physics events, you use Physics Listeners.</p></div></div></div>
  4. <div class="sect1"><h2 id="sample-code">Sample Code</h2><div class="sectionbody"><div class="ulist"><ul><li><p><a href="https://github.com/jMonkeyEngine/jmonkeyengine/blob/master/jme3-examples/src/main/java/jme3test/bullet/TestCollisionListener.java">TestCollisionListener.java</a></p></li><li><p><a href="https://github.com/jMonkeyEngine/jmonkeyengine/blob/master/jme3-examples/src/main/java/jme3test/bullet/TestAttachGhostObject.java">TestAttachGhostObject.java</a></p></li><li><p><a href="https://github.com/jMonkeyEngine/jmonkeyengine/blob/master/jme3-examples/src/main/java/jme3test/bullet/TestGhostObject.java">TestGhostObject.java</a></p></li></ul></div></div></div>
  5. <div class="sect1"><h2 id="physicsghostobjects">PhysicsGhostObjects</h2><div class="sectionbody"><div class="paragraph"><p>Attach a com.jme3.bullet.control.GhostControl to any Spatial to turn it into a PhysicsGhostObject. Ghost objects automatically follow their spatial and detect collisions. The attached ghost itself is invisible and non-solid (!) and doesn&#8217;t interfere with your game otherwise, it only passively reports collisions.</p></div>
  6. <div class="paragraph"><p>You can leave the GhostControl non-solid and invisible and attach it to an (invisible) Node in the scene to create something like a motion detector. But a GhostControl also works fine when added to spatials that are solid (with RigidBodyControl) and visible (with Geometry). One use case for GhostControls is to check for collisions among <a href="../../jme3/advanced/walking_character.html">CharacterControls</a> when the characters are walking.</p></div>
  7. <div class="paragraph"><p>The shape of the ghost depends on the CollisionShape that you gave the GhostControl. This means that the GhostControl&#8217;s shape can be different from the RigidBodyControl&#8217;s shape. For example, the non-solid ghost shape can be bigger than the solid shape of the Spatial (so you can “feel ahead).</p></div>
  8. <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java">GhostControl ghost = <span class="keyword">new</span> GhostControl(
  9. <span class="keyword">new</span> BoxCollisionShape(<span class="keyword">new</span> Vector3f(<span class="integer">1</span>,<span class="integer">1</span>,<span class="integer">1</span>))); <span class="comment">// a box-shaped ghost</span>
  10. Node node = <span class="keyword">new</span> Node(<span class="string"><span class="delimiter">&quot;</span><span class="content">a ghost-controlled thing</span><span class="delimiter">&quot;</span></span>);
  11. node.addControl(ghost); <span class="comment">// the ghost follows this node</span>
  12. <span class="comment">// Optional: Add a Geometry, or other controls, to the node if you need to</span>
  13. ...
  14. <span class="comment">// attach everything to activate it</span>
  15. rootNode.attachChild(node);
  16. getPhysicsSpace().add(ghost);</code></pre></div></div>
  17. <table class="tableblock frame-all grid-all spread"><colgroup><col style="width: 50%;"><col style="width: 50%;"></colgroup><thead><tr><th class="tableblock halign-left valign-top">Ghost methods</th><th class="tableblock halign-left valign-top">Usage</th></tr></thead><tbody><tr><td class="tableblock halign-left valign-top"><div><div class="paragraph"><p>getOverlappingObjects()</p></div></div></td><td class="tableblock halign-left valign-top"><div><div class="paragraph"><p>Returns the List of objects that are currently colliding (overlapping) with the ghost.</p></div></div></td></tr><tr><td class="tableblock halign-left valign-top"><div><div class="paragraph"><p>getOverlappingCount()</p></div></div></td><td class="tableblock halign-left valign-top"><div><div class="paragraph"><p>Returns the number of currently colliding objects.</p></div></div></td></tr><tr><td class="tableblock halign-left valign-top"><div><div class="paragraph"><p>getOverlapping(i)</p></div></div></td><td class="tableblock halign-left valign-top"><div><div class="paragraph"><p>Get PhysicsCollisionObject number i.</p></div></div></td></tr></tbody></table></div></div>
  18. <div class="sect2"><h3 id="physics-tick-listener">Physics Tick Listener</h3><div class="paragraph"><p>The jBullet Physics implementation is stepped at a constant 60 physics ticks per second frame rate.
  19. Applying forces or checking for overlaps only has an effect right at a physics update cycle, which is not every frame. If you do physics interactions at arbitrary spots in the simpleUpdate() loop, calls will be dropped at irregular intervals, because they happen out of cycle.</p></div>
  20. <div class="sect2"><h3 id="when-not-to-use-tick-listener">When (Not) to Use Tick Listener?</h3><div class="paragraph"><p>When you write game mechanics that apply forces, you must implement a tick listener (com.jme3.bullet.PhysicsTickListener) for it. The tick listener makes certain the forces are not dropped, but applied in time for the next physics tick.</p></div>
  21. <div class="paragraph"><p>Also, when you check for overlaps of two physical objects using a GhostControl, you cannot just go <code>ghost.getOverLappingObjects()</code> somewhere outside the update loop. You have to make certain 1 physics tick has passed before the overlapping objects list is filled with data. Again, the PhysicsTickListener does the timing for you.</p></div>
  22. <div class="paragraph"><p>When your game mechanics however just poll the current state (e.g. getPhysicsLocation()) of physical objects, or if you only use the GhostControl like a sphere trigger inside an update loop, then you don&#8217;t need an extra PhysicsTickListener.</p></div></div>
  23. <div class="sect2"><h3 id="how-to-listen-to-physics-ticks">How to Listen to Physics Ticks</h3><div class="paragraph"><p>Here&#8217;s is the declaration of an examplary Physics Control that listens to ticks. (The example shows a RigidBodyControl, but it can also be GhostControl.)</p></div>
  24. <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">MyCustomControl</span>
  25. <span class="directive">extends</span> RigidBodyControl <span class="directive">implements</span> PhysicsTickListener { ... }</code></pre></div></div>
  26. <div class="paragraph"><p>When you implement the interface, you have to implement <code>physicsTick()</code> and <code>preTick()</code> methods.</p></div>
  27. <div class="ulist"><ul><li><p><code>prePhysicsTick(PhysicsSpace space, float tpf)</code> is called before each step, here you apply forces (change the state).</p></li><li><p><code>physicsTick(PhysicsSpace space, float tpf)</code> is called after each step, here you poll the results (get the current state).</p></li></ul></div>
  28. <div class="paragraph"><p>The tpf value is time per frame in seconds. You can use it as a factor to time actions so they run equally on slow and fast machines.</p></div>
  29. <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@override</span>
  30. <span class="directive">public</span> <span class="type">void</span> prePhysicsTick(PhysicsSpace space, <span class="type">float</span> tpf){
  31. <span class="comment">// apply state changes ...</span>
  32. }
  33. <span class="annotation">@override</span>
  34. <span class="directive">public</span> <span class="type">void</span> physicsTick(PhysicsSpace space, <span class="type">float</span> tpf){
  35. <span class="comment">// poll game state ...</span>
  36. }</code></pre></div></div></div></div>
  37. <div class="sect2"><h3 id="physics-collision-listener">Physics Collision Listener</h3><div class="sect2"><h3 id="when-not-to-use-collision-listener">When (Not) to Use Collision Listener</h3><div class="paragraph"><p>If you do not implement the Collision Listener interface (com.jme3.bullet.collision.PhysicsCollisionListener), a collisions will just mean that physical forces between solid objects are applied automatically. If you just want “Balls rolling, bricks falling you do not need a listener.</p></div>
  38. <div class="paragraph"><p>If however you want to respond to a collision event (com.jme3.bullet.collision.PhysicsCollisionEvent) with a custom action, then you need to implement the PhysicsCollisionListener interface. Typical actions triggered by collisions include:</p></div>
  39. <div class="ulist"><ul><li><p>Increasing a counter (e.g. score points)</p></li><li><p>Decreasing a counter (e.g. health points)</p></li><li><p>Triggering an effect (e.g. explosion)</p></li><li><p>Playing a sound (e.g. explosion, ouch)</p></li><li><p>… and countless more, depending on your game</p></li></ul></div></div>
  40. <div class="sect2"><h3 id="how-to-listen-to-collisions">How to Listen to Collisions</h3><div class="paragraph"><p>You need to add the PhysicsCollisionListener to the physics space before collisions will be listened for. Here&#8217;s an example of a Physics Control that uses a collision listener. (The example shows a RigidBodyControl, but it can also be GhostControl.)</p></div>
  41. <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">MyCustomControl</span> <span class="directive">extends</span> RigidBodyControl
  42. <span class="directive">implements</span> PhysicsCollisionListener {
  43. <span class="directive">public</span> MyCustomControl() {
  44. bulletAppState.getPhysicsSpace().addCollisionListener(<span class="local-variable">this</span>);
  45. ...
  46. }</code></pre></div></div>
  47. <div class="paragraph"><p>To respond to the PhysicsCollisionEvent you now have to override the <code>collision()</code> method in MyCustomControl. This gives you access to the <code>event</code> object. Mostly you will be interested in the identity of any two nodes that collided: <code>event.getNodeA()</code> and <code>event.getNodeB()</code>.</p></div>
  48. <div class="paragraph"><p>After you identify the colliding nodes, specify the action to trigger when this pair collides. Note that you cannot know which one will be Node A or Node B, you have to deal with either variant.</p></div>
  49. <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java"> <span class="directive">public</span> <span class="type">void</span> collision(PhysicsCollisionEvent event) {
  50. <span class="keyword">if</span> ( event.getNodeA().getName().equals(<span class="string"><span class="delimiter">&quot;</span><span class="content">player</span><span class="delimiter">&quot;</span></span>) ) {
  51. <span class="directive">final</span> Node node = event.getNodeA();
  52. <span class="comment">/** ... do something with the node ... */</span>
  53. } <span class="keyword">else</span> <span class="keyword">if</span> ( event.getNodeB().getName().equals(<span class="string"><span class="delimiter">&quot;</span><span class="content">player</span><span class="delimiter">&quot;</span></span>) ) {
  54. <span class="directive">final</span> Node node = event.getNodeB();
  55. <span class="comment">/** ... do something with the node ... */</span>
  56. }
  57. }</code></pre></div></div>
  58. <div class="admonitionblock important"><table><tr><td class="icon"><i class="fa icon-important" title="Important"></i></td><td class="content"><div class="paragraph"><p>Note that after the collision() method ends, the PhysicsCollisionEvent is cleared. You must get all objects and values you need within the collision() method.</p></div></td></tr></table></div></div>
  59. <div class="sect2"><h3 id="reading-details-from-a-physicscollisionevent">Reading Details From a PhysicsCollisionEvent</h3><div class="paragraph"><p>The PhysicsCollisionEvent <code>event</code> gives you access to detailed information about the collision. You already know the event objects can identify which nodes collided, but it even knows how hard they collided:</p></div>
  60. <table class="tableblock frame-all grid-all spread"><colgroup><col style="width: 50%;"><col style="width: 50%;"></colgroup><thead><tr><th class="tableblock halign-left valign-top">Method</th><th class="tableblock halign-left valign-top">Purpose</th></tr></thead><tbody><tr><td class="tableblock halign-left valign-top"><div><div class="paragraph"><p>getObjectA()<br>
  61. getObjectB()</p></div></div></td><td class="tableblock halign-left valign-top"><div><div class="paragraph"><p>The two participants in the collision. You cannot know in advance whether some node will be recorded as A or B, you always have to consider both cases.</p></div></div></td></tr><tr><td class="tableblock halign-left valign-top"><div><div class="paragraph"><p>getAppliedImpulse()</p></div></div></td><td class="tableblock halign-left valign-top"><div><div class="paragraph"><p>A float value representing the collision impulse</p></div></div></td></tr><tr><td class="tableblock halign-left valign-top"><div><div class="paragraph"><p>getAppliedImpulseLateral1()</p></div></div></td><td class="tableblock halign-left valign-top"><div><div class="paragraph"><p>A float value representing the lateral collision impulse</p></div></div></td></tr><tr><td class="tableblock halign-left valign-top"><div><div class="paragraph"><p>getAppliedImpulseLateral2()</p></div></div></td><td class="tableblock halign-left valign-top"><div><div class="paragraph"><p>A float value representing the lateral collision impulse</p></div></div></td></tr><tr><td class="tableblock halign-left valign-top"><div><div class="paragraph"><p>getCombinedFriction()</p></div></div></td><td class="tableblock halign-left valign-top"><div><div class="paragraph"><p>A float value representing the collision friction</p></div></div></td></tr><tr><td class="tableblock halign-left valign-top"><div><div class="paragraph"><p>getCombinedRestitution()</p></div></div></td><td class="tableblock halign-left valign-top"><div><div class="paragraph"><p>A float value representing the collision restitution (bounciness)</p></div></div></td></tr></tbody></table>
  62. <div class="paragraph"><p>Note that after the collision method has been called the object is not valid anymore so you should copy any data you want to keep into local variables.</p></div></div>
  63. <div class="sect2"><h3 id="collision-groups">Collision Groups</h3><div class="paragraph"><p>You can improve performance by resricting the number of tests that collision detection has to perform. If you have a case where you are only interested in collisions between certain objects but not others, you can assign sets of physical obejcts to different collision groups.</p></div>
  64. <div class="paragraph"><p>For example, for a click-to-select, you only care if the selection ray collides with a few selectable objects such as dropped weapons or powerups (one group), but not with non-selectables such as floors or walls (different group).</p></div>
  65. <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java">myNode.getControl(RigidBodyControl.class).setCollisionGroup(PhysicsCollisionObject.COLLISION_GROUP_02);
  66. myNode.getControl(RigidBodyControl.class).setCollideWithGroups(PhysicsCollisionObject.COLLISION_GROUP_02);</code></pre></div></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({
  67. apiKey: 'a736b6d93de805e26ec2f49b55013fbd',
  68. indexName: 'jmonkeyengine',
  69. inputSelector: '#doc-search',
  70. debug: false // Set debug to true if you want to inspect the dropdown
  71. });</script></body></html>