Collision with elements in 3d don't work

Hello, is there a way that the own camera in a 3d game doesn’t goes through file elements (glb files) like a street, terrain etc. I can walk through walls and everything in my 3d game and want to fix that.
I didn’t find a soulution for my problem on the internet so far.
Thanks for your help.

1 Like

Hi, what you are looking for is called collission detection. It’s described in this part of the manual Collision Detection — Panda3D Manual

By default no object will collide and be kept out of other objects. For that you need to define special collision shapes on your models (streets, terrain, walls, etc) as well as on your camera. For cameras, it’s common to use either rays, spheres or a combination of them dependent on what you try to achieve. In addition you also need to have a handler that controls what happens when a collision occurs. In your case it should reposition the camera to keep out of other models, something like the CollisionHandlerPusher does automatically.

1 Like

here is my code(sorry it’s a bit chaotical):
(where should I set the collision into the programm?)

from math import pi, sin, cos
from direct.showbase.ShowBase import ShowBase
from panda3d.core import loadPrcFile
from panda3d.core import DirectionalLight, AmbientLight
from panda3d.core import TransparencyAttrib
from panda3d.core import WindowProperties
from panda3d.core import CollisionTraverser, CollisionNode, CollisionBox, CollisionRay, CollisionHandlerQueue
from direct.gui.OnscreenImage import OnscreenImage
from direct.task import Task
from direct.actor.Actor import Actor
from direct.interval.IntervalGlobal import *
from panda3d.core import Point3
from pandac.PandaModules import *
from direct.gui.OnscreenText import OnscreenText
import sys

loadPrcFile (‘settings.prc’)

def degToRad(degrees):
return degrees * (pi / 180)

class MyGame (ShowBase):
def init(self):
ShowBase.init (self)

    self.selectedBlockType='grass'

    self.loadModels ()
    self.createCollisions()
    self.generateTerrain ()
    self.setupCamera ()
    self.setupSkybox ()
    self.captureMouse ()
    self.setupControls ()

    taskMgr.add (self.update, 'update')

def update(self, task):
    dt=globalClock.getDt ()



    playerMoveSpeed=2000

    x_movement=0
    y_movement=0
    z_movement=0

    if self.keyMap['forward']:
        x_movement-=dt * playerMoveSpeed * sin (degToRad (camera.getH ()))
        y_movement+=dt * playerMoveSpeed * cos (degToRad (camera.getH ()))
    if self.keyMap['backward']:
        x_movement+=dt * playerMoveSpeed * sin (degToRad (camera.getH ()))
        y_movement-=dt * playerMoveSpeed * cos (degToRad (camera.getH ()))
    if self.keyMap['left']:
        x_movement-=dt * playerMoveSpeed * cos (degToRad (camera.getH ()))
        y_movement-=dt * playerMoveSpeed * sin (degToRad (camera.getH ()))
    if self.keyMap['right']:
        x_movement+=dt * playerMoveSpeed * cos (degToRad (camera.getH ()))
        y_movement+=dt * playerMoveSpeed * sin (degToRad (camera.getH ()))
    if self.keyMap['up']:
        z_movement+=dt * playerMoveSpeed
    if self.keyMap['down']:
        z_movement-=dt * playerMoveSpeed

    camera.setPos (
        camera.getX () + x_movement,
        camera.getY () + y_movement,
        camera.getZ () + z_movement,
    )

    if self.cameraSwingActivated:
        md=self.win.getPointer (0)
        mouseX=md.getX ()
        mouseY=md.getY ()

        mouseChangeX=mouseX - self.lastMouseX
        mouseChangeY=mouseY - self.lastMouseY

        self.cameraSwingFactor=10

        currentH=self.camera.getH ()
        currentP=self.camera.getP ()

        self.camera.setHpr (
            currentH - mouseChangeX * dt * self.cameraSwingFactor,
            min (90, max (-90, currentP - mouseChangeY * dt * self.cameraSwingFactor)),
            0
        )

        self.lastMouseX=mouseX
        self.lastMouseY=mouseY

    return task.cont

def setupControls(self):
    self.keyMap={
        "forward": False,
        "backward": False,
        "left": False,
        "right": False,
        "up": False,
        "down": False,
    }

    self.accept ('escape', self.releaseMouse)
    self.accept ('mouse1', self.handleLeftClick)
    self.accept ('mouse3', self.handleRightClick)

    self.accept ('w', self.updateKeyMap, ['forward', True])
    self.accept ('w-up', self.updateKeyMap, ['forward', False])
    self.accept ('a', self.updateKeyMap, ['left', True])
    self.accept ('a-up', self.updateKeyMap, ['left', False])
    self.accept ('s', self.updateKeyMap, ['backward', True])
    self.accept ('s-up', self.updateKeyMap, ['backward', False])
    self.accept ('d', self.updateKeyMap, ['right', True])
    self.accept ('d-up', self.updateKeyMap, ['right', False])
    self.accept ('space', self.updateKeyMap, ['up', True])
    self.accept ('space-up', self.updateKeyMap, ['up', False])
    self.accept ('lshift', self.updateKeyMap, ['down', True])
    self.accept ('lshift-up', self.updateKeyMap, ['down', False])

    self.accept ('1', self.setSelectedBlockType, ['grass'])
    self.accept ('2', self.setSelectedBlockType, ['dirt'])
    self.accept ('3', self.setSelectedBlockType, ['sand'])
    self.accept ('4', self.setSelectedBlockType, ['stone'])



def setSelectedBlockType(self, type):
    self.selectedBlockType=type

def handleRightClick(self):
    self.captureMouse ()

    # arrow

    self.disableMouse ()
    # Load the environment model.
    self.scene=self.loader.loadModel ("shuriken.glb")
    # Reparent the model to render.
    self.scene.reparentTo (self.camera)
    # Apply scale and position transforms on the model.
    self.scene.setScale (1.5, 1.5, 1.5)
    self.scene.setPos (0, 0, 0)
    self.scene.setHpr (100, 100, 100)

    # Add the spinCameraTask procedure to the task manager.
    # self.taskMgr.add (self.spinCameraTask, "SpinCameraTask")

    # Load and transform the panda actor.
    self.pandaActor1=Actor ("shuriken.glb")

    self.pandaActor1.setScale (8, 8, 8)
    self.pandaActor1.setPos (0, 0, 0)
    self.pandaActor1.setHpr (90, 90, 90)
    self.pandaActor1.reparentTo (self.camera)
    # Loop its animation.
    self.pandaActor1.loop ("shuriken.glb")

    # Create the four lerp intervals needed for the panda to
    # walk back and forth.
    posInterval1=self.pandaActor1.posInterval (1,
                                               Point3 (0, 280, 0),
                                               startPos=Point3 (0, 0, 0))
    posInterval2=self.pandaActor1.posInterval (1,
                                               Point3 (0, 280, 0),
                                               startPos=Point3 (0, 0, 0))
    hprInterval1=self.pandaActor1.hprInterval (1,
                                               Point3 (100, 100, 10),
                                               startHpr=Point3 (0, 0, 0))
    hprInterval2=self.pandaActor1.hprInterval (1,
                                               Point3 (10, 10, 10),
                                               startHpr=Point3 (0, 0, 0))

    # Create and play the sequence that coordinates the intervals.
    self.pandaPace=Parallel (posInterval1, hprInterval1,
                             posInterval2, hprInterval2,
                             name="pandaPace")
    self.pandaPace=Parallel (posInterval1, hprInterval1,
                             posInterval2, hprInterval2,
                             name="pandaPace")
    self.pandaPace.loop ()

    self.pandaActor1=Actor ("shuriken.glb")

    self.pandaActor1.setScale (100, 100, 100)
    self.pandaActor1.setPos (-390, -150, -605)
    self.pandaActor1.setHpr (0, 90, 90)
    self.pandaActor1.reparentTo (self.camera)
    # Loop its animation.
    self.pandaActor1.loop ("shuriken.glb")

    # Create the four lerp intervals needed for the panda to
    # walk back and forth.
    posInterval1=self.pandaActor1.posInterval (10,
                                               Point3 (0, -90, 0),
                                               startPos=Point3 (0, 39, 0))
    posInterval2=self.pandaActor1.posInterval (10,
                                               Point3 (0, 90, 0),
                                               startPos=Point3 (0, -39, 0))
    hprInterval1=self.pandaActor1.hprInterval (3,
                                               Point3 (180, 100, 100),
                                               startHpr=Point3 (0, 0, 0))
    hprInterval2=self.pandaActor1.hprInterval (3,
                                               Point3 (100, 100, 100),
                                               startHpr=Point3 (180, 0, 100))

    # Create and play the sequence that coordinates the intervals.
    self.pandaPace=Parallel (posInterval1, hprInterval1,
                             posInterval2, hprInterval2,
                             name="pandaPace")
    self.pandaPace=Parallel (posInterval1, hprInterval1,
                             posInterval2, hprInterval2,
                             name="pandaPace")

def handleLeftClick(self):

    self.captureMouse ()

    # arrow
    self.disableMouse ()

    # Load the environment model.
    self.scene=self.loader.loadModel ("crossbowb.glb")
    # Reparent the model to render.
    self.scene.reparentTo (self.camera)
    # Apply scale and position transforms on the model.
    self.scene.setScale (140, 140, 140)
    self.scene.setPos (-20, 79, 12)
    self.scene.setHpr (-56, 500, -39)




    self.pandaActor1=Actor ("arrow.glb")

    self.pandaActor1.setScale (1.5, 1.5, 1.5)
    self.pandaActor1.setPos (-390, -250, -65)
    self.pandaActor1.setHpr (-37, -170, -270)
    self.pandaActor1.reparentTo (self.camera)
    # Loop its animation.
    self.pandaActor1.loop ("arrow.glb")

    # Create the four lerp intervals needed for the panda to
    # walk back and forth.
    posInterval1=self.pandaActor1.posInterval (1,
                                               Point3 (0, 90, 0),
                                               startPos=Point3 (0, 0, 0))
    posInterval2=self.pandaActor1.posInterval (1,
                                               Point3 (0, 90, 0),
                                               startPos=Point3 (0, 0, 0))
    hprInterval1=self.pandaActor1.hprInterval (3,
                                               Point3 (90, 0, 0),
                                               startHpr=Point3 (0, 0, 0))
    hprInterval2=self.pandaActor1.hprInterval (3,
                                               Point3 (0, 0, 0),
                                               startHpr=Point3 (90, 0, 0))

    # Create and play the sequence that coordinates the intervals.
    self.pandaPace=Parallel (posInterval1, hprInterval1,
                             posInterval2, hprInterval2,
                             name="pandaPace")
    self.pandaPace=Parallel (posInterval1, hprInterval1,
                             posInterval2, hprInterval2,
                             name="pandaPace")
    self.pandaPace.loop ()

    self.pandaActor1=Actor ("arrow.glb")

    self.pandaActor1.setScale (1.5, 1.5, 1.5)
    self.pandaActor1.setPos (-390, -150, -605)
    self.pandaActor1.setHpr (-117, -17, -27)
    self.pandaActor1.reparentTo (self.camera)
    # Loop its animation.
    self.pandaActor1.loop ("arrow.glb")

    # Create the four lerp intervals needed for the panda to
    # walk back and forth.
    posInterval1=self.pandaActor1.posInterval (15,
                                               Point3 (0, -90, 0),
                                               startPos=Point3 (0, 39, 0))
    posInterval2=self.pandaActor1.posInterval (15,
                                               Point3 (0, 90, 0),
                                               startPos=Point3 (0, -39, 0))
    hprInterval1=self.pandaActor1.hprInterval (3,
                                               Point3 (180, 0, 0),
                                               startHpr=Point3 (0, 0, 0))
    hprInterval2=self.pandaActor1.hprInterval (3,
                                               Point3 (0, 0, 0),
                                               startHpr=Point3 (180, 0, 0))

    # Create and play the sequence that coordinates the intervals.
    self.pandaPace=Parallel (posInterval1, hprInterval1,
                             posInterval2, hprInterval2,
                             name="pandaPace")
    self.pandaPace=Parallel (posInterval1, hprInterval1,
                             posInterval2, hprInterval2,
                             name="pandaPace")

def removeBlock(self):
    if self.rayQueue.getNumEntries () > 0:
        self.rayQueue.sortEntries ()
        rayHit=self.rayQueue.getEntry (0)

        hitNodePath=rayHit.getIntoNodePath ()
        hitObject=hitNodePath.getPythonTag ('owner')
        distanceFromPlayer=hitObject.getDistance (self.camera)

        if distanceFromPlayer < 12:
            hitNodePath.clearPythonTag ('owner')
            hitObject.removeNode ()

def placeBlock(self):
    if self.rayQueue.getNumEntries () > 0:
        self.rayQueue.sortEntries ()
        rayHit=self.rayQueue.getEntry (0)
        hitNodePath=rayHit.getIntoNodePath ()
        normal=rayHit.getSurfaceNormal (hitNodePath)
        hitObject=hitNodePath.getPythonTag ('owner')
        distanceFromPlayer=hitObject.getDistance (self.camera)

        if distanceFromPlayer < 14:
            hitBlockPos=hitObject.getPos ()
            newBlockPos=hitBlockPos + normal * 2
            self.createNewBlock (newBlockPos.x, newBlockPos.y, newBlockPos.z, self.selectedBlockType)

def updateKeyMap(self, key, value):
    self.keyMap[key]=value

def captureMouse(self):
    self.cameraSwingActivated=True

    md=self.win.getPointer (0)
    self.lastMouseX=md.getX ()
    self.lastMouseY=md.getY ()

    properties=WindowProperties ()
    properties.setCursorHidden (True)
    properties.setMouseMode (WindowProperties.M_relative)
    self.win.requestProperties (properties)

def releaseMouse(self):
    self.cameraSwingActivated=False

    properties=WindowProperties ()
    properties.setCursorHidden (False)
    properties.setMouseMode (WindowProperties.M_absolute)
    self.win.requestProperties (properties)

def setupCamera(self):
    self.disableMouse ()
    self.camera.setPos (0, 0, 3)
    self.camLens.setFov (90)
    self.camera.reparentTo(self.render)#

    crosshairs=OnscreenImage (
        image='crosshairs.png',
        pos=(0, 0, 0),
        scale=0.05,
    )
    crosshairs.setTransparency (TransparencyAttrib.MAlpha)

    self.cTrav=CollisionTraverser ()
    ray=CollisionRay ()
    ray.setFromLens (self.camNode, (0, 0))
    rayNode=CollisionNode ('line-of-sight')
    rayNode.addSolid (ray)
    rayNodePath=self.camera.attachNewNode (rayNode)
    self.rayQueue=CollisionHandlerQueue ()
    self.cTrav.addCollider (rayNodePath, self.rayQueue)
def createCollisions(self):

    ray = CollisionRay()
    ray.setOrigin(0,0,-.2)
    ray.setDirection(0,0,-1)
    cn= CollisionNode('playerRay')
    cn.addSolid(ray)
    cn.setFromCollideMask(BitMask32.bit(0))
    cn.setIntoCollideMask(BitMask32.allOff())

def setupSkybox(self):
    skybox=loader.loadModel ("skybox.egg")
    skybox.setScale (500)
    skybox.setBin ('background', 1)
    skybox.setDepthWrite (0)
    skybox.setLightOff ()
    skybox.reparentTo (render)

def generateTerrain(self):
    for z in range (1):
        for y in range (2):
            for x in range (2):
                self.createNewBlock (
                    x * 2 - 20,
                    y * 2 - 20,
                    -z * 2,
                    'stone' if z == 0 else 'stone'
                )

def createNewBlock(self, x, y, z, type):
    newBlockNode=render.attachNewNode ('new-block-placeholder')
    newBlockNode.setPos (x, y, z)

    if type == 'grass':
        self.grassBlock.instanceTo (newBlockNode)
    elif type == 'dirt':
        self.dirtBlock.instanceTo (newBlockNode)
    elif type == 'sand':
        self.sandBlock.instanceTo (newBlockNode)
    elif type == 'stone':
        self.stoneBlock.instanceTo (newBlockNode)
    elif type == 'sword':
        self.sword.instanceto (newBlockNode)

    blockSolid=CollisionBox ((-1, -1, -1), (1, 1, 1))
    blockNode=CollisionNode ('block-collision-node')
    blockNode.addSolid (blockSolid)
    collider=newBlockNode.attachNewNode (blockNode)
    collider.setPythonTag ('owner', newBlockNode)

def physics_update(Task):
    dt=globalClock.get_dt ()
    self.world.do_physics (dt)

    return Task.cont
def loadModels(self):

    self.grassBlock=loader.loadModel ('grass-block (2).glb')
    self.dirtBlock=loader.loadModel ('dirt-block.glb')
    self.stoneBlock=loader.loadModel ('stone-block.glb')
    self.sandBlock=loader.loadModel ('sand-block (1).glb')


    #garage

    self.rock_boulder_1=loader.loadModel ("parking_garage_free_download.glb")
    self.rock_boulder_1.setPos(0,0,3)
    self.rock_boulder_1.setScale(500,500,500)
    self.rock_boulder_1.setHpr(0,90,90)
    self.rock_boulder_1.reparentTo (render)
    self.rock_col=self.rock_boulder_1.find("**/rock_boulder_1_collide")

def spinCameraTask(self, task):
angleDegrees=task.time * 6.0
angleRadians=angleDegrees * (pi / 180.0)
self.camera.setPos (20 * sin (angleRadians), -20 * cos (angleRadians), 3)
self.camera.setHpr (angleDegrees, 0, 0)
return Task.cont

def setupLights(self):
    mainLight=DirectionalLight ('main light')
    mainLightNodePath=render.attachNewNode (mainLight)
    mainLightNodePath.setHpr (30, -60, 0)
    render.setLight (mainLightNodePath)

    ambientLight=AmbientLight ('ambient light')
    ambientLight.setColor ((0.3, 0.3, 0.3, 1))
    ambientLightNodePath=render.attachNewNode (ambientLight)
    render.setLight (ambientLightNodePath)

game=MyGame ()
game.run ()

It looks like you’re going for an first person style camera, so I’d recommend adding a collision sphere to it in your setupCamera function, similar to how you already add the ray.
Then, the most simple way for now is to add a CollisionHandlerPusher just like you did with the ray queue handler. Add your sphere to the new handler and you should be done.

An example of how to do that for the base camera is already available in the documentation for the CollisionHandlerPusher. So you should be able to copy-paste most of that, you just don’t need to pass the driveInterface since you’re using your own logic to controll the camera.

1 Like

#garage

    self.smiley=loader.loadModel ("parking_garage_free_download.glb")
    self.smiley.setPos(0,0,3)
    self.smiley.setScale(500,500,500)
    self.smiley.setHpr(0,90,90)
    self.smiley.reparentTo (self.render)

    self.fromObject = self.smiley.attachNewNode(CollisionNode('colNode'))
    self.fromObject.node().addSolid(CollisionSphere(0,0,0,1))
    self.pusher = CollisionHandlerPusher()
    self.pusher.addCollider((self.fromObject, self.smiley))

    self.traverser.addCollider(fromObject, pusher)
    self.smiley.setPos(x,y,z)

I’ve tried this code for the CollisionHandlerPusher but it doesn’t work.
That’s the message what the computer say’s it’s wrong. I don’t know what’s the meaning of this message in detail only that I should add 1 or 2 more arguments to it.

self.pusher.addCollider((self.fromObject, self.smiley))
TypeError: add_collider() takes 3 or 4 arguments (2 given)

Thanks for your help.

You surrounded the parameters of addCollider with braces making then a tuple instead of two individual values. Just remove one pair so it’s
self.pusher.addCollider(self.fromObject, self.smiley)
Then it should work.

Thank you. This part of the code is now working.
So I’ ll try the rest of my programm.