Camera collision detection

Hi! I am making an First Person controller game in the grassy environment from the Hello World program.
I already made a CollisionInvSphere Solid around the environment so that the player doesn’t fall off, but I can’t figure out the radius of the camera to create a CollisionSphere around the camera.
I tried:

camRad = base.camera.getBounds().getRadius()

But I am getting this error:

Assertion failed!: !is_empty() at line 48 of c:\buildslave\sdk-windows-amd64\build\panda\src\mathutil\boundingSphere.I
File “”, line 1, in
AssertionError: !is_empty() at line 48 of c:\buildslave\sdk-windows-amd64\build\panda\src\mathutil\boundingSphere.I

Can someone please help?

An in-game camera doesn’t really have a radius, I don’t think–after all, there’s no geometry to define any sort of size. The closest measure that you might find would be the distance of the near-plane.

That said, you might find that a good size for the camera’s collision is likely a little larger than that. I’d suggest just experimenting with sizes, and seeing what feels good.

Thanks. Could you give me some recommended sizes.
Also, could you tell me where are the models for your tutorial. The tutorial is great but i sadly can’t get the models

Thanks! Setting the radius to 5 did the trick

Now I have another issue. The CollisionHandlerPusher, is not pushing me back. It is actually pushing me down. Any solutions? Here is my code:

from panda3d.core import loadPrcFileData

confVars = '''
window-title Forest Game
'''

loadPrcFileData('', confVars)

from direct.showbase.ShowBase import ShowBase
from panda3d.core import CollisionTraverser, CollisionHandlerPusher
from panda3d.core import CollisionNode, CollisionSphere, CollisionInvSphere


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

        self.disableMouse()
        
        self.cTrav = CollisionTraverser()
        
        pusher = CollisionHandlerPusher()

        self.env = self.loader.loadModel("models/environment")
        self.env.setScale(0.25, 0.25, 0.25)
        self.env.setPos(-8, 42, -3)
        self.env.reparentTo(self.render)
        envBounds = self.env.getBounds()
        envCenter = envBounds.getCenter()
        envRad = envBounds.getRadius() * 2.1
        
        cNode = CollisionNode("environment")
        cNode.addSolid(CollisionInvSphere(envCenter, envRad))
        envC = self.env.attachNewNode(cNode)
        
        camBounds = self.camera.getBounds()
        camCenter = camBounds.getCenter()
        camRad = 5
         
        cNode = CollisionNode("camera")
        cNode.addSolid(CollisionSphere(camCenter, camRad))
        camC = self.camera.attachNewNode(cNode)
        
        self.cTrav.addCollider(camC, pusher)
        pusher.addCollider(camC, self.camera)

        self.accept("w", self.__key, ["w"])
        self.accept("a", self.__key, ["a"])
        self.accept("s", self.__key, ["s"])
        self.accept("d", self.__key, ["d"])
        self.accept("arrow_left", self.__key, ["left"])
        self.accept("arrow_right", self.__key, ["right"])

    def __key(self, key):
        if key == "w":
            self.camera.setY(self.camera, 2)
        elif key == "a":
            self.camera.setX(self.camera, -2)
        elif key == "s":
            self.camera.setY(self.camera, -2)
        elif key == "d":
            self.camera.setX(self.camera, 2)
        elif key == "left":
            self.camera.setH(self.camera, 2)
        elif key == "right":
            self.camera.setH(self.camera, -2)


game = MyGame()
game.run()

That’s detailed in the “Start Here” page, I believe–if you scroll down, you should see links to all of the various assets.

Unless you mean that one or more of those links doesn’t work? In which case, which are you having trouble with?

I’m glad that you so like it! :slight_smile:

I think that part of the problem is that you’re positioning your inverse-sphere at a z-coordinate of -3, which places its equator below the surface of the ground. As a result, when you collide with it you’re colliding with part of its upper hemisphere, and thus colliding with a downward-facing slope. This then causes you to be pushed downwards.

If I may ask, why did you place your inverse-sphere at that z-coordinate?

All that said, if you intend for your character to only ever move in the X-Y plane–i.e. to have no vertical movement–then you can call “setHorizontal(True)” on your pusher–that should constrain the “push” that it provides to that plane.

See the API for more, at this link.

I’m not sure where the z position is. I just used the methods ‘self.env.getBounds().getCenter()’ and ‘self.env.getBounds().getRadius()’ and it automatically did it. I set z to -3 because in the Hello World tutorial, the camera was a little below the tree and rock, so I set the z to -3

That’s fair. I think that the environment model is a little offset, resulting in the tree and rock being a little above the vertical centre.

However, since you’ve attached your inverse-sphere to the environment, it inherits its position from that environment, and thus inherits that downward shift. The camera, meanwhile, is still located at a z-position of 0. As a result, the inverse-sphere ends up located below the camera.

So what I suggest, then, is that you separate the inverse-sphere from the environment model. (You can still given them a common parent-node if you want to move them together.) That way you can offset the environment to centre it nicely, while still leaving the inverse-sphere at its default centred position.

Thanks a lot! pusher.setHorizontal(True) worked for me! Now I will add more components to my game

1 Like

How to do that? I am still a beginner. Though pusher.setHorizontal(True) worked, can you still tell me

And thanks for the quick replies

2 Likes

Do you mean to ask how you separate the inverse-sphere from the environment?

If so, then you simply don’t attach it to the environment; instead attach it to something like “render”.

That is, instead of the following:

        cNode = CollisionNode("environment")
        cNode.addSolid(CollisionInvSphere(envCenter, envRad))
        envC = self.env.attachNewNode(cNode)

You would have this:

        cNode = CollisionNode("environment")
        cNode.addSolid(CollisionInvSphere(envCenter, envRad))
        envC = render.attachNewNode(cNode)

Now the inverse-sphere is no longer attached to the environment-model.

If you want a common parent for the the environment-model and the inverse-sphere, then you simply create a new node, and parent both of those objects to it.

I’m glad that you found a solution. :slight_smile:

Let me note, however, that with the sphere offset downwards your effective radius is slightly smaller than you set it to be: after all, you’re colliding with a part of the sphere higher than the equator, which will then have a smaller circumference.

It’s my pleasure. :slight_smile:

2 Likes

And I will definitely check the models

1 Like

Ok thanks. so should I use the pusher.setHorizontal method or separate the Inverse Sphere from the environment. Which do you think is better

Well, I would suggest separating the inverse-sphere from the environment: it brings you closer to what you’re trying to construct, I suspect, and means that you don’t have model-related offsets affecting your collision.

That said, if you do indeed intend to have only horizontal movement, then I’d suggest also calling “setHorizontal”: it can prevent issues in which collision pushes your object in a direction that you don’t intend, especially if the collision for your world becomes more complex.

(If, however, you intend to have vertical movement as well as horizontal, then the question may become more complex, and might depend on quite what you have in mind.)

2 Likes

I don’t intend to have vertical movement yet. But if I do, I will come back to you.

2 Likes