Bitmask collision detected but pusher isn't stopping object from going through

Hi all,

I am currently working on a project, I have an object that I have exported and added a collidemask so gravity doesn’t pull the character through the terrain/object, when I turn on show_collisions(render) it shows that the color of the object does indeed change (I do assume that it detects collisions because of that) however I still fall through. I have implemented the same thing for the terrain and it works, I am wondering why it doesn’t for this.


(the brown object is basically the object I am referring to).

The code:

player.py

#collision stuff
        self.pusher = CollisionHandlerPusher()
        self.colliderNode = CollisionNode("player")
        self.colliderNode.addSolid(CollisionSphere(0, 0, 0, 1))
        self.colliderNode.setFromCollideMask(CollideMask.bit(0))
        self.colliderNode.setIntoCollideMask(BitMask32.allOff())
        collider = self.playerHolder.attachNewNode(self.colliderNode)
        self.pusher.addCollider(collider,self.playerHolder)
        self.cTrav = CollisionTraverser()
        self.cTrav.addCollider(collider,self.pusher)
        self.cTrav.show_collisions(render)

buildings.py

from panda3d.core import CollideMask,BitMask32
class dock():
    def __init__(self,loader,location):
        self.loader = loader
        self.port = self.loader.loadModel("assets/environment/buildings/port.bam")
        self.port.setPos(location)
        self.port.setH(90)
        self.port.setScale(4)
        self.port.setCollideMask(BitMask32.bit(0))
        self.port.reparentTo(render)

If you need to see more of the code please let me know, thanks in advance :slight_smile:

Hmm… This may not be relevant, but in general I believe that it’s unwise to apply a scaling factor to a collision-object–it can cause problems. What happens if you remove the scaling factor? (Other than the area becoming too small, of course.)

[edit]
To clarify, I’m referring to the scaling factor applied to “self.port”, here:

1 Like

Can you show where playerHolder is defined? Is it a parent of the player model?

1 Like

I have removed the scalefactor line, still no success. I have scaled the GeoMip terrain as well but I do not get this problem.

Yes, playerholder is the parent of the player model, the collision and a bunch of other nodes (mainly for the camera control, so if I move the camera it doesn’t rotate the player model).

Here is the full code for player.py:

import sys
from panda3d.core import Vec3,PointLight, WindowProperties, CollisionTraverser, CollisionHandlerPusher, CollisionSphere, CollisionNode, CollideMask, BitMask32
import math
from playerGUI import GUI

class Player():
    def __init__(self,camera,accept,render,loader,maxJPHeight):
        #initial variables and sounds
        self.jetPack_energy = 100
        self.maximumHeight = maxJPHeight
        self.jetPack_AUDIO = loader.loadSfx("assets/base/sounds/jetpack2.wav")
        self.jetPack_AUDIO.setLoop(True)

        #initiate GUI
        self.HUD = GUI()
        self.playerHolder = render.attachNewNode('player')

        self.character = loader.loadModel('assets/base/models/player.bam')
        self.toggleFPCam = False
        self.character.setPos(0,0,0)
        self.character.reparentTo(self.playerHolder)
        self.playerBase = self.playerHolder.attachNewNode('camParent')
        self.thirdPersonNode = self.playerBase.attachNewNode('thirdPersonCam')
        camera.reparentTo(self.thirdPersonNode)
        self.mouseSeconds = []

        #collision stuff
        self.pusher = CollisionHandlerPusher()
        self.colliderNode = CollisionNode("player")
        self.colliderNode.addSolid(CollisionSphere(0, 0, 0, 1))
        self.colliderNode.setFromCollideMask(CollideMask.bit(0))
        self.colliderNode.setIntoCollideMask(BitMask32.allOff())
        collider = self.playerHolder.attachNewNode(self.colliderNode)
        self.pusher.addCollider(collider,self.playerHolder)
        self.cTrav = CollisionTraverser()
        self.cTrav.addCollider(collider,self.pusher)
        self.cTrav.show_collisions(render)
        self.setupLighting() # light
        #initial position
        self.playerHolder.setPos(64529.7, 25629.3, 2000)
        self.keyMap = {
            "left": False,
            "right": False,
            "forward": False,
            "backwards": False,
            "change_camera": False,
            "leftClick": False,
            "space": False,
            "p":False
        }

        accept("escape", sys.exit)
        accept("w", self.updateKey, ["forward", True])  #
        accept("w-up", self.updateKey, ["forward", False])

        accept("a", self.updateKey, ["left", True])
        accept("a-up", self.updateKey, ["left", False])

        accept("s", self.updateKey, ["backwards", True])
        accept("s-up", self.updateKey, ["backwards", False])

        accept("d", self.updateKey, ["right", True])
        accept("d-up", self.updateKey, ["right", False])

        accept("c",self.updateKey,["change_camera", True])

        accept("mouse1",self.updateKey,["leftClick",True])
        accept("mouse1-up",self.updateKey,["leftClick",False])

        accept("p", self.updateKey, ["p", True])
        accept("p-up", self.updateKey, ["p", False])

        accept("space",self.updateKey,["space",True])
        accept("space-up",self.updateKey,["space",False])

    def updateKey(self,key,value):
        self.keyMap[key] = value
        if key == "change_camera":
            self.changeCamera()

    def changeCamera(self):
        if self.toggleFPCam == False:
            self.toggleFPCam = True
        else:
            self.toggleFPCam = False

    def recenterMouse(self):
        base.win.movePointer(0, int(base.win.getProperties().getXSize() / 2), int(base.win.getProperties().getYSize() / 2))

    def setupLighting(self):
        plight = PointLight('plight')
        plight.setColor((0.2, 0.2, 0.2, 1))
        plnp = self.playerHolder.attachNewNode(plight)
        render.setLight(plnp)

    def playerUpdate(self,task):
        deltaTime = globalClock.getDt()
        #mouse controls
        if self.toggleFPCam: #first person camera controls
            camera.setPos(self.character.getPos())  # 0,-50,-10
            #camera.setZ(camera, 20)
            props = WindowProperties()
            props.setCursorHidden(True)
            props.setMouseMode(WindowProperties.M_relative)
            base.win.requestProperties(props)
            self.character.hide()

            if (base.mouseWatcherNode.hasMouse() == True):
                mouseposition = base.mouseWatcherNode.getMouse()
                camera.setP(mouseposition.getY() * 30)
                #self.playerBase.setH(self.playerBase.getH())
                #camera.setP(mouseposition.getY() * 30)
                self.playerBase.setH(mouseposition.getX() * -50)
                if (mouseposition.getX() < 0.1 and mouseposition.getX() > -0.1):
                    self.playerBase.setH(self.playerBase.getH())
                else:
                    #self.playerBase.setH(self.playerBase.getH() + mouseposition.getX() * -1)
                    pass

        else: #takes out of first person perspective if toggleFPS is turned off.
            props = WindowProperties()
            props.setCursorHidden(False)
            props.setMouseMode(WindowProperties.M_absolute)
            base.win.requestProperties(props)
            self.character.show()
            camera.setPos(0, -50, -4)  # 0,-50,-10
            camera.lookAt(self.character)

        self.walkConstant = 900
        self.rotateConstant = 100
        #Keyboard controls
        if self.keyMap["forward"]:
            self.playerHolder.setY(self.playerBase, (self.walkConstant*deltaTime))
            self.character.setP(self.character.getP() + (-self.rotateConstant*deltaTime*(math.cos(math.radians(self.playerBase.getH())))))
            self.character.setR(self.character.getR() - (self.rotateConstant*deltaTime*(-math.sin(math.radians(self.playerBase.getH())))))

        if self.keyMap["right"]:
            self.playerHolder.setX(self.playerBase, (self.walkConstant*deltaTime))
        if self.keyMap["p"]:
            print(self.playerHolder.getPos())
        if self.keyMap["left"]:
            self.playerHolder.setX(self.playerBase, (-self.walkConstant*deltaTime))

        if self.keyMap["backwards"]:
            self.playerHolder.setY(self.playerBase, (-self.walkConstant * deltaTime))
            self.character.setP(self.character,(self.rotateConstant*deltaTime))

        if self.keyMap["space"] and self.jetPack_energy>0:
            jetpack = 0.00001*(((self.playerHolder.getZ())-self.maximumHeight)**2)+98.1
            self.playerHolder.setZ(self.playerBase, jetpack)
            self.jetPack_energy -= 8*deltaTime
            if self.jetPack_AUDIO.status() != self.jetPack_AUDIO.PLAYING:
                self.jetPack_AUDIO.play()
        else:
            if self.jetPack_energy < 100:
                self.jetPack_energy += 10*deltaTime
            if self.jetPack_energy > 100:
                self.jetPack_energy = 100
            self.jetPack_AUDIO.stop()
        self.HUD.jetpackStatus.text = str(int(self.jetPack_energy))+"%"

        #third person camera control
        if (self.keyMap["leftClick"] == True) and (self.toggleFPCam == False): #third person camera controls
            if (base.mouseWatcherNode.hasMouse() == True):
                mouseposition = base.mouseWatcherNode.getMouse()
                self.mouseSeconds.append(mouseposition)
            if len(self.mouseSeconds) == 2:
                lookConstant = 1
                upperconstant = 40
                lowerconstant = 1
                moveX = ((self.mouseSeconds[1].getX())*upperconstant - (self.mouseSeconds[0].getX())*lowerconstant)*lookConstant
                moveY = ((self.mouseSeconds[1].getY())*upperconstant - (self.mouseSeconds[0].getY())*lowerconstant)*lookConstant
                if (moveX > 1 or moveX < -1):
                    self.playerBase.setH(self.playerBase.getH() - moveX)
                if (moveY > 1 or moveY < -1):
                    self.thirdPersonNode.setP(self.thirdPersonNode.getY()+moveY)
                self.mouseSeconds = []
        self.cTrav.traverse(render)
        self.playerHolder.setPos(self.playerHolder, Vec3(0,0,-98.1)) # Gravity
        return task.cont

Hmm… I wonder.

You’re using some pretty big numbers, including the application of a gravity-born acceleration of 98, and that without incorporating the delta-time.

The environment that displays the problem appears to use polygonal collision, and the environment that works is a height-field. So, perhaps the problem is that your object is descending too fast, fast enough that it’s “skipping over” the infinitely-thin polygonal collision-shape, where the heightfield possibly accounts for the space beneath it.

To test this, what happens if you decrease the value of gravity, and perhaps apply the delta-time to it, too? Does the object still pass through the ground?

1 Like

I initially tried to use -9.81 instead of -98.1 when I was at the stage of adding the physics, however that resulted in the object floating over the object but it worked after increasing the force of gravity, I do suspect that it was because of me increasing the size of the terrain thanks to your first response.


I have just now tried decreasing the force of the gravity to -9.81*deltatime but still no luck. I am currently trying to upload the files via GitHub and I will add another response to this post when I do succeed in doing so, however, GitHub’s 25MB file limit is making it harder for me to upload the terrain bam file. Hopefully it’ll work by adding via command line

1 Like

Finally succeeded in uploading it into GitHub, the project is at GitHub - azizalbastaki/Genius, if there is any need of clarification for the structure of the code or anything about the code please ask.

How do I get to the problem area? I seem to spawn into the heightfield, which, as you said, works.

I think that I may have an idea of what’s going on, but I want to test my hypothesis…

[edit]
Nevermind, I think that I found it, or another like it.

If I’m correct, then the problem is more or less as follows:

As I said previously, you are using some very big numbers. Some huge numbers, in fact. Your positions are measured in tens or hundred of thousands of units, your gravity is ten times that of Earth, etc.

Conversely, however, your player-collider is only one unit big.

As a result, at the speeds and sizes that you’re using, your collider just passes straight through the infinitely-thin collision-polygons.

A quick test to demonstrate this, and a potential solution, is to change your sphere-solid’s radius to be “100” instead of “1”–on my end, at least, this results in the player-character remaining stable over the “dock”-area.

You can also see the difference by making your player-collider visible (by calling “collider.show()” in the “#collision stuff” section). You should find that with your current code, the collider is all but invisible below you, while with a larger radius it becomes visible.

That said, I’d like to suggest perhaps reducing your scales a bit–that might make life a little bit easier!

1 Like

I wasn’t sure whether you were referring to the collision sphere or the actual model (self.character) so I tried both and didn’t succeed, I am going to take the time to resize my models and constants (walkconstant or the gravity variable) and see where that takes me, thankfully there aren’t too many objects in my scene graph yet.

Another solution that’s on my mind and that I am yet to try is to use a CollisionHandlerQueue instead of a pusher and stop gravity from being applied depending on where the colliding object is, not sure whether it’ll work but I’ll see.

The size of the model should have no real effect on the collision system.

(Unless there’s a scaling factor being applied that I didn’t notice. However, from what I recall, the collision-object isn’t below the visible model in the hierarchy, and so shouldn’t be affected by it.)

Instead, the line that I changed was this, I believe:

Instead of the above, I used this:
self.colliderNode.addSolid(CollisionSphere(0, 0, 0, 100))

Thus producing a sphere of radius 100.

Depending on your frame-rate you might try larger values.

I doubt that this will work, given what I saw: the problem isn’t in the response to collisions (which is what a change in collision-handler will give, I believe), but in the detection of them.

By the way, I think that I may know why the use of the standard gravity-value is producing so slow a descent: you’re applying gravity as a constant speed of 9.8 or 98, rather than a constant acceleration. The actual speed should increase over time, presumably until a terminal velocity is reached, if I’m not much mistaken.

1 Like

Alright, will definitely try this, thanks a lot for your help :slight_smile:

1 Like

Not a problem! Good luck with the rest of the project–I hope that it goes well! :slight_smile:

1 Like

For what it’s worth, I recommend using a CollisionRay for the vertical collisions, and a pusher for the horizontal ones. It’s more reliable than using the pusher to deal with terrain. Roaming Ralph gives an example of this.

2 Likes

This is also a good point, indeed–let me second this advice!

1 Like

Will consider, thanks for the advice :slight_smile:

I have seen Roaming Ralph’s code before and I interpreted it as if it sends a CollisionRay vertically down (I think it also goes upwards but the upwards part is irrelevant for this example unless if the terrain goes up), detects the height of the terrain and sets Ralph’s Z axis to whatever the CollisionRay detected, please correct me If I misunderstood what’s happening in Roaming Ralph.

The problem I have with this approach is that my character has a jetpack so the character won’t always be stuck to the ground, detecting whether the character is airborne or on the ground seems to be the challenge if I were to replicate what Roaming Ralph does, nevertheless, detecting airborness and implementing would be a really good approach to this issue if I can find a way to do that.

Actually, I might have already thought of a way to approach it like that :grin:

So my solution is that I still use gravity to pull my character downwards and detects the height of the terrain, If the object is at that height or below it, I set it to that height otherwise I just keep gravity acting.

1 Like

You could just check whether the character is above the terrain, and if so, don’t reset the position.

3 Likes

Hi all, I have just decided to get back on working on horizontal collisions.

I just wanted to ask, is it feasible/logical to use collision rays for horizontal collisions instead of using a pusher?

For example, would it work if I have 8 collision rays being emitted from the player at equal angles? Would 8 collision rays be more memory expensive than 1 pusher?

I don’t know whether it would be more expensive, but it does seem likely to be more fiddly than a pusher. And possibly less reliable.

I suppose that my question is this: what prompts you to consider using rays instead of a pusher for horizontal collisions?

1 Like