Bitmask inversion and performance

My current game is intended to involve some simple melee combat; to this end, when characters attack a CollisionSphere is attached to a particular joint (if present), and damage is dealt if it is detected to collide with another character’s personal collision geometry.

This presents a problem, however: since the joint in question is, in my main character’s case, at least, sufficiently close to the character’s collision geometry, collisions are detected between the character and its own attack geometry.

I have two solutions for this, I believe:

  1. When the collision event is fired, compare the object impacted to the object that the attack is attached to. If they are the same, return without further action.

  2. Set up bitmasks such that collision events are not fired for attacks and their owners.

Of those, bitmasks seem better to me: I imagine that this method would result in less calls, since the events wouldn’t be fired.

So, to this end I tried setting an “into” bitmask in each character, and then setting the inverse “from” bitmask in each attack object, using “invertInPlace”.

The resulting code looks something like this in the relevant sections:

class RevenantActor(GameObject):
  def __init__(self, -parameters here-):
     ...        
     # "id" is currently just an integer, as I recall, 
     #  with the two current characters using the
     #  values 1 and 2
     self.id = id
     self.collider.setCollideMask(BitMask32(self.id))        
     ...

  def shoot(self):
     ...
     #"shot" is a tuple, the first value indicating
     # whether the shot was fired, and the
     # second containing the attack object itself,
     # if applicable.
     if shot[0]:
         mask = BitMask32(self.id)
         #print mask
         mask.invertInPlace()
         #print mask
         shot[1].collider.node().setFromCollideMask(mask)
        ...

The problem is that, with invertInPlace, uh, “in place”, the game’s performance drops through the floor. With the other method given above, no such problem seems to be present.

This seems to be related to the currently-incomplete state of my enemy character, which would appear to result in multiple attack objects overlapping each other.

That seems fair enough at first, but according to a print statement, the numbers in question don’t seem to go above ten overlapping shots, and again, this doesn’t seem to be a problem when I’m comparing the shot’s owner to the impacted object. Similarly, if the objects are created with the same frequency, but made short-enough lived that they don’t overlap, the problem seems to disappear (or at least be significantly reduced).

Why is this? Is there a better method than invertInPlace?

I suspect that what is happening is that, when you invert your bitmask, you are inadvertently adding in the bit that corresponds to visible geometry. Thus, your collider is suddenly testing for collisions with all visible geometry, an extremely expensive operation.

A better approach would be to just pick two different bits, for instance bit 1 and bit 2, for your two different states, instead of bit 1 and “every other bit”.

If you really wanted to use “every other bit” for some reason, you should at least limit it to the default CollisionNode bits (which will exclude the geom bit):

mask.invertInPlace()
mask &= CollisionNode.getDefaultCollideMask()

David

Aah, of course, thank you.

Hmm… Well, I want these attack objects to collide with all other creatures, at least, hence my wanting to include everything but the originating creature. I just didn’t mean to include all visible geometry along with that. >_<;

Alas, as you point out, I’m probably enabling collision with geometry, and, as a result of having incompletely moved from a projectile weapon system to a melee weapon system, the attack objects still include some geometry.

Hmm… Well, as long as I don’t create new creatures at run-time, I could simply calculate the total of the ids of all creatures save the owning one, keeping ids to powers of two (so that they result in single bits each) store that, and give it to the attack object.

If I wanted to add creatures at run-time, with no set upper bound, then your system would probably be better.