Physics sphere won't show

I am trying to add a spherical hitbox around the player in my program. However, when I try and show the sphere, it doesn’t show. I don’t know what is wrong. Thanks in advance for your help.

Here is my code (excuse my excessive comments, I am an idiot who will forget things if they aren’t clear):

#imports animatable objects
from direct.actor.Actor import Actor
from panda3d.core import Vec3
#this are shapes for the collision boxes of the entities
from panda3d.core import CollisionTube, CollisionNode, CollisionSphere, CollisionHandlerQueue

FRICTION = 200

#Entity holds the player, hooligan and trap
class Entity():
    def __init__(self,pos,modelName,modelAnimations,colliderName):
        self.entityInstance = Actor(modelName,modelAnimations)
        self.entityInstance.reparentTo(render)
        self.entityInstance.setPos(pos)

        self.moving = None

        #this sets velocity and acceleration
        self.velocity = Vec3(0,0,0)
        self.acceleration = 50.0
        self.speedCap = 50

        #This adds a collision node, so that collisions can registered
        pcColNode = CollisionNode(colliderName)
        #this assigns a tube shaped collision box
        pcColNode.addSolid(CollisionSphere(0,0,0,8))
        self.collider = self.entityInstance.attachNewNode(pcColNode)
        self.collider.show()
    def update(self,time):
        #the entity adjusts itself to the distance
        #it should have moved in the time since last update
        self.entityInstance.setPos(self.entityInstance.getPos() + self.velocity*time)

        speed=self.velocity.length()
        #this checks if the speed variable exceed the speedCap variable
        #if so, it changes the velocity and speed to reflect the speedCap
        if speed > self.speedCap:
            self.velocity.normalize()
            self.velocity = self.velocity * self.speedCap
            speed = self.speedCap

        #this checks whether the player has let go of the w button
        if not self.moving:
            #if they have, it calculates how much the player should have slowed down
            frictionValue = FRICTION*time
            #if the player should have stopped by now,
            #it sets the velocity to 0
            if frictionValue > speed:
                self.velocity.set(0,0,0)
            #otherwise, it slows down the velocity to the appropriate amount.
            else:
                #this creates a friction vector (which will be a negative number)
                #based on how much the player should have slowed down
                #if then 'adds' (it's a negative number) from self.velocity
                #which scales down the player's velocity
                frictionVector = -self.velocity
                frictionVector.normalize()
                frictionVector = frictionVector * frictionValue

                self.velocity = self.velocity + frictionVector

#PC stands for Player Character
class PC(Entity):
    def __init__(self):
        Entity.__init__(self,
                        #this sets the initial location of the panda
                        Vec3(0,0,0),
                        "models/panda-model",
                        {
                        #I will eventually put animations here
                        },
                        "player"
                        )
        #This scales the panda down to fit on the screen
        self.entityInstance.setScale(0.010,0.010,0.010)


        base.collisionHandler.addCollider(self.collider,self.entityInstance)
        base.collisionDetector.addCollider(self.collider,base.collisionHandler)
        

    #this contains things that need to be updated as the PC class runs
    def update(self, keys, time):

        Entity.update(self,time)

        #tells us whether the player is currently moving
        self.moving = False
        #If a movement key is currently being pressed
        #or has been released
        #this moves in the Panda character
        #in the correct direction
        if keys["up"]:
            self.moving=True
            self.velocity.addY(self.acceleration*time)
        if keys["left"]:
            self.moving = True
            self.velocity.addX(-self.acceleration*time)
        if keys["down"]:
            self.moving = True
            self.velocity.addY(-self.acceleration*time)
        if keys ["right"]:
            self.moving = True
            self.velocity.addX(self.acceleration*time)



#self.pandaActor = Actor("models/panda-model")
#assigns panda model to scene graph, rendering it
#self.pandaActor.reparentTo(self.render)
#sets position to center of screen
#self.pandaActor.setPos(0,0,0)
#scales down panda to fit in center of screen
#self.pandaActor.setScale(0.010,0.010,0.010)
#import statements go here
#showbase is used to display the Panda3D window to the user
from direct.showbase.ShowBase import ShowBase
#window properties is used to decide the size and other
#miscellaneous properties of the windows
from panda3d.core import WindowProperties
#screen info is used to detect the size of the user's
#monitor so that the window is appropriately sized
#only get_monitors is installed because that is the only module used
from screeninfo import get_monitors
#directGUI is used to create the menu for the game
from direct.gui.DirectGui import *
#actor is used to create moving 'actors' like enemies, traps and the player
from direct.actor.Actor import Actor
#Panda3Actors holds all the 'actors' used in this program
from Panda3Actors import *
#Vec3 and Vec4 are used to store floating point vectors
from panda3d.core import Vec3,Vec4
#these are all used for the Panda3D physics engine
from panda3d.core import CollisionTube,CollisionNode, CollisionTraverser, CollisionHandlerPusher, CollisionSphere


#Game is the class that contains/ will contain most of the game's framework e.g arena, physics
#It is also the first class used upon starting the game
class Game(ShowBase):
    def __init__ (self):
        #ShowBase loads most of Panda3D modules
        #Here, it loads the window the game is displayed on
        ShowBase.__init__ (self)
        #this fetches the resolution of the monitor the user is using
        monitor = get_monitors()
        #get_monitors() returns a list, a string is needed
        #Converts monitor to a string so that string manipulation can be used
        monitor = str(monitor)
        #This function takes a search term and string index
        #Uses them to extract a substring of the monitor string containing the
        #height or width featured in the monitor string
        def findScreenAttribute(searchTerm,endPoint):
            startPos = monitor.find(searchTerm)
            endPos = monitor[startPos:endPoint]
            pos1 = endPos.find("=")
            pos2 = endPos.find(",")
            return(endPos[pos1+1:pos2])


        #Here the function is called, to find the resolution
        width = (findScreenAttribute("width=",30))
        height = (findScreenAttribute("height=",50))
        #These values are converted to integers so they can be used by SetSize
        width = int(width)
        height = int(height)
        #The window size is set to the width and height variables
        WindowSize = WindowProperties()
        WindowSize.setSize(width,height)
        self.win.requestProperties(WindowSize)

        #This is the whole opening menu
        self.startMenuBackground = DirectFrame(frameColor = (0, 0, 0, 1),
                                               frameSize = (-1, 1, -1, 1),
                                               parent = render2d)
        self.startMenu = DirectFrame(frameColor = (1,1,1,0))

        #this sets the font of all menus
        self.font = loader.loadFont("Assets/ModeSeven.ttf")
        #The title, e.g. "Panda 3" in center of page
        title = DirectLabel(text = "Panda 3",
                            parent = self.startMenu,
                            pos = (0,0, 0.7),
                            scale = 0.3,
                            text_fg = (1,1,1,1),
                            #Relief is the background for the text, this removes the default grey background
                            relief = None,
                            text_font = self.font
                            )
        #pandaImage is the image that displays in the centre of the screen
        pandaImage = OnscreenImage(image='Assets/PandaImage.png', pos = (0,0,0),
                                   scale = 0.3,
                                   parent=self.startMenu
                                   )
        #the START button on the opening menu
        btn = DirectButton(text = "START",
                           command = self.gameStart,
                           parent = self.startMenu,
                           scale = 0.2,
                           pos = (0,0,-0.7),
                           frameSize = (-2, 2, -1, 1.5),
                           #this removes the default border from button, making it a plain square
                           relief = DGG.FLAT,
                           #this sets button to white
                           frameColor = (1,1,1,1),
                           text_font = self.font
                           )
        btn = DirectButton(text = "LEADERBOARD",
                           command = self.openLeaderboard,
                           parent = self.startMenu,
                           scale = 0.2,
                           pos = (-0.95,0,-0.7),
                           text_scale = 0.5,
                           frameSize = (-2, 2, -1, 1),
                           #removes border from button, making it plain square
                           relief = DGG.FLAT,
                           #this sets button to white
                           frameColor = (1,1,1,1),
                           text_font = self.font
                           )
        btn = DirectButton(text = "EXIT",
                           command = self.quitGame,
                           parent = self.startMenu,
                           scale = 0.2,
                           pos = (0.95,0,-0.7),
                           text_scale = 0.7,
                           frameSize = (-2, 2, -1, 1),
                           #removes border from button, making it a plain square
                           relief = DGG.FLAT,
                           #this sets button to white
                           frameColor = (1,1,1,1),
                           text_font = self.font
                           )
        self.startMenu.show()

        #Panda3D has default mouse control, I am not using that so I disable it
        self.disableMouse()
        #Panda3D operates on a scene graph, a series of interconnected nodes
        #that dictate the heirarchy of objects
        #this is how Panda3D renders objects
        #this loads the model
        self.arena = loader.loadModel("Assets/Arena.obj")
        #this loads the texture
        arenaTexture = loader.loadTexture("Assets/ArenaTexture.psd")
        #this puts the texture on the model
        self.arena.setTexture(arenaTexture)
        #this adds the arena model to the scene graph
        #allowing it to be rendered
        self.arena.reparentTo(render)
        #self.startMenu.hide()
        #self.startMenuBackground.hide()

        #.obj models load into Panda3D sideways
        #this rotates the model so that it's a horizontal orientation
        #P stands for Pitch
        self.arena.setP(90)
        #This sets the camera to a high position so that it can view the arena
        self.camera.setPos(0,0,200)
        #this rotates the camera so it's facing down
        self.camera.setP(-90)

        self.keyLog = {
            "up": False,
            "left": False,
            "down": False,
            "right": False}

        #This notifies Panda3D to check if these keys change
        #And registers the change if they do
        self.accept("w", self.changeKeyLog, ["up",True])
        self.accept("w-up", self.changeKeyLog, ["up",False])
        self.accept("a", self.changeKeyLog, ["left",True])
        self.accept("a-up",self.changeKeyLog, ["left",False])
        self.accept("s", self.changeKeyLog, ["down",True])
        self.accept("s-up", self.changeKeyLog, ["down",False])
        self.accept("d", self.changeKeyLog, ["right", True])
        self.accept("d-up", self.changeKeyLog, ["right", False])

        #this detects when collisions happen
        self.collisionDetector = CollisionTraverser()
        #this dictates how the program responds when a collision happens
        self.collisionHandler = CollisionHandlerPusher()

        self.collisionHandler.setHorizontal(True)

        #This is the task manager for Panda3D
        #I will use the taskUpdate method for things that need to be checked
        #As the program runs e.g. player movement
        self.taskUpdate = taskMgr.add(self.update, "update")

        self.pc = None


    def update(self,task):
        #this sets timeElapsed to the time elapsed since the last update
        timeElapsed=globalClock.getDt()

        #this checks if the pc is created yet
        #If so, it calls the pc.update method(in Panda3Actors)
        #passing in the keyLog and the task
        if self.pc is not None:
            self.pc.update(self.keyLog,timeElapsed)

        return task.cont


    #This updates the values in the keylog dictionary
    #It's used to correct the dictionary after keys are pressed by the user
    def changeKeyLog(self, key, keyState):
        self.keyLog[key] = keyState
        print("Testing: ", key, " == ", keyState)

    def gameStart(self):
        self.startMenu.hide()
        self.startMenuBackground.hide()

        #Player is created
        self.pc = PC()


        print("Button clicked")
    def openLeaderboard(self):
        print("Button clicked")
    def quitGame(self):
        print("Button Clicked")

#runGame will be the first process to run, as it starts the game
runGame = Game()
runGame.run()

First of all, let me please ask: in future copy in only the code that seems relevant–in general a short snippet is much easier (and much more attractive) to go through than a program entire.

That said, I think that I see the problem:

You’re scaling your Panda-Actor (which is understandable). But you’re also attaching your collider to that Actor, and as a result it’s inheriting that scaling. Thus it ends up with a size of (8 * 0.01) = 0.08–which I suspect is small enough that it’s hidden inside the Panda.

(By the way, on which note, I’d recommend against having a scaling-factor apply to a collider, where feasible–it can interfere with the results of the collision system, I believe.)

1 Like