Bullet characters intersect with each other

Hello, I have the following code snippet:

 83         #PHYSICS
 84         shape = BulletCapsuleShape(.4,1.3,ZUp)
 85         self.controller = BulletCharacterControllerNode(shape,.4,self.getName() + '_Controller')
 86         self.charNP = render.attachNewNode(self.controller)
 87         base.world.attach(self.controller)
 88         self.actor.reparentTo(self.charNP) #of type actor.Actor.Actor
 89         self.controller.setJumpSpeed(JUMP_SPEED) #shouldn't be important

which, actually, works quite well except for one thing: I can’t manage to have two Bullet characters to collide(they intersect each other). I already tried to set charNP’s collide mask but it didn’t worked.
I have also tried to attach a rigid body to charNP(or to actor) but I didn’t managed to have the rigid body to follow the character(it stands stucked in the center of the scene…)

Any idea? :slight_smile:

Perhaps you could try setting the collide mask to allOn(), and use attach_character().

          #PHYSICS
          shape = BulletCapsuleShape(.4,1.3,ZUp)
          self.controller = BulletCharacterControllerNode(shape,.4,self.getName() + '_Controller') 
          self.charNP = render.attach_new_node(self.controller)
          self.charNP.set_collide_mask(BitMask32.allOn())
          base.world.attach_character(self.charNP.node())
          self.actor.reparent_to(self.charNP) #of type actor.Actor.Actor
          self.controller.set_jump_speed(JUMP_SPEED) #shouldn't be important

Hi, thank for your answer but no, it didn’t work :-/
I don’t know why. The fact is that I’m quite new to bullet and I haven’t found examples with 2 or more BulletCharacterControllerNodes which are suppose to push each others…so I’m quite clueless on how to proceed…

BTW, here is the working example on which I am experimenting. Anybody should be able to run it out of the box. Hope it will help to diagnose the issue:

from functools import partial
from direct.showbase.ShowBase import ShowBase
from direct.actor.Actor import Actor
from panda3d.bullet import BulletCapsuleShape, BulletCharacterControllerNode, ZUp, BulletWorld, BulletDebugNode, BulletPlaneShape, BulletRigidBodyNode 
from panda3d.core import Vec3

JUMP_SPEED = 5


class Test(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)
        
        #WORLD SETUP
        base.world = BulletWorld()
        base.world.setGravity(Vec3(0, 0, -9.81))

        
        #DEBUG STUFF
        debugNode = BulletDebugNode('Debug')
        debugNode.showWireframe(True)
        debugNode.showConstraints(True)
        debugNode.showBoundingBoxes(True)
        debugNode.showNormals(True)
        debugNP = render.attachNewNode(debugNode)
        debugNP.show()
        base.world.setDebugNode(debugNP.node())

        #GROUND
        sceneCollider = BulletPlaneShape(Vec3(0,0,1),0)
        node = BulletRigidBodyNode()
        node.addShape(sceneCollider)
        np = render.attachNewNode(node)
        np.setPos(0, 0, 0)
        base.world.attachRigidBody(node)



        #PHYSICS
        #I think I should do something here but what? :-/
        #PANDA 1(CONTROLLED BY ME)
        self.actor = Actor("models/panda-model",
                                {"walk": "models/panda-walk4"}) 
        shape = BulletCapsuleShape(500,1,ZUp) 
        self.controller = BulletCharacterControllerNode(shape,.4,'Controller1')
        self.charNP = render.attachNewNode(self.controller)
        base.world.attach(self.controller)
        self.actor.reparentTo(self.charNP)
        self.controller.setJumpSpeed(JUMP_SPEED)
        self.charNP.reparentTo(render)

        #PANDA 2(NPC)
        self.actor2 = Actor("models/panda-model",
                                {"walk": "models/panda-walk4"}) 
        shape = BulletCapsuleShape(500,1,ZUp) 
        self.controller2 = BulletCharacterControllerNode(shape,.4,'Controller2')
        self.charNP2 = render.attachNewNode(self.controller2)
        base.world.attach(self.controller2)
        self.actor2.reparentTo(self.charNP2)
        self.controller2.setJumpSpeed(JUMP_SPEED)
        self.charNP2.reparentTo(render)
        self.charNP2.setPos(0,-1500,0)
        self.charNP2.setH(180)


        #INPUT
        self.accept('a',partial(self.move,0))
        self.accept('d',partial(self.move,1))
        self.accept('a-up',partial(self.move,2))
        self.accept('d-up',partial(self.move,2))

        
        base.taskMgr.add(self.updatePhysicalWorld,'updatePhysicalWorld')
        base.taskMgr.add(self.cameraTask,'cameraTask')


    def move(self,direction):
        if direction == 0:
            self.controller.setLinearMovement(Vec3(0,-200,0),True)
        elif direction == 1:
            self.controller.setLinearMovement(Vec3(0,200,0),True)
        else:
            self.controller.setLinearMovement(Vec3(0,0,0),True)
 

    def updatePhysicalWorld(self,task):
        dt = globalClock.getDt()
        self.world.doPhysics(dt)
        return task.cont

    def cameraTask(self,task):
        base.cam.setPos(self.actor.getPos() + Vec3(6500,0,500))
        base.cam.lookAt(self.actor)
        return task.cont

test = Test()
test.run()

I’m not sure if using this is a good idea. This is a very clumsy class with no guarantee of expected behavior.

You mean the controller or my test class? :slight_smile:

I mean the controller, it is not fully implemented on the side of the bullet, respectively, on the side of the panda, you should not expect miracles from its work.

I see, thank you for your answer :slight_smile:

I don’t know that Bullet’s kinematic character controller actually supports collisions between characters. A kinematic character controller (including Bullet’s) usually use ray casts and other such checks to determine how to respond to the world around them rather than regular physics simulation (this would be a dynamic character controller).

You could try adding another collision shape (parented to either the controller or the actor) to see if the character controller will detect and avoid that. However, this could also result in issue with the character colliding with itself.

FWIW, that’s basically what my Arena FPS Sample Program does, and IE the player character can be pushed out of the way by the NPC character controller.

        # create special hit areas
        # use Task section for npc collision movement logic
        # special head node size
        special_shape = BulletBoxShape(Vec3(0.1, 0.1, 0.1))
        # ghost npc node
        body = BulletGhostNode('special_node_A')
        special_node = self.render.attach_new_node(body)
        special_node.node().add_shape(special_shape, TransformState.makePos(Point3(0, 0, 0.4)))
        # special_node.node().set_mass(0)
        # special_node.node().set_friction(0.5)
        special_node.set_collide_mask(BitMask32(0x0f))
        # turn on Continuous Collision Detection
        special_node.node().set_deactivation_enabled(False)
        special_node.node().set_ccd_motion_threshold(0.000000007)
        special_node.node().set_ccd_swept_sphere_radius(0.30)
        self.world.attach_ghost(special_node.node())
        
        # dynamic collision
        special_shape = BulletBoxShape(Vec3(0.3, 0.15, 0.6))
        # rigidbody npc node
        body = BulletRigidBodyNode('d_coll_A')
        d_coll = self.render.attach_new_node(body)
        d_coll.node().add_shape(special_shape, TransformState.makePos(Point3(0, 0, 0.7)))
        # d_coll.node().set_mass(0)
        d_coll.node().set_friction(0.5)
        d_coll.set_collide_mask(BitMask32.allOn())
        # turn on Continuous Collision Detection
        d_coll.node().set_deactivation_enabled(False)
        d_coll.node().set_ccd_motion_threshold(0.000000007)
        d_coll.node().set_ccd_swept_sphere_radius(0.30)
        self.world.attach_rigid_body(d_coll.node())

…and then instead of reparenting the NPC dynamic collision rig to the NPC character controller, it is just position-updated in a Task to match the current position of the NPC.

If I recall correctly, this avoids self-collision by using a very short character controller capsule shape and a TransformState to place the dynamic collision rigid body above that.

Could you not also avoid self-collision by use of masking? (Either standard or group; I’m not sure offhand of which would work better.)