Test for collisions between two specific objects

Hi! I’m having a bit of a problem here. I have actors comprised of multiple objects parented to their joints. And in my scene, there are a few dozen of these actors in close proximity. I was wondering if there was a way to test for collision node intersections between two specific nodes, instead of traversing all collision nodes and testing them all against eachother. Most of the time, I don’t need to know if my actor’s forearm is colliding with their shoulder (I can tell if it’s still attached in the code). But sometimes I will need to know if one actor’s fist is connecting with another actor’s jaw. I was wondering what would be the best way to go about this. For reference, I’m using the collision system and not bullet.

Thanks in advance!

(P.S, if there’s a better way to go about this than specific node-to-node collision testing, I’d like to know!)

There are collision masks for this.

https://docs.panda3d.org/1.10/python/programming/collision-detection/collision-bitmasks

2 Likes

Exactly: Give each collider a bit-mask, and make sure that the mask applied to the first actor’s fist shares at least one bit with the mask applied to the other actor’s jaw, but none with the first actor’s forearm.

If you want a–*ahem*–“bit” more detail, the manual goes into the subject here:
https://docs.panda3d.org/1.10/python/programming/collision-detection/collision-bitmasks
And my “Beginner’s Tutorial” discusses it here:
(Starting after the first section of code on that page, I believe.)

1 Like

Would this temporarily render the first actor’s forearm invulnerable to attack from others? Also, do collision bitmasks check for collisions internally with nodes that have incompatible bitmasks, or does the system ignore them entirely? I’m thinking in terms of (preemptive I know I’m sorry) optimization for large-scale battles.

Thanks for the replies! I’m definitely going to read that beginner’s tutorial cover to cover.

Presuming that you set up your bit-masks to suit, no, I believe.

In short, the bit-masks control what may collide with what. In this case, the fist would be allowed to collide with the other character’s jaw, but not with its own forearm. Presumably the other character’s fist would still be allowed to collide with the first character’s forearm.

Honestly, that I don’t know, I’m afraid. I would imagine that it wouldn’t check for collisions, just compare bit-masks and move on. However, that’s just what I expect; I don’t have specific knowledge of what the engine does internally in this case.

I would suggest not worrying about that just yet–worry about it if it actually proves to be a problem. Indeed, I suspect that other matters will prove to be far worse performance issues than that!

For my part it’s my pleasure! I hope that I’ve helped! :slight_smile:

Excellent! I hope that it proves useful to you! :slight_smile:

1 Like

I just tested a group of 7 actors all standing inside eachother with all bitmasks turned off, and the performance was only slightly less than if they didn’t have collision geometry at all (like 1-2 frames). so I think you might be right :open_mouth:

I’m assuming from a quick read of the manual you and Serega posted that collisions are allowed to occur when two bits at the same position meet? so 0x00000003 and 0x00000001 would collide, but 0x00000002 and 0x00000001 wouldn’t?

Also, while we’re here, is there a maximum parenting depth for where collision nodes are in the hierarchy? Because my structure is Python Wrapper for PandaNode > Actor > Hand Joint > Knife Object > Knife Blade, with the blade being a collision object and for some reason it refuses to register a collision between the Knife Blade’s collision node and my enemy actor’s squishy insides. However, if I attach the Knife Blade collision node to the Knife Object “above” it, it works fine. I was wondering if this was a panda thing or if I messed something up somewhere.

Honestly this is saving my life right now! I’ve been struggling with this for a long time and this is exactly the solution I need :smiley:

Excellent! That is the sort of thing that I’d hope that the engine would do, indeed! :slight_smile:

Exactly, I believe.

I think that it’s a little clearer in binary than hex. Consider the following pairs of bitmasks:

0000000101
0000000100
0000000101
0000001000

The first pair should result in a collision: they share a bit in the same place. The second pair, however, shouldn’t result in a collision: they have no bits in common.

Not as far as I’m aware. I might be mistaken, however.

Still, the hierarchy that you listed isn’t all that deep, so I would be surprised if it were deep enough to hit such a limit, were there one.

Have you tried making your colliders visible (by calling “show” on their associated NodePaths)? Perhaps something is ending up misplaced?

Otherwise, there may be some more-subtle issue at play–an effect from the “Knife Blade” node that affects its children, but naturally not the immediate children of its parents, perhaps.

Excellent, then! I’m glad! :slight_smile:

2 Likes

I did! When my actor has the knife and the attack animation plays, the debug boxes indicate that the colliders are where they’re supposed to be, with the proper normal orientation. They intersect with the enemy and with all bitmasks turned on on both objects, they still do not register a collision. Which is weird, because if I attack my enemy with my fist, it registers a collision. All collision setups are controlled by the same helper method, which is a slight modification of the initCollisionSphere from the manual’s collision example

Hmm… Very strange. That does seem to suggest that there’s something more subtle going on here.

Another thought is to turn on collision visualisation, and see whether the objects are being allowed to collide at all. To do this, call “showCollisions(render)” on your collision-traverser.

Otherwise, are you doing anything with the “Knife Blade” node/NodePath that you aren’t doing with its parent? A scale, a property set, something else?

I figured it out! This is a bit complicated, but a long time ago I had set it up so that items could be picked up by collision rays via a CollisionHandlerQueue, while tests for NPCs attacking eachother were done by a CollisionHandlerEvent. The actual Knife Object has no geometry by which to mouse pick, so it worked with the Queue. While the components of the knife were the only geometry that could be seen to be picked. I moved everything over to the CollisionHandlerEvent, and now my weapons work but mouse picking broke XD

Apparently a collision node can’t be a collider for two types of handlers at once! Who knew? Hopefully this helps someone in the future.

1 Like

Aaah, yes, that would do it, I do think! Indeed, only one handler can, well, handle a given active collision-object, I’m afraid.

However, there’s nothing stopping you from removing an object from one handler and putting it into another, as far as I’m aware! So one potential solution might be to have the Queue-handler control the knife while it’s available to be picked up, and then to move it to the Event-handler once the character is wielding it.

2 Likes

Considering there would be more items in a scene to swap handlers between, would it be more optimal to try and move the mouse-clicking to the Event handler? :open_mouth:

idk if there would be a problem with that, or if there would be a problem with the accept system being used in this way. Does it accept combined events (PickUpButton+Collision, for example)? At the moment, my ray only casts once when the PickUpButton is pressed.

I’m not sure that an Event-handler would be all that good a choice for mouse-picking: it doesn’t provide a clear indication that a given event corresponds to the first hit, and will provide events for all hits produced by a ray, I believe. It would thus presumably call for some means of determining the first hit, and of ignoring all others–something that a Queue-handler already effectively provides.

Further, I would imagine that such handler-swapping wouldn’t be frequent, in game-loop terms: even if there are a lot of collectable items in a given scene, the player will only pick them up so quickly–and at a rate far less than one per frame.

So I wouldn’t worry about there being more items. I’d suggest just writing the code so that the swap-over is done automatically, and see how that goes!

I’m… not quite sure of what sort of intended behaviour you’re describing. By the sounds of it, you’re talking about the event that causes your ray to cast–something like a key-press, or a mouse-click, or a gamepad button-press, or some such. Why would you want your ray to cast on a collision occurring, too?

However, to answer your question, no, I don’t believe that the event-system accepts combined events of that sort. However, it doesn’t have to: there’s nothing stopping you from having more than one event that calls a given method. For example:

# Import-statements here

class MyGame(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)
        # More initialisation here

        self.accept("space", self.someMethod)
        self.accept("w", self.someMethod)

    def someMethod():
        print ("Kittens!")

myGame = Game()
myGame.run()

That should then run “someMethod” when either the space-bar or the w-key are pressed.