Physics + Collisions issue

I am trying to create a scene in Panda3D with a top-down camera and click-to-move functionality. It is working almost as intended, however there seem to be some issues with the character. Initially, it falls to the floor, however when I click anywhere on the map, it moves to the right direction but it also falls through the floor down to a certain point, which seems to be dependent on the initial position of the character. For example, when the initial Z of the character is set to 21.8, it falls to around -21.3875, when it is set to 31.8, it falls to around -31.3875, like so:

After clicking, the positions are as follows:

The PLAYER POS represents the position of the Actor (player.character), while the PHYS PLAYER POS represents the position of the physics node (player.actorNP)

Here is a video showing the issue:

Here is my source code:


Panda3D + RenderPipeline learning project

# Insert the pipeline path to the system path, this is required to be
# able to import the pipeline classes
pipeline_path = "RenderPipeline"

# Import system libraries
import os
import sys
import struct
from math import pi, sin, cos
import constants as const

# Add pipeline libraries to path
sys.path.insert(0, pipeline_path)

# Import Panda3D libraries - panda3d.core and direct packages
from panda3d.core import *
from panda3d.physics import *
from direct.gui.OnscreenText import OnscreenText
from import Actor
from direct.showbase.ShowBase import ShowBase
from direct.task import Task
from direct.interval.IntervalGlobal import Sequence
from direct.interval.ActorInterval import ActorInterval

# Import the RenderPipeline libraries
from rpcore import RenderPipeline

# Import the player classes
from characters import PlayerCharacter

    A list of configuration files from which we will read the game settings:
        * config.prc                - General game and display configurations, such as resolution, fullscreen, etc
        * @TODO: keybindings.prc    - A config file with key bindings
configfiles = [
    The main player character dictionary, containing the location of the model and a list of all its animations

    @TODO: move it to a separate character/player class
player = {
    'model'         : 'resources/Guard/Guard',
    'animations'    : {
        "run"       : "resources/Guard/run",
        "walk"      : "resources/Guard/walk",
        "dance"     : "resources/Guard/dance",
        "guitar"    : "resources/Guard/guitar",
        "idle"      : "resources/Guard/idle",
        "leg-sweep" : "resources/Guard/leg-sweep",
        "run-jump"  : "resources/Guard/run-jump"

class Application(ShowBase):
        Initialize and setup the application
    def __init__(self):

        # Load configuration file - Window size, title, fullscreen, etc
        # Define the key bindings
        # Init the RenderPipeline
        # Setup the environment
        # Setup the physics
        # Setup the player actor
        # Setup the camera
        # Setup the lighting
        # Setup mouse click handler

        taskMgr.add(self.updateCamera, "updateCameraTask")

        Initialize the RenderPipeline
    def initRenderPipeline(self):
        # Construct the render pipeline
        self.render_pipeline = RenderPipeline()
        self.render_pipeline.daytime_mgr.time = "07:00"

        # Use a special effect for rendering the scene, this is because the
        # roaming ralph model has no normals or valid materials
        self.render_pipeline.set_effect(render, "scene-effect.yaml", {}, sort=250)

        # Declare variables.,0,0,1))

        Initialize the environment
    def initEnvironment(self):
        # Load the scene
        self.environ = loader.loadModel("resources/world")

        # Remove wall nodes

        # Add collision mask

        Initialize the player, with its model and animations.
        Afterwards, loop the idle animation.
    def initPlayer(self):
        # Create the main character
        # Set his position a bit higher, so we can see him fall
        initialPosition = Vec3(-110.9, 29.4, 21.8)
        self.player     = PlayerCharacter(player, initialPosition)

        # Add the character physics and collisions
        self.playerPos  = self.player.getPos()

        We will detect the height of the terrain by creating a collision ray and casting it downward toward the terrain. One ray will start above ralph's head, and the other will start above the camera. A ray may hit the terrain, or it may hit a rock or a tree. If it hits the terrain, we can detect the height. If it hits anything else, we rule that the move is illegal.
    def initPhysics(self):
        self.cTrav          = CollisionTraverser()
        self.physicsHandler = PhysicsCollisionHandler()
        self.queueHandler   = CollisionHandlerQueue()

        # Enable particle system. It is needed in order to enable physics
        # Add gravity to the whole environment
        gravityFN       = ForceNode('world-forces')
        gravityFNP      = render.attachNewNode(gravityFN)
        gravityForce    = LinearVectorForce(0,0,-9.81) #gravity acceleration

        Initialize the camera
    def initCamera(self):
        # Disable the default camera controls
        # Set the initial camera position
            self.player.getActor().getX() + 10,
            self.player.getActor().getY() + 10,
            self.player.getActor().getZ() + 15
        # Look at the player
        # ???

        Create the initial, basic lighting
    def initLighting(self):
        # Create some lighting
        ambientLight = AmbientLight("ambientLight")
        ambientLight.setColor(Vec4(.3, .3, .3, 1))
        directionalLight = DirectionalLight("directionalLight")
        directionalLight.setDirection(Vec3(-5, -5, -5))
        directionalLight.setColor(Vec4(1, 1, 1, 1))
        directionalLight.setSpecularColor(Vec4(1, 1, 1, 1))

        Initialize the mouse click collision handler
    def initMouseHandler(self):
        self.clickNode   = CollisionNode('mouseRay')
        self.clickNP     = camera.attachNewNode(self.clickNode)
        self.clickRay    = CollisionRay()
        self.cTrav.addCollider(self.clickNP, self.queueHandler)

        Get the mouse click position
    def getMousePosition(self, mousepos):
        self.clickRay.setFromLens(base.camNode, mousepos.getX(),mousepos.getY())
        if self.queueHandler.getNumEntries() > 0:
            self.position = self.queueHandler.getEntry(0).getSurfacePoint(self.environ)
            return None

        Move the player to the clicked position
    def movePlayerToPosition(self):

        Define the keys to be used in this game.
        Later, we will add a dynamic config in a file, which we can change in-game
    def defineKeys(self):
        # Define an exit key
        self.accept("escape", sys.exit)
        # Define the left mouseclick as a moving key
        self.accept('mouse1', self.movePlayerToPosition)

        Accepts defined keys to move the player
        Also deals with grid checking and collision detection
        TODO: finish character movement
    def updateCamera(self, task):
        if (self.playerPos != self.player.getPos()):
            self.playerPos = self.player.getPos()

                self.player.getX() + 10,
                self.player.getY() + 10,
                self.player.getZ() + 15

        # Return task.cont so that the task runs continuously. Otherwise, it will just run once
        return task.cont

        Loads all the needed configuration files, which are stored in the
        global configfiles variable
    def loadConfig(self):
        for prc in configfiles:
            if (os.path.exists(prc)):

# Run the application


    This is an abstract class for a character defining some common methods for any actor(player), be it plyabla or non-playable.

# Import system libraries
import os
import sys
import struct

# Add root path (to use constants)
sys.path.insert(0, '../')

import constants as const
import uuid
from abc import ABC, abstractmethod

from panda3d.core import *
from panda3d.physics import *
from import Actor
from direct.showbase.ShowBase import ShowBase
from direct.interval.IntervalGlobal import Sequence
from direct.interval.ActorInterval import ActorInterval

class BaseCharacter(ABC):

        The init method adds all necessary data to the object
    def __init__(self, resources, initialPos):
        # Define the character resources, position and parent world
        self.characterResources     = resources
        self.characterInitialPos    = initialPos

        # Generate the player hash

        # Start rendering the character

        Generates a random hash which will be assigned to the player.
        This hash acts as a unique ID. We need this so the player nodes in the scene graph will not coincide
    def generatePlayerHash(self):
        self.hash = str(uuid.uuid4())

        Creates the player model(Actor) along with its animations, and starts looping the idle animations.
    def createCharacter(self):
        self.character = Actor(

        Adds character physics
    def addCharacterPhysics(self):
        # Add a physics node to the player
        self.actorPhysicsNode = NodePath("PlayerPhysicsNode_" + self.hash)
        self.actorNode = ActorNode("PlayerPhysics_" + self.hash)
        self.actorNP = self.actorPhysicsNode.attachNewNode(self.actorNode)

        Adds character collision handler
    def addCharacterCollisions(self):
        # Add collision nodes and rays
        self.actorColSphere = CollisionSphere(self.character.getPos(), 1)
        self.actorColNode   = CollisionNode('PlayerCollisionNode_' + self.hash)
        self.actorColNodeNP = self.actorNP.attachNewNode(self.actorColNode)

        # Add a Floor CollisionHandler. This ensures that the character will not fall through the floor
        base.physicsHandler.addCollider(self.actorColNodeNP, self.actorNP)
        base.cTrav.addCollider(self.actorColNodeNP, base.physicsHandler)

        Returns the Actor for this character
    def getActor(self):
        return self.character

        Tries to animate the character. If it cannot, prints a message and passes
    def animate(self, animation: str, loop: bool):
            if (loop):

        except Error:
            print("Character " + self.hash + " has no valid animation with the name: " + animation)

        Updates the character position according to the pshysics node position
    def update(self):
        # self.character.setPos(self.actorNP.getPos())

        Returns the player's current position
    def getPos(self):
        return self.character.getPos()

        Returns the player's current X position
    def getX(self):
        return self.character.getX()

        Returns the player's current Y position
    def getY(self):
        return self.character.getY()

        Returns the player's current Z position
    def getZ(self):
        return self.character.getZ()

        Prints the current actor position and phsyics node position
    def debug(self):
        print('PLAYER POS: ', self.character.getPos())
        print('PHYS PLAYER POS: ', self.actorNP.getPos())

        @TODO: implement this method here
        This will move the character to a specified position
    def moveTo(self, position):
        # Look at the position at which we're going
        # Quick hack - for some models, lookAt and headsUp rotates the wrong way. Rotate 180 degrees
        moveInterval = self.character.posInterval(3.0, position)
        moveSequence = Sequence(moveInterval)
        self.animate('run', True)


    This class defines a playable character.
    @TODO: Finish class. At the moment there is nothing to extend from the BaseCharacter class.

from . import BaseCharacter

class PlayerCharacter(BaseCharacter):


from panda3d.core import *

FLOOR_MASK  = BitMask32.bit(1)
MASK_OFF    = BitMask32.allOff()

I would really appreciate it if someone would be able to give me a few hints and tips as to what I am doing wrong, and how I could make it work properly.

Thank you in advance.

I’ve never used that way, but seems to me to be better to use a ray.

I’m not too familiar with the Panda physics system, but I think you’d want to move self.actorPhysicsNode instead of self.character. The character is just the model itself and has the ActorNode as its parent, so I think you’d want to move that instead in order to get proper physics.

I was trying to use a sphere so I would be able to detect collisions with other objects aswell, not only the floor. And from what I understand from the documentation, the sphere is the way to go in this case. I just tried with a CollisionRay and it doesn’t help. It falls through the floor endlessly (it doesn’t even stop as it does with the sphere)

I understand. However when I do this, the character model moves weird (not in the direction I click it, but this is another issue). The problem is that the physics node still falls through the floor. :frowning:

Do you have this line in the model file (egg)?

{ Polyset keep descend }

Yes, multiple times, actually. One in the terrain group, and one in each rock and each tree group.

You can use the RPG example: …

Thanks! I will have a look at it.

I dont understand. Care to explain a bit more please?

On another note, I have managed to make the collision handler working by setting the player’s CollisionSphere position from characterInitialPos to (0, 0, 0). However, there is another issue now: The actor’s model does not seem to move where it should be. The physics node falls and stops as it should, and setting the collision sphere to be visible shows that it is also where it should be, but the character model itself seems to go haywire, in what seems like pseudo-random locations.

I have managed to fix this issue aswell, by setting the character actor(model) position to (0, 0, 0). I should have figured it should be like this, since after reparenting it to the physics node means that its position is relative to the physics node, not to the environment itself.

The collisions still seem to be wonky when colliting into a rock, a tree or a top of a hill (the node seems to go randomly everywhere for a second or so, after which it goes where it should), but I’m sure I will manage to fix that aswell.