I’m using a triangle mesh as the ground for a grid-based level, which is using two triangles per cell. I am also using a capsule as the player collision shape. When I move the player over the edges of the mesh, it catches a little bit (see example).
I think this is called Internal Edge Collision:
But I can’t make heads or tails of the answers listed above. Does anyone have any ideas for improving the example to move better?
from direct.showbase.InputStateGlobal import inputState
from direct.showbase.ShowBase import ShowBase
from panda3d.core import NodePath
from panda3d.core import Vec3
from panda3d.bullet import (
BulletCapsuleShape,
BulletCharacterControllerNode,
BulletDebugNode,
BulletRigidBodyNode,
BulletTriangleMesh,
BulletTriangleMeshShape,
BulletWorld,
ZUp,
)
LEVEL_VERTICES = [(-0.5, 0.5, 0.0), (-0.5, -0.5, 0.0), (0.5, 0.5, 0.0), (0.5, -0.5, 0.0), (-0.5, 1.5, 0.0), (0.5, 1.5, 0.0), (-0.5, 2.5, 0.0), (0.5, 2.5, 0.0), (-0.5, 3.5, 0.0), (0.5, 3.5, 0.0), (-0.5, 4.5, 0.0), (0.5, 4.5, 0.0), (-0.5, 5.5, 0.0), (0.5, 5.5, 0.0), (-0.5, 6.5, 0.0), (0.5, 6.5, 0.0), (-0.5, 7.5, 0.0), (0.5, 7.5, 0.0), (-0.5, 8.5, 0.0), (0.5, 8.5, 0.0), (-0.5, 9.5, 0.0), (0.5, 9.5, 0.0), (1.5, 0.5, 0.0), (1.5, -0.5, 0.0), (1.5, 1.5, 0.0), (1.5, 2.5, 0.0), (1.5, 3.5, 0.0), (1.5, 4.5, 0.0), (1.5, 5.5, 0.0), (1.5, 6.5, 0.0), (1.5, 7.5, 0.0), (1.5, 8.5, 0.0), (1.5, 9.5, 0.0), (2.5, 0.5, 0.0), (2.5, -0.5, 0.0), (2.5, 1.5, 0.0), (2.5, 2.5, 0.0), (2.5, 3.5, 0.0), (2.5, 4.5, 0.0), (2.5, 5.5, 0.0), (2.5, 6.5, 0.0), (2.5, 7.5, 0.0), (2.5, 8.5, 0.0), (2.5, 9.5, 0.0), (3.5, 0.5, 0.0), (3.5, -0.5, 0.0), (3.5, 1.5, 0.0), (3.5, 2.5, 0.0), (3.5, 3.5, 0.0), (3.5, 4.5, 0.0), (3.5, 5.5, 0.0), (3.5, 6.5, 0.0), (3.5, 7.5, 0.0), (3.5, 8.5, 0.0), (3.5, 9.5, 0.0), (4.5, 0.5, 0.0), (4.5, -0.5, 0.0), (4.5, 1.5, 0.0), (4.5, 2.5, 0.0), (4.5, 3.5, 0.0), (4.5, 4.5, 0.0), (4.5, 5.5, 0.0), (4.5, 6.5, 0.0), (4.5, 7.5, 0.0), (4.5, 8.5, 0.0), (4.5, 9.5, 0.0), (5.5, 0.5, 0.0), (5.5, -0.5, 0.0), (5.5, 1.5, 0.0), (5.5, 2.5, 0.0), (5.5, 3.5, 0.0), (5.5, 4.5, 0.0), (5.5, 5.5, 0.0), (5.5, 6.5, 0.0), (5.5, 7.5, 0.0), (5.5, 8.5, 0.0), (5.5, 9.5, 0.0), (6.5, 0.5, 0.0), (6.5, -0.5, 0.0), (6.5, 1.5, 0.0), (6.5, 2.5, 0.0), (6.5, 3.5, 0.0), (6.5, 4.5, 0.0), (6.5, 5.5, 0.0), (6.5, 6.5, 0.0), (6.5, 7.5, 0.0), (6.5, 8.5, 0.0), (6.5, 9.5, 0.0), (7.5, 0.5, 0.0), (7.5, -0.5, 0.0), (7.5, 1.5, 0.0), (7.5, 2.5, 0.0), (7.5, 3.5, 0.0), (7.5, 4.5, 0.0), (7.5, 5.5, 0.0), (7.5, 6.5, 0.0), (7.5, 7.5, 0.0), (7.5, 8.5, 0.0), (7.5, 9.5, 0.0), (8.5, 0.5, 0.0), (8.5, -0.5, 0.0), (8.5, 1.5, 0.0), (8.5, 2.5, 0.0), (8.5, 3.5, 0.0), (8.5, 4.5, 0.0), (8.5, 5.5, 0.0), (8.5, 6.5, 0.0), (8.5, 7.5, 0.0), (8.5, 8.5, 0.0), (8.5, 9.5, 0.0), (9.5, 0.5, 0.0), (9.5, -0.5, 0.0), (9.5, 1.5, 0.0), (9.5, 2.5, 0.0), (9.5, 3.5, 0.0), (9.5, 4.5, 0.0), (9.5, 5.5, 0.0), (9.5, 6.5, 0.0), (9.5, 7.5, 0.0), (9.5, 8.5, 0.0), (9.5, 9.5, 0.0)]
LEVEL_TRIANGLES = [(0, 1, 2), (2, 1, 3), (4, 0, 5), (5, 0, 2), (6, 4, 7), (7, 4, 5), (8, 6, 9), (9, 6, 7), (10, 8, 11), (11, 8, 9), (12, 10, 13), (13, 10, 11), (14, 12, 15), (15, 12, 13), (16, 14, 17), (17, 14, 15), (18, 16, 19), (19, 16, 17), (20, 18, 21), (21, 18, 19), (2, 3, 22), (22, 3, 23), (5, 2, 24), (24, 2, 22), (7, 5, 25), (25, 5, 24), (9, 7, 26), (26, 7, 25), (11, 9, 27), (27, 9, 26), (13, 11, 28), (28, 11, 27), (15, 13, 29), (29, 13, 28), (17, 15, 30), (30, 15, 29), (19, 17, 31), (31, 17, 30), (21, 19, 32), (32, 19, 31), (22, 23, 33), (33, 23, 34), (24, 22, 35), (35, 22, 33), (25, 24, 36), (36, 24, 35), (26, 25, 37), (37, 25, 36), (27, 26, 38), (38, 26, 37), (28, 27, 39), (39, 27, 38), (29, 28, 40), (40, 28, 39), (30, 29, 41), (41, 29, 40), (31, 30, 42), (42, 30, 41), (32, 31, 43), (43, 31, 42), (33, 34, 44), (44, 34, 45), (35, 33, 46), (46, 33, 44), (36, 35, 47), (47, 35, 46), (37, 36, 48), (48, 36, 47), (38, 37, 49), (49, 37, 48), (39, 38, 50), (50, 38, 49), (40, 39, 51), (51, 39, 50), (41, 40, 52), (52, 40, 51), (42, 41, 53), (53, 41, 52), (43, 42, 54), (54, 42, 53), (44, 45, 55), (55, 45, 56), (46, 44, 57), (57, 44, 55), (47, 46, 58), (58, 46, 57), (48, 47, 59), (59, 47, 58), (49, 48, 60), (60, 48, 59), (50, 49, 61), (61, 49, 60), (51, 50, 62), (62, 50, 61), (52, 51, 63), (63, 51, 62), (53, 52, 64), (64, 52, 63), (54, 53, 65), (65, 53, 64), (55, 56, 66), (66, 56, 67), (57, 55, 68), (68, 55, 66), (58, 57, 69), (69, 57, 68), (59, 58, 70), (70, 58, 69), (60, 59, 71), (71, 59, 70), (61, 60, 72), (72, 60, 71), (62, 61, 73), (73, 61, 72), (63, 62, 74), (74, 62, 73), (64, 63, 75), (75, 63, 74), (65, 64, 76), (76, 64, 75), (66, 67, 77), (77, 67, 78), (68, 66, 79), (79, 66, 77), (69, 68, 80), (80, 68, 79), (70, 69, 81), (81, 69, 80), (71, 70, 82), (82, 70, 81), (72, 71, 83), (83, 71, 82), (73, 72, 84), (84, 72, 83), (74, 73, 85), (85, 73, 84), (75, 74, 86), (86, 74, 85), (76, 75, 87), (87, 75, 86), (77, 78, 88), (88, 78, 89), (79, 77, 90), (90, 77, 88), (80, 79, 91), (91, 79, 90), (81, 80, 92), (92, 80, 91), (82, 81, 93), (93, 81, 92), (83, 82, 94), (94, 82, 93), (84, 83, 95), (95, 83, 94), (85, 84, 96), (96, 84, 95), (86, 85, 97), (97, 85, 96), (87, 86, 98), (98, 86, 97), (88, 89, 99), (99, 89, 100), (90, 88, 101), (101, 88, 99), (91, 90, 102), (102, 90, 101), (92, 91, 103), (103, 91, 102), (93, 92, 104), (104, 92, 103), (94, 93, 105), (105, 93, 104), (95, 94, 106), (106, 94, 105), (96, 95, 107), (107, 95, 106), (97, 96, 108), (108, 96, 107), (98, 97, 109), (109, 97, 108), (99, 100, 110), (110, 100, 111), (101, 99, 112), (112, 99, 110), (102, 101, 113), (113, 101, 112), (103, 102, 114), (114, 102, 113), (104, 103, 115), (115, 103, 114), (105, 104, 116), (116, 104, 115), (106, 105, 117), (117, 105, 116), (107, 106, 118), (118, 106, 117), (108, 107, 119), (119, 107, 118), (109, 108, 120), (120, 108, 119)]
class MyApp(ShowBase):
def __init__(self):
super().__init__()
self.disableMouse()
self.accept("escape", exit)
self.moveTask = self.taskMgr.add(self.moveTask, 'moveTask')
self.world = BulletWorld()
self.world.setGravity(Vec3(0, 0, -9.81))
self.physics_root = BulletRigidBodyNode('level_physics')
# Set up level.
self.mesh = BulletTriangleMesh()
self.mesh.setWeldingDistance(0.1)
for i, j, k in LEVEL_TRIANGLES:
self.mesh.addTriangle(
LEVEL_VERTICES[i],
LEVEL_VERTICES[j],
LEVEL_VERTICES[k],
)
ground_shape = BulletTriangleMeshShape(self.mesh, dynamic=False)
self.physics_root.addShape(ground_shape)
self.world.attachRigidBody(self.physics_root)
self.camera.setPosHpr(4, -10, 10, 0, -35, 0) # Set the camera
self.player_capsule = BulletCapsuleShape(0.5, 1.0/2.0, ZUp)
self.playerNode = BulletCharacterControllerNode(self.player_capsule, 0.4, 'Player')
self.physics_np = NodePath(self.playerNode)
self.physics_np.reparentTo(self.render)
self.world.attach(self.physics_np.node())
self.debugactive = True
debugNode = BulletDebugNode("Debug")
debugNode.showWireframe(True)
debugNode.showConstraints(True)
debugNode.showBoundingBoxes(True)
debugNode.showNormals(True)
debugNP = self.render.attachNewNode(debugNode)
debugNP.show()
self.world.setDebugNode(debugNode)
self.taskMgr.add(self.updatePhysicsBullet, 'task_physicsUpdater_Bullet', priority=-20)
inputState.watchWithModifiers('up', 'w')
inputState.watchWithModifiers('down', 's')
inputState.watchWithModifiers('left', 'a')
inputState.watchWithModifiers('right', 'd')
def updatePhysicsBullet(self, task):
dt = globalClock.getDt()
self.world.doPhysics(dt, 10, 1.0/180.0)
return task.cont
def moveTask(self, task):
movement = Vec3()
if inputState.isSet('up'):
movement += Vec3(0, 1, 0)
if inputState.isSet('down'):
movement += Vec3(0, -1, 0)
if inputState.isSet('left'):
movement += Vec3(-1, 0, 0)
if inputState.isSet('right'):
movement += Vec3(1, 0, 0)
self.playerNode.setLinearMovement(movement, True)
return task.cont
def main():
app = MyApp()
app.run()
if __name__ == '__main__':
main()