Change Requests for Physics Plugin
Shri

Gio,

While making the one way plaftorm demo, I came across two things which I think should go into the next version of the physics plugin release.

  1. There is no way to set user data for a fixture through wade parameters. This means you have to manually use fixture.m_userData = "user data". You set it per physics body as the scene object, so I think this was a simple oversite and should be a one line fix to the addFixture function, somewhere around line 360 of physicsObject.js, "fixDef.userData = fixtureData.userData;"
  2. The physics behavior getFixtureList() function returns the fixture list array in box2d format. The array returned is really just a pointer to the head of the fixture linked list. Which means that to access all the elements of the "array" you have to walk the list by assigning the next pointer manually (i.e. fixture = fixture.m_next). It seems that instead, the array should be populated for you before return. That way, you could refer to the fixtures as (fixtureList[0], fixtureList[1], etc) which would be a more intuitive way to access the array

 

just my two cents - cheers - shri

All 9 Comments
Gio

Hi Shri

The PhysicsObject behavior has got a bunch of public properties, one of them being a fixtures "array". I say "array" in quotes because it isn't quite, strictly speaking, an array, but it behaves like one for most purposes. So you should be able to do:

var behavior = myObject.getBehavior('PhysicsObject');
behavior.fixtures[0].restitution = 2;  // or whatever you want to do with your fixtures
behavior.fixtures[1].friction = 0;

I'd like to understand what the use case for userData would be. Could you not do

behavior.fixtures[0].myProperty = myValue;

or do you need to use it in a different way?

Shri

Gio,

I have broken the stickman into 3 fixtures head, body and foot. The onCollision data returns which two fixtures are in collision. It is easiest to figure out which fixtures are in collision by referring to the name that was set via the fixture user data. Based on the box2d api, you should be able to set and get fixture user data. So, for example, when a collision occurs between the "head" of the stickman and a platform, I turn off collisions so he can pass through using this check:

else if ((data.contact.m_fixtureB.m_userData == "head" && data.contact.m_fixtureA.m_userData == "platform") || (data.contact.m_fixtureA.m_userData == "head" && data.contact.m_fixtureB.m_userData == "platform"))

When I do it the box2d way via fixture user data everthing works fine. When I try to do it the way you are suggesting

else if ((data.contact.m_fixtureB.isHead == true && data.contact.m_fixtureA.m_userData == "platform") ||
                (data.contact.m_fixtureA.isHead == true && data.contact.m_fixtureB.m_userData == "platform"))

It does not work.I think the reason is because, the box2d engine does not keep/pass the extra information you set on a fixture. So even though it is accessible/visible in wade, it is ignored by the physics engine. The fixture user data was added for this specific case. As I pointed out above, it is a one line change to the code, and then you could just do something like this when creating a fixture

fixtures: {
	0: { density: 1, friction: 0.5, restitution: 0.4, shapeType: "box",
	spriteIndex: 0, isSensor: false, autoCalculateShape: true,
	filter: { groupIndex: 0, categoryBits: 1, maskBits: 65535, userData: "head" }
	},

Rather then having to do it manually like:

var fixtures = player.physics.getFixtureList();
fixtures.m_userData = "head";

Regarding the other point, I am referring to the getFixtureList() in the physics behavior api  "Get a list of fixtures attached to this object Returns: {Array} An array of fixtures attached to this physics body"

var fix = player.physics.getFixtureList();
console.log(fix); --> returns b_d.b2Fixture {m_proxyCount: 1, m_filter: b…d.b2Filter, m_userData: "head", m_friction: 0.5, m_restitution: 0.4…}
console.log(fix[0]) --> undefined
console.log(fix[1]) --> undefined

So, when I set and reset the stickman as a sensor (to pass through platforms) or not (normal collision) I use

// set the three fixtures of the player to the value passed in
// set sensor to true to not have collisions, false to have collisions
this.setSensor = function(val) {
	self.owner.isSensor = val;
	var fixtures = self.owner.physics.getFixtureList();
	while (fixtures != null) {
		fixtures.m_isSensor = val;
		fixtures = fixtures.m_next;
	}
}	// end setSensor

The api definition implies that you should be able to access the returned values via fixtures[0],fixtures[1], fixtures.length, etc. So, you should either change the definition in the api or (MY VOTE) populate the array before returning from getFixtureList().

cheers - shri

Gio

I see what you mean.

I don't think PhysicsObejct.getFixtureList() should exist to be honest - it's there from before we defined a nice interface to access fixtures through wade, without going through box2d. As it is now, it's probably only confusing.

What you should do to set and reset the stickman as a sensor is this:

var fixtures = self.owner.physics.fixtures;
for (var i=0; i<fixtures.length; i++)
{
    fixtures[i].isSensor = val;
}

In other words, you shouldn't need to access the b2Fixture objects of box2d directly. Unless you really want to, in which case you can get the _b2dBody object and use the box2d API from there. However I would not recommended it - we are trying to have an abstract API, that doesn't necessarily reflect the underlying engine's (box2d's) API, as this will allow us to potentially switch or upgrade physics engines without users having to change their code.

This is easier said than done, especially because wade.physics is, admittedly, not finished, so I perfectly understand that you'll want to access the underlying physics engine to do things that the wade API does not support yet.

And I can see where you're coming from, because in the collision event we are effectively giving you data straight out of box2d. We should change this, the data that you get in the collision event should contain WADE fixtures as opposed to box2d fixtures. I'll see if we can get this fixed quickly.

WADE fixtures are plain JS objects. Well not quite plain if you want to get technical. But anyway, you'll be able to store your own properties in those objects and read them back later, potentially negating the need for a separate userData field.

Shri

Gio,

Yes, I agree that an abstract interface to the physics engine is the desired outcome. I like the way "corona" and "phaser" have done it personally.

The point I was trying to make with the getFixtureList comment was that the end user had to look into the underlying box2d stuff to do what I wanted to do, and that is not good.

If you do this: "We should change this, the data that you get in the collision event should contain WADE fixtures as opposed to box2d fixtures", there will be no need to add the fixture user data.

I put in your suggested for loop for sensor setting into the next demo I am working on and it seems to work as advertised.

cheers -shri

Gio

OK, sorted I think. In the next version, the data object that gets passed to the onCollision event will contain a fixtures object like this

fixtures:
{
    A: fixtureForObjectA,
    B: fixtureForObjectB
}

Where fixtureForObjectA and fixtureForObjectB are WADE fixtures, not box2d fixtures.

I'd like to get your input regarding the rest of the data that is passed to onCollision. I think we need manifold because that tells you where exactly things have been colliding and the collision normal(s). However we should obviously convert the units to WADE coordinates first.

Do you think we need the "points" array in the manifold structure too? I'd be inclined to remove it.

I think we don't need the "contact" object either, since you now have other ways to access the fixtures and the manifold. However, I can see why that would be useful in some (probably exceptionally rare) cases. Perhaps we could keep it, but call it "_contact" - people generally now that they're not supposed to use things that start with an underscore as they're mostly for internal use.

So basically this is what I'm proposing:

data:
{
    bodyIndex: string, // either 'A' or 'B'
    fixtures:
    {
        A: wadeFixture,
        B: wadeFixture
    },
    manifold:
    {
        localNormal: wade.vec2,
        localPoint: wade.vec2,
        type: string // could be 'circles', 'faceA' or 'faceB'
    },
    otherObject: SceneObject,
    _contact: b2PolygonContact // or other b2Contact class
}

Unfortunately this will break projects relying on the current data structure, though I don't think a lot of people use manifold data directly (most wade.physics projects I've seen just use otherObject to check what you're colliding with), and it should be easy to switch to the new format.

Any thoughts?

Shri

Gio,

I was refreshing my collision understanding by reading this link Near the middle, he gives some information regarding a use of the points array, search for this passage: "It's important to be aware that the collision normal does not give you the angle that these fixtures collided at -" He then goes to show how you would get the collision angle even if both objects are moving and rotating.

b2Vec2 vel1 = triangleBody->GetLinearVelocityFromWorldPoint( worldManifold.points[0] );
b2Vec2 vel2 = squareBody->GetLinearVelocityFromWorldPoint( worldManifold.points[0] );
b2Vec2 impactVelocity = vel1 - vel2;

I could see a case for wanting to get the collision angle. I think I can already do this in wade though since I would know the velocities and positions of the two scene objects prior to colliding. Maybe you could add that to the data set ? Other than that, I can't think of a reason to keep the points array.

The contact object is a must have for disabling collisions (contact.SetEnabled(false);) for use in one way platforms or if you wanted the ability to pass through something like a wall. The SetFriction and SetRestitution methods of the contact object are also useful (think of different bumpers types in pinball). I'm not sure if you want to abstract out and minimmize the contact functionality and return a "wade contact object" ?

Lastly, I don't know if you want to provide a post solve callback ? This link gives you one case and there are others where you would want to know what the impulse response on the collision was and then do something before the post solver does its thing.

my two cents - cheers - shri

Gio

Thanks for your input Shri. Much appreciated.

Keeping in mind that we have to find a compromise between features and ease of use, and that you can always get the underlying _b2dBody object to do some really advanced stuff through the box2d API if you really need to, I have two questions that would help me make a better judgment:

1. You say "the contact object is a must have for disabling collisions"

You can cancel collisions just by returning true from the onCollisionEvent, in line with all other WADE events. Is that enough, or would you still need the contact object to cancel collisions in a different way? To me it sounds like it would be redundant for this purpose, but I may not be thinking of all use cases.

2. You say "the SetFriction and SetRestitution methods of the contact object are also usefu (think of different bumpers types in pinball)"

Couldn't this be achieved by setting the friction and restitution on the pinball fixtures to start with? Why do it in the onCollision event?

I know that foxcode has been doing quite a bit of physics stuff with WADE recently, I'd like to know what he thinks too when he's back on Monday, then we'll make a decision regarding the data that should be passed to onCollision. If you have any more thoughts, please let us know.

Shri

Gio,

1. I thought the purpose of returning true from a wade event is to prevent the event from bubbling. If you return true from onCollision(), that should not disable the contact. Disabling the contact should be a special (but neccessary) case which is why you would have to pass through the box2d contact object. For example, in the "normal" case, say you want to change the color of a ball if it hits a certain object

ball.onCollision = function(data) {
    if the ball hits a monster then change color to red
    else if the ball hits a wall, then change color to grey
    return true;  
}

In the special case, lets say you want to change color, but then pass through the monster using SetEnabled(false)

ball.onCollision = function(data) {
    If the ball hits a monster {
        change color to red
        data.contact.SetEnabled(false)
    }
    else if the ball hits a wall {
        change color to grey
        STILL DO COLLISION
    }
    return true;
}

2. The following is from a buletin board post:

"When two fixtures get close to each other a b2Contact is created to manage any potential interaction between them. The info from the fixtures (eg. friction) is copied into the b2Contact, and the original values in the fixtures themselves are no longer referenced for that contact. To make any changes of the kind you are wanting, you'll have to change the friction values in the b2Contact rather than the fixtures. You can do this in any of the contact listener functions, as they all pass the b2Contact to you. b2Contact has SetFriction and a few other functions which will come in handy for this kind of customization:".

So no, setting the values on the fixture would not work in the collision

For further information, please refer to the box2d manual - section 9, specifically 9.4

A few post ago, you said " I think we don't need the "contact" object either ...", and that is why I made the comment that the b2d contact object is a must have for the data in the onCollision() event. In you last post you wrote " you can always get the underlying _b2dBody object..."  but you are abstracting out some of the data and not returning all of it.

Maybe instead of returning _contact as the last object, you should just return _rawData as the original box2d collision data, that way if anyone wants to muck around with the box2d stuff they have it and you don't have to wrestle with what  to and not to return.

cheers - shri

Gio

I think that having to do data.contact.setEnabled(false) to cancel a collision is far from being intuitive enough. As you know, we try to make our API as accessible as possible, and if we did it that way users would have to know quite a bit of how things work in order to cancel a collision. The current way (return true) is definitely much easier.

However you have the right idea about returning _rawData, so users get the best of both worlds: the intuitive simple interface, and a way to do more complex stuff if they so wish, without having to mess about with contact listeneres and all that.

The only caveat is that anything starting with underscores in the API is always subject to change - but most people know that, so we should be alright. I'll also add a note in the documentation about using it at your own risk.

Post a reply
Add Attachment
Submit Reply
Login to Reply