How are bodyA and bodyB determined in SKPhysicsCon

2019-07-26 13:09发布

问题:

I have two different fixed SKPhysicsBody in an SKScene. The only difference between the two is their categoryBitMask. One has a categoryBitMask of 512, the other 1024.

static const u_int32_t kWallCategory = 0x1 << 9;         //512  
static const u_int32_t kStructureCategory = 0x1 << 10;    //1024

In my program, I have a standard contact handler called from -(void)didBeginContact:(SKPhysicsContact *)contact that begins:

-(void)handleContact:(SKPhysicsContact*)contact {
     SKPhysicsBody *bodyA = contact.bodyA;
     SKPhysicsBody *bodyB = contact.bodyB;
     int type1 = contact.bodyA.categoryBitMask;
     int type2 = contact.bodyB.categoryBitMask;

In my program, I need to determine the contact vector between a dynamic body and one of the two fixed bodies. In order to do so, I access the contactNormal property of the SKPhysicsContact

CGVector c = contact.contactNormal;

What I noticed, though, was that the contact vectors were inconsistent and I determined this was because sometimes bodyA of the SKPhysicsContact was the fixed body, sometimes bodyA was the dynamic body.

For example, when a bullet (dynamic body) hits a building (fixed body) from the left, I want the contact vector to be (-1,0) each time. Currently, the contact vector is sometimes (-1,0) (from the left) and sometimes (1,0) (from the right) all depending on which body contact.bodyA is.

My question: given all else being equal (physics properties, etc.) what determines what is bodyA and what is bodyB in an SKPhysicsContact?

回答1:

After tinkering with several aspects of each SKPhysicsBody (mass, friction, categoryBitmask, etc), I've ascertained that the determination between SKPhysicsContact.bodyA and SKPhysicsContact.bodyB is based solely on when the SKPhysicsBody is added to the scene

An example to highlight the difference:

SKPhysicsBody *bullet hits SKPhysicsBody *building from the left.

Case 1: If *building was added to the scene before *bullet, the SKPhysicsContact.contactNormal is: (-1,0) Because SKPhysicsContact.bodyA is *building, therefore the contact vector is where the *building is being contacted from (i.e. the left).

Case 2: If *building was added to the scene after *bullet, the SKPhysicsContact.contactNormal is: (1,0) Because SKPhysicsContact.bodyA is now *bullet, therefore the contact vector is where the *bullet is being contacted from (i.e. the right).

Problem

Because *bullet and *building entities are added at various times in my program, I needed to implement a method in which I always knew what SKPhysicsContact.bodyA and SKPhysicsContact.bodyB were and, more importantly, a way to keep the SKPhysicsContact.contactNormal consistent.

Solution

Determine the SKPhysicsBody that you need to determine where collisions are coming from and set the physicsBody.categoryBitMask

static const u_int32_t kProjectileCategory = 0x1 << 1;  //Dynamic body 
//Make everything with bitmask > 32 will be set for the fixed bodies
static const u_int32_t kStructureCategory = 0x1 << 10;  //Fixed body

Then, in the contact handler:

-(void)handleContact:(SKPhysicsContact*)contact {
//bodyA will be the SKPhysicsBody that was added to the SKScene before bodyB
SKPhysicsBody *bodyA = contact.bodyA;

//bodyB will be the SKPhysicsBody that was added to the SKScene after bodyA
SKPhysicsBody *bodyB = contact.bodyB;

int categoryBitmask1 = contact.bodyA.categoryBitMask;
int categoryBitmask2 = contact.bodyB.categoryBitMask;

CGVector v;

//If the categoryBitmask of bodyA is lower, we will get the contact vector from the Dynamic body, which we don't want
if (categoryBitmask1 < categoryBitmask2)
{
    //Since we always want the contact vector determined FROM the fixed body (higher category bitmask), we have to get the reciprocal contact vector in the case that bodyA is the dynamic (non-fixed) body
    v = CGVectorMake(contact.contactNormal.dx * -1, contact.contactNormal.dy * -1);
} else {
    //If the categoryBitmask
    v = contact.contactNormal;
}