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
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.
ReplyDeleteHah! 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