Wednesday, January 23, 2013

Simple 2D Physics in Three.js

‹prev | My Chain | next›

I have been toying with the idea of including a simple game like Doodle Jump in 3D Game Programming for Kids. Admittedly, it is not 3D, but it still seems like it might be fun. I just don't know how easy it would be for kids to code. There is one way to find out...

First up, I need to add physics to the game, which means Physijs in addition to the usual Three.js. I do not have a template for this in my fork of the code editor. I may need to do that before the final version of the book. For now, I manually add the <script> tag:
<body></body>
<script src="http://gamingJS.com/Three.js"></script>
<script src="http://gamingJS.com/physi.js"></script>
<script src="http://gamingJS.com/ChromeFixes.js"></script>
And the normal phsysijs settings in the actual code:
  // Physics settings
  Physijs.scripts.ammo = 'http://gamingJS.com/ammo.js';
  Physijs.scripts.worker = 'http://gamingJS.com/physijs_worker.js';

  // This is where stuff in our game will happen:
  var scene = new Physijs.Scene({ fixedTimeStep: 2 / 60 });
  scene.setGravity(new THREE.Vector3( 0, -100, 0 ));
The numbers in there are just some that seem to move things well.

Next, I do something a little different—I use an orthographic camera for better 2D presentation:
  var width = window.innerWidth,
      height = window.innerHeight;
  var camera = new THREE.OrthographicCamera( width / - 2, width / 2, height / 2, height / - 2, 1, 1e6 );
  camera.position.z = 500;
  scene.add(camera);
For that to work, I have to swap out the usual least-common-denominator canvas camera for the WebGL equivalent:
  var renderer = new THREE.WebGLRenderer();
  renderer.setSize(window.innerWidth, window.innerHeight);
  document.body.appendChild(renderer.domElement);
If I end up including this in the book, I will have to check this out with a perspective camera and the canvas renderer to see if it is worth having kids without WebGL attempt this.

With that, I am ready to start adding my player and the platforms. I start with a function to randomly place platforms:
  function makePlatform() {
    var box = new Physijs.BoxMesh(
      new THREE.CubeGeometry(100, 10, 1),
      new THREE.MeshNormalMaterial()
    ); 

    box.position.set(
      (width/2 - width * Math.random()) * 0.75, 
      (height/2 - height * Math.random()),
      0
    );
    
    return box;
  }
Nothing too fancy there. I make the usual Phsysijs box mesh so that the player can interact with the platform. I will have to add bounce to it later, but this should be a good start. The width and height variables come from the calculations that used the browser's width and height to build the camera.

I call this function manually four times:
  scene.add(makePlatform());
  scene.add(makePlatform());
  scene.add(makePlatform());
  scene.add(makePlatform());
Which gives me platforms:


I do similar work to create a player in the shape of a disc, placing it at the top of the screen and giving it a little horizontal shove:
  var player = new Physijs.ConvexMesh(
    new THREE.CylinderGeometry(20, 20, 1, 24),
    new THREE.MeshNormalMaterial()
  );
  
  player.position.set(100 + width/-2, height/2, 0);
  player.rotation.x = Math.PI/2;
  scene.add(player);
  player.setLinearVelocity(
    new THREE.Vector3(10, 0, 0)
  );
With that, I am ready to animate my code:
  function animate() {
    requestAnimationFrame(animate);
    scene.simulate(); // run physics
    renderer.render(scene, camera);
  }
  animate();
In addition to the usual Three.js animation, I also have to tell Phsysijs to do its thing, which it does, but... I forgot to peg the platforms. The player and the platforms fall under the influence of Phsyijs gravity. To fix, I add a third parameter, zero, to the platforms' BoxMesh constructor to tell the physics engine that these platforms are not affected by gravity:
  function makePlatform() {
    var box = new Physijs.BoxMesh(
      new THREE.CubeGeometry(100, 10, 1),
      new THREE.MeshNormalMaterial(),
      0
    ); 

    box.position.set(
      (width/2 - width * Math.random()) * 0.75, 
      (height/2 - height * Math.random()),
      0
    );
    
    return box;
  }
This reminds me that I also need to constrain the player from rotating or from moving in-or-out of the screen (this is a 2D game):
  player.setAngularFactor(new THREE.Vector3( 0, 0, 1 )); // only rotate around the Z axis
  player.setLinearFactor(new THREE.Vector3( 1, 1, 0 )); // only move on X and Y axis
With that, I have my player falling and hitting platforms. There is still much to be done, but this will serve as a good stopping point. I'm glad that I have not forgotten all of this. It does help to have the pre-beta copy of the book around to refresh my memory on some of this stuff as well.

(the code so far)


Day #639

No comments:

Post a Comment