Tuesday, August 7, 2012

No User Input Collisions

‹prev | My Chain | next›

I was able to get Box2D.js collision detection working in Gladius yesterday. The trick turned out to be rotating everything into the XY plane, Box2D being a two dimensional tool and all. I had to strip things down to a very simple test case before it worked properly. Tonight, I hope to get all the pieces put back together.

I continue to use the Box2D onContactBegin and onContactEnd callbacks to change the color of an spherical obstacle:
        obstacleBox2d.onContactBegin = function(event){
          console.log("Obstable contact begin");
         this.owner.findComponent( "Model").setMaterialDefinition(resources.grey_material);
        };
        obstacleBox2d.onContactEnd = function(event){
          console.log("Obstable contact end");
         this.owner.findComponent( "Model").setMaterialDefinition(resources.yellow_material);
        };
When contact between my avatar (just a code in the stripped down case) and the obstacle, the obstacle turns grey. When contact ends, the obstacle reverts back to the starting yellow:


The first thing that I would like to restore is the input logic that controls the avatar. Without it, I am force to manually set the position, then reload the page.

So I re-add the require.js declaration:
  require(
    [ "gladius-core", "gladius-cubicvr", "gladius-box2d", "gladius-input" ],
    function( Gladius, cubicvrExtension,  box2dExtension, inputExtension ) {
    // ...
  });
I have to register the extension with the engine and map keys to controller states:
      var engine = new Gladius();

      engine.registerExtension(inputExtension, {
        dispatcher: {
          element: document
        }
      });

      var avatarControls = new engine["gladius-input"].Map({
        "States": {
          "moveForward":  ["W"],
          "moveBackward": ["S"],
          "moveLeft":     ["A"],
          "moveRight":    ["D"]
        }
      });
      resources['avatar_controls'] = avatarControls;

      engine.resume();

      game(engine, resources);
And, in the game() function itself, I have to again grab the input extension and use it to map controller states to avatar movement:


      function game(engine, resources) {
        var cubicvr = engine.findExtension("gladius-cubicvr");
        var box2d = engine.findExtension( "gladius-box2d" );
        var input = engine.findExtension( "gladius-input" );
        // ...
        var avatarLogic = {
          "Update": function(event) {
            if (!this.owner.hasComponent("Controller")) return;

            var controller = this.owner.findComponent("Controller")
              , transform = this.owner.findComponent("Transform");

            var position;
            if (controller.states["moveLeft"]) {
              position = [-space.clock.delta * 0.01, 0, 0];
            }
            if (controller.states["moveRight"]) {
              position = [space.clock.delta * 0.01, 0, 0];
            }
            if (controller.states["moveForward"]) {
              position = [0,  space.clock.delta * 0.01, 0];
            }
            if (controller.states["moveBackward"]) {
              position = [0, -space.clock.delta * 0.01, 0];
            }

            if (position) {
              var pos = math.vector3.add(position, transform.position);
              transform.setPosition(pos);
              // console.log(pos);
            }
          }
        };
        // ...
      }
All of this was previously done (though the movement is now restricted to the XY plane for Box2D purposes). But I have to comment it all out when not using the input extension less I get the dreaded "already completed" errors. With that, I can add the controller and actor logic to the avatar so that I should have collision detection as well as user control of the avatar:
        av = new engine.Entity("avatar",
          [
            new engine.core.Transform([0,0,0], [Math.PI/2, 0, 0]),
            avatarBox2d,
            new cubicvr.Model(resources.cone_mesh, resources.red_material),
            new input.Controller( resources.avatar_controls ),
            new engine.logic.Actor( avatarLogic )
          ]
        );
        space.add(av);
Everything loads OK, but... I cannot move the avatar.

My first thought is that I have somehow messed up the controls, but debugging the setPosition() call in the actor logic, it seems that the position changes are being ignored.

Eventually, I realize that, if I remove the Box2D component from the Entity:
        av = new engine.Entity("avatar",
          [
            new engine.core.Transform([0,0,0], [Math.PI/2, 0, 0]),
            // avatarBox2d,
            new cubicvr.Model(resources.cone_mesh, resources.red_material),
            new input.Controller( resources.avatar_controls ),
            new engine.logic.Actor( avatarLogic )
          ]
        );
        space.add(av);
Then I can again move the avatar, but, of course, collision detection no longer works (the obstacle stays yellow):


Further investigation reveals that I can setPosition() in the z-direction only. That is, a manual av.findComponent("Transform").setPosition([10, 10, 10]) has the effect of position the avatar at [0, 0, 10]. As strange as it seems, the Box2D extension seems to prevent movement in the directions in which it is attempting to see collisions.

I try moving the Box2D body into a separate Entity with the avatar as the parent Entity:
        space.add(new engine.Entity("avatar-box2d",
          [
            new engine.core.Transform(),
            avatarBox2d
          ],
          [],
          space.findNamed("avatar")
        ));
But collisions are still no detected. I suspect that this is because the XY position for the Box2D body is 0,0 relative to the avatar's frame of reference. If I specify a starting position of 10 pixels to the left of the avatar, the same distance the obstacle is from the origin:
        space.add(new engine.Entity("avatar-box2d",
          [
            new engine.core.Transform([-10, 0, 0]),
            avatarBox2d
          ],
          [],
          space.findNamed("avatar")
        ));
Then, I see a collision:


But of course there should be no collision. Furthermore, moving the avatar (and hence the child Box2D body) does not see the end of the collision:





Somehow the Box2D coordinates remain fixes. I'm stumped on this one, so I am forced to call it a night here. Hopefully something will occur to my while I sleep (or someone will kindly tell me where I am doing wrong).

Day #471

2 comments:

  1. I think you're fighting with the physics simulation here. Box2D wants to set the position of your objects, so I suggest moving your actors around by applying forces rather than updating the position directly.

    ReplyDelete
    Replies
    1. Hah! It's amazing how much I have to learn here. That makes perfect sense and even explains why I can move in the Z direction. It'll be interesting to see how well forces can look like regular first-person movement. Good grist for tonight. Thanks!

      Delete