Actor falling through the world when physics enabled

So I’ve been making some progress, and after starting something new from scratch, inspired mostly by astelix’s tutorials and the Roaming Ralph sample program, I’ve managed to get a simple environment where my actor runs around on keyboard commands, climbs slopes, and does not walk through walls. :smiley:

The actor uses a collision sphere to keep from going through walls, and a collision ray to detect terrain height, apparently - you see, I didn’t actually intend for it to detect terrain height, but suddenly it was doing it, even though there was nothing analoguous to Roaming Ralph’s height detection, so I’ve left the code as is for fear of breaking it.

However, I am having a hard time implementing physics, because when I do so, the actor seems to fall directly through the world and disappear, even though there is a CollisionHandlerFloor() and such. This is when I have implemented gravity and enabled physics on the actor’s appropriate node. I have uploaded the file, including the environment in .blend and .egg, as well as two versions of the code, one with physics (in the third, fourth and fifth “sections” of the World class) and one without. The actor used is the smiley model that comes with Panda. I would be very grateful if someone could take a look at the code and tell me what’s wrong with my physics.

easy-share.com/1911301637/Zip_SSTE.rar

Also, I have a minor question. How do I use the addImpulse() function? Is there a clearly commented, simple, stand-alone snippet of code I could take a look at? What do I have to use it on? I’ve tried using it on various actors and nodes and it never works, always returning an error like “has no attribute addImpulse”. And why does it always seem to appear next to a “getPhysicsObject”? (which also gets the “has no attribute” errors) I get the same errors when trying to use LerpInterval or PosInterval, too, so I must have systematically not understood something. I am trying to create a jump function, see.

EDIT: Oh, and a second small question: I have successfully implemented cartoon shading on an angular, blocky terrain - but it doesn’t make all the edges of the terrain black, only those not directly in front of the camera. How can I make it so that all the terrain’s edges are black?

Thanks for any help you can provide! :slight_smile:

For those who would rather not download the file, here is the physics-related part of the code:

        #Now let's try and enable a physics system.  First, traversers and handlers.
        self.cTrav = CollisionTraverser()
        self.floorHandler = CollisionHandlerFloor()
        self.floorHandler.setMaxVelocity(24)
        self.wallHandler = CollisionHandlerPusher()
        self.physicsHandler = PhysicsCollisionHandler()

        #Now, we enable particles, in order to enable physics.
        self.enableParticles()

        #Then, gravity.
        self.gravityFN = ForceNode("world-forces")
        self.gravityFNP = render.attachNewNode(self.gravityFN)
        self.gravityForce = LinearVectorForce(0,0,-9.81)
        self.gravityFN.addForce(self.gravityForce)
        #Add the gravity to the physics manager.
        base.physicsMgr.addLinearForce(self.gravityForce)


        #Now, we will need masks for the walls and floors.
        self.wallMask = BitMask32.bit(1)
        self.floorMask = BitMask32.bit(2)

        #==========

        #Now to start, we shall load the model, and call it "environ".
        self.environ = self.loader.loadModel("StickSoldiersTestEnvironment")
        self.environ.reparentTo(self.render)
        #The model is quite small, so we need to make it much bigger.
        self.environ.setScale(25)
        #We can center the model at the origin.
        self.environ.setPos(0, 0, 0)

        #We also ought to set the camera position; remember, distances in
        #Panda3D are in rather large numbers!
        base.camera.setPos(0, -250, 160)

        #If we are to implement physics, we must tell the game which
        #parts of the map belong to which BitMask.  First, turn off all general
        #BitMasks.
        self.environ.setCollideMask(BitMask32.allOff())
        #Now we activate the indivitual colliders.
        self.floorCollider = self.environ.find("**/floorcollider")
        self.wallCollider = self.environ.find("**/wallcollider")
        #And set their BitMasks.
        #ERROR: This is apparently "empty".
        self.floorCollider.node().setIntoCollideMask(self.floorMask)
        self.wallCollider.node().setIntoCollideMask(self.wallMask)

        #==========

        #So we want to make an actor with physics enabled; let's give that a shot.
        self.actorNP = NodePath(PandaNode("smiley-physics-node"))
        self.actorNP.reparentTo(base.render)
        self.actor = self.loader.loadModel("smiley")
        self.actor.reparentTo(self.actorNP)
        self.actorNode = ActorNode("smiley-physics")
        self.actorNodePath = self.actorNP.attachNewNode(self.actorNode)
        base.physicsMgr.attachPhysicalNode(self.actorNode)
        self.actor.reparentTo(self.actorNodePath)
        
        #Let's put the model a bit in the air, to make it visible.
        self.actor.setPos(0, 0, 15)
        self.actorNP.setPos(0,0,4)
        #The model is also rather small, so let's enlarge it.
        self.actor.setScale(4)

        #Now let's handle the collision stuff, first turning off the general collision BitMasks.
        self.actor.setCollideMask(BitMask32.allOff())
        #Then attaching a collision solid to the actor.
        self.actorCollider = self.actorNodePath.attachNewNode(CollisionNode("smileyCNode"))
        self.actorCollider.node().addSolid(CollisionSphere(0, 0, 0, 1))
        #And finally enabling the masks on the collision solid (not the actor itself).
        #Wall Mask is used to indicate that objects cannot go into the actor,
        #much like the walls themselves.
        self.actorCollider.node().setFromCollideMask(self.wallMask)
        self.actorCollider.node().setFromCollideMask(self.floorMask)
        self.actorCollider.node().setIntoCollideMask(BitMask32.allOff())

        #And now, we want a collision ray to be attached to our actor.
        self.rayGeometry = CollisionRay(0, 0, 0, 0, 0, -1)
        self.actorRay = self.actor.attachNewNode(CollisionNode("actorRay"))
        self.actorRay.node().addSolid(self.rayGeometry)
        #And then we tell it that it can only collide with the floorMask
        #as a From object, and disable it as an Into object, meaning
        #no From objects can collide with it.
        self.actorRay.node().setFromCollideMask(self.floorMask)
        self.actorRay.node().setIntoCollideMask(BitMask32.allOff())

        self.physicsHandler.addCollider(self.actorCollider, self.actorNodePath)

        self.cTrav.addCollider(self.actorCollider, self.physicsHandler)

        #Finally, this will display the collision rays.
        self.actorCollider.show()
        self.actorRay.show()
       
        #And this will display the collisions.
        self.cTrav.showCollisions(render)

        #==========

        #We also need to disable the mouse, or else our camera controls won't work.
        #UNCERTAINTY: Why not?
        base.disableMouse()
        
        #==========

        #Now we need to assign the collision solids to their respective handlers.
        self.floorHandler.addCollider(self.actorRay, self.actorNP)
        self.wallHandler.addCollider(self.actorCollider, self.actorNP)

        #And now we need to add these colliders to the main traverser.
        self.cTrav.addCollider(self.actorRay, self.floorHandler)
        self.cTrav.addCollider(self.actorCollider, self.wallHandler)
        
        #==========

Hello again :blush:

So I’ve been trying to splice various physics systems into my design, but it’s very difficult to do because I have yet to find a comprehensive physics example that wasn’t very specific to goals that aren’t mine - that is, I haven’t found any physics examples with characters walking around environments; just a bunch of tech demos I try to apply to my actor and environment, only to have it fail for reasons I can’t comprehend.

I sort of understand some of what’s going on, but I’d need to see simple panda physics applied to a character walking around a real environment (most demos seem to use planes or flat surfaces, but I have varying terrain height). I learn this stuff best by example, but I haven’t found any simple examples to learn from.

That said, I’ve tried something new this time around, though there’s a lot of junk code in here because I’m not entirely sure what’s important and what’s not. Anyway, now the actor sort of stays stuck halfway through the terrain for several seconds before sinking through the floor - an improvement, I suppose. Here is the code, with lighting and some failed code excluded. Can anyone tell me what I’m doing wrong? That would be a great help! :slight_smile:

import sys, os
from math import pi, sin, cos
from direct.showbase.ShowBase import ShowBase
from direct.task import Task
from direct.actor.Actor import Actor
from direct.interval.IntervalGlobal import Sequence
from direct.filter.CommonFilters import CommonFilters
from panda3d.core import Point3
from panda3d.core import Filename,AmbientLight,DirectionalLight
from panda3d.core import LightRampAttrib, AuxBitplaneAttrib
from panda3d.core import Vec3,Vec4,BitMask32
from direct.showbase.DirectObject import DirectObject
from panda3d.core import PandaNode,NodePath,Camera,TextNode
from direct.gui.OnscreenText import OnscreenText
from direct.interval.IntervalGlobal import *
from pandac.PandaModules import CollisionHandlerFloor, CollisionHandlerPusher, CollisionNode, CollisionSphere
from pandac.PandaModules import CollisionTraverser, BitMask32, CollisionRay, NodePath
from pandac.PandaModules import *

#This is a function to put titles on the screen.
def addTitle(text):
    return OnscreenText(text=text, style=1, fg=(1,1,1,1),
                        pos=(1.3,-0.95), align=TextNode.ARight, scale = .07)

#==========

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

        #First, let's set up a screen title.
        self.title = OnscreenText(text="StickSoldiers 3D - Test Environment",
                              style=1, fg=(1,1,1,1),
                              pos=(0.7,-0.95), scale = .07)

        #==========

        #Now let's try and enable a physics system.  First, traversers and handlers.
        self.cTrav = CollisionTraverser()
        self.floorHandler = CollisionHandlerFloor()
        self.floorHandler.setMaxVelocity(24)
        self.wallHandler = CollisionHandlerPusher()
        self.physicsHandler = PhysicsCollisionHandler()
        self.gravityHandler = CollisionHandlerGravity()

        #Now, we enable particles, in order to enable physics.
        self.enableParticles()

        #Then, gravity.
        self.gravityFN = ForceNode("world-forces")
        self.gravityFNP = render.attachNewNode(self.gravityFN)
        self.gravityForce = LinearVectorForce(0,0,-9.81)
        self.gravityFN.addForce(self.gravityForce)
        #Add the gravity to the physics manager.
        base.physicsMgr.addLinearForce(self.gravityForce)


        #Now, we will need masks for the walls and floors.
        self.wallMask = BitMask32.bit(1)
        self.floorMask = BitMask32.bit(2)

        #==========

        #Now to start, we shall load the model, and call it "environ".
        self.environ = self.loader.loadModel("StickSoldiersTestEnvironment")
        self.environ.reparentTo(self.render)
        #The model is quite small, so we need to make it much bigger.
        self.environ.setScale(25)
        #We can center the model at the origin.
        self.environ.setPos(0, 0, 0)

        #We also ought to set the camera position; remember, distances in
        #Panda3D are in rather large numbers!
        base.camera.setPos(0, -250, 160)

        #If we are to implement physics, we must tell the game which
        #parts of the map belong to which BitMask.  First, turn off all general
        #BitMasks.
        self.environ.setCollideMask(BitMask32.allOff())
        #Now we activate the indivitual colliders.
        self.floorCollider = self.environ.find("**/floorcollider")
        self.wallCollider = self.environ.find("**/wallcollider")
        #And set their BitMasks.
        #ERROR: This is apparently "empty".
        self.floorCollider.node().setIntoCollideMask(self.floorMask)
        self.wallCollider.node().setIntoCollideMask(self.wallMask)

        #==========

        #So we want to make an actor with physics enabled; let's give that a shot.
        self.actorNP = render.attachNewNode(ActorNode("ballActor"))
        self.actorNP.setPos(0, 0, 20)
        self.actor = loader.loadModel("smiley")
        self.actor.setScale(4)
        self.actor.reparentTo(self.actorNP)
        base.physicsMgr.attachPhysicalNode(self.actorNP.node())
        self.actorNP.node().getPhysicsObject().setMass(10)
        self.collisionh = PhysicsCollisionHandler()
        self.collisionh.setStaticFrictionCoef(0.0)
        self.collisionh.setDynamicFrictionCoef(0.0)

        ballCNPath = self.actorNP.attachNewNode(CollisionNode('ballCN'))
        ballCNPath.node().addSolid(CollisionSphere(0, 0, 0, 1))
        cmask = BitMask32()
        cmask.setBit(1)
        cmask.setBit(0)
        ballCNPath.node().setFromCollideMask(cmask)
        self.collisionh.addCollider(ballCNPath, self.actorNP)
        base.cTrav.addCollider(ballCNPath, self.collisionh)


        '''#Now let's handle the collision stuff, first turning off the general collision BitMasks.
        self.actor.setCollideMask(BitMask32.allOff())
        #Then attaching a collision solid to the actor.
        self.actorCollider = self.actorNodePath.attachNewNode(CollisionNode("smileyCNode"))
        self.actorCollider.node().addSolid(CollisionSphere(0, 0, 0, 1))
        #And finally enabling the masks on the collision solid (not the actor itself).
        #Wall Mask is used to indicate that objects cannot go into the actor,
        #much like the walls themselves.
        self.actorCollider.node().setFromCollideMask(self.wallMask)
        self.actorCollider.node().setFromCollideMask(self.floorMask)
        self.actorCollider.node().setIntoCollideMask(BitMask32.allOff())'''

        #And now, we want a collision ray to be attached to our actor.
        self.rayGeometry = CollisionRay(0, 0, 2, 0, 0, -1)
        self.actorRay = self.actorNP.attachNewNode(CollisionNode("actorRay"))
        self.actorRay.node().addSolid(self.rayGeometry)
        #And then we tell it that it can only collide with the floorMask
        #as a From object, and disable it as an Into object, meaning
        #no From objects can collide with it.
        self.actorRay.node().setFromCollideMask(self.floorMask)
        self.actorRay.node().setIntoCollideMask(BitMask32.allOff())

        

        self.cTrav.addCollider(ballCNPath, self.physicsHandler)

        #Finally, this will display the collision rays.
        ballCNPath.show()
        self.actorRay.show()
       
        #And this will display the collisions.
        self.cTrav.showCollisions(render)

        #==========

        #We also need to disable the mouse, or else our camera controls won't work.
        #UNCERTAINTY: Why not?
        base.disableMouse()
        
        #==========

        #Now we need to assign the collision solids to their respective handlers.
        self.floorHandler.addCollider(self.actorRay, self.actorNP)
        self.wallHandler.addCollider(ballCNPath, self.actorNP)
        self.gravityHandler.addCollider(self.actorRay, self.actorNP)

        #And now we need to add these colliders to the main traverser.
        self.cTrav.addCollider(self.actorRay, self.floorHandler)
        self.cTrav.addCollider(ballCNPath, self.wallHandler)
        self.cTrav.addCollider(self.actorRay, self.gravityHandler)

        #Now we need to set up key bindings.  Let's first try to set up
        #a simple Exit command.  This involves accept functions.
        #First, though, we need a keyMap for the command we want to implement.
        self.keyMap = {"camleft":0, "camright":0, "camforward":0, "cambackward":0, "camup":0, "camdown":0,
                       "actorforward":0, "actorbackward":0, "actorleft":0, "actorright":0}
        #Now that that's done, let's test the escape function.
        self.accept("escape", sys.exit)
        #Now that works; let's try something more complicated:
        #using the arrow keys to move the camera back and forth, and turning
        #it left and right.
        self.accept("arrow_left", self.setKey, ["camleft",1])
        self.accept("arrow_right", self.setKey, ["camright",1])
        self.accept("arrow_up", self.setKey, ["camforward",1])
        self.accept("arrow_down", self.setKey, ["cambackward",1])
        self.accept("t", self.setKey, ["camup",1])
        self.accept("h", self.setKey, ["camdown",1])
        self.accept("w", self.setKey, ["actorforward",1])
        self.accept("s", self.setKey, ["actorbackward",1])
        self.accept("a", self.setKey, ["actorleft",1])
        self.accept("d", self.setKey, ["actorright",1])
        self.accept("arrow_left-up", self.setKey, ["camleft",0])
        self.accept("arrow_right-up", self.setKey, ["camright",0])
        self.accept("arrow_up-up", self.setKey, ["camforward",0])
        self.accept("arrow_down-up", self.setKey, ["cambackward",0])
        self.accept("t-up", self.setKey, ["camup",0])
        self.accept("h-up", self.setKey, ["camdown",0])
        self.accept("w-up", self.setKey, ["actorforward",0])
        self.accept("s-up", self.setKey, ["actorbackward",0])
        self.accept("a-up", self.setKey, ["actorleft",0])
        self.accept("d-up", self.setKey, ["actorright",0])

        #Now we need to make a method for actually moving the camera
        #When the appropriate input is given.  First, however, we need to
        #tell the task manager about this method, so it can run it
        #every frame.
        taskMgr.add(self.moveCam,"moveTask")
        taskMgr.add(self.moveActor,"moveTask")
        
        #==========
        
    #Now we also need a function for actually changing the keyMap
    #according to the scheme detailed above.  Remember, this must be
    #parallel to the __init__ block, not inside it!
    def setKey(self, key, value):
        self.keyMap[key] = value
        
    #==========
        
    #Now let's create that camera-movement method.
    def moveCam(self, task):
        base.camera.lookAt(0, 0, 0)
        #Now we need if-conditions for each of the keys.
        if (self.keyMap["camleft"] != 0):
            #UNCERTAINTY: What is globalClock, what is getdT(), and
            #why are we calling these?
            base.camera.setX(base.camera, -70 * globalClock.getDt())
        if (self.keyMap["camright"]!=0):
            base.camera.setX(base.camera, +70 * globalClock.getDt())
        if (self.keyMap["camforward"] != 0):
            base.camera.setY(base.camera, +70 * globalClock.getDt())
        if (self.keyMap["cambackward"]!=0):
            base.camera.setY(base.camera, -70 * globalClock.getDt())
        if (self.keyMap["camup"] != 0):
            base.camera.setZ(base.camera, +20 * globalClock.getDt())
        if (self.keyMap["camdown"]!=0):
            base.camera.setZ(base.camera, -20 * globalClock.getDt())

        #UNCERTAINTY: I am not sure why we need to do this.
        return task.cont

    #==========
        
    #Now let's try and create a function that moves the character around.
    def moveActor(self, task):
        if (self.keyMap["actorforward"] != 0):
            self.actor.setY(self.actor, -20 * globalClock.getDt())
        if (self.keyMap["actorbackward"] != 0):
            self.actor.setY(self.actor, +20 * globalClock.getDt())
        if (self.keyMap["actorleft"] != 0):
            self.actor.setH(self.actor.getH() + 270 * globalClock.getDt())
        if (self.keyMap["actorright"] != 0):
            self.actor.setH(self.actor.getH() - 270 * globalClock.getDt())

        self.cTrav.traverse(render)

        #UNCERTAINTY: I am not sure why we need to do this.
        return task.cont
            
w = World()
w.run()

So I’ve cut the code down to its bare essentials, but still no success; the ball still falls through the world. I tried making separate classes for world and actor, so that I could more easily keep their code separate, but now the code won’t even start. I get all sorts of error messages related to render, either “Player instance has no attribute ‘render’” or “Global name ‘render’ is not defined”.

I’ve tried appending self. to all instances of render, but though this works when I keep everything in one class (the ball still falls through the world, even though I tried a CollisionHandlerGravity), when I split the classes it insists that Player() has no attribute ‘render’, though it doesn’t complain about the render in World(). What’s going on here?

This is the code that works, but has the actor falling through the floor:

import sys, random, os, math, time
from math import pi, sin, cos
from direct.showbase.ShowBase import ShowBase
from direct.task import Task
from direct.actor.Actor import Actor
from direct.interval.IntervalGlobal import Sequence
from direct.filter.CommonFilters import CommonFilters
from panda3d.core import Point3
from panda3d.core import Filename,AmbientLight,DirectionalLight
from panda3d.core import LightRampAttrib, AuxBitplaneAttrib
from panda3d.core import Vec3,Vec4,BitMask32, Point3
from direct.showbase.DirectObject import DirectObject
from panda3d.core import PandaNode,NodePath,Camera,TextNode
from direct.gui.OnscreenText import OnscreenText
from direct.interval.IntervalGlobal import *
from pandac.PandaModules import CollisionHandlerFloor, CollisionHandlerPusher, CollisionNode, CollisionSphere
from pandac.PandaModules import CollisionTraverser, BitMask32, CollisionRay, NodePath
from pandac.PandaModules import *

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

        base.cTrav=CollisionTraverser()
        base.cTrav.setRespectPrevTransform(True)
        base.enableParticles()
        base.cTrav.showCollisions (self.render)
        gravity = ForceNode("globalGravityForce")
        gravityNP = render.attachNewNode(gravity)
        gravityForce = LinearVectorForce (0, 0, -9.8)  # 9.8 m/s gravity
        gravityForce.setMassDependent(False)  # constant acceleration (set true if you think Galileo was wrong)
        gravity.addForce(gravityForce)
        base.physicsMgr.addLinearForce(gravityForce)
        self.gravHandler = CollisionHandlerGravity()
        self.floorBit = 1
        self.wallBit = 0

        self.loadWorld()
        self.loadLight()
        self.loadCamera()
        self.loadActor()

    def loadLight(self):
        plight = AmbientLight("my plight")
        plight.setColor(VBase4(0.12, 0.12, 0.12, 1))
        plnp = render.attachNewNode(plight)
        render.setLight(plnp)
        light2 = PointLight("pointlight")
        plnp2 = render.attachNewNode(light2)
        plnp2.setPos(0, 0, 25)
        render.setLight(plnp2)

    def loadCamera(self):
        base.disableMouse() 
        base.camera.setPos(30,-200,120)
        base.camera.lookAt(0, 0, 0)

    def loadWorld(self):
        base.setBackgroundColor(0.0,0.3,0.0)
        self.terrain = loader.loadModel("StickSoldiersTestEnvironment")
        self.terrain.reparentTo(self.render)
        self.terrain.setScale(15)

        self.floor = self.terrain.find("**/floorcollider")
        self.floor.setCollideMask(BitMask32.allOff())
        self.floor.node().setCollideMask(BitMask32.bit(self.floorBit))
        self.walls = self.terrain.find("**/wallcollider")
        self.walls.setCollideMask(BitMask32.allOff())
        self.walls.node().setCollideMask(BitMask32.bit(self.wallBit))

    def loadActor(self):
        self.physNode = NodePath(PandaNode("physicsNode"))
        self.physNode.reparentTo(self.render)
        self.actor = loader.loadModel("smiley")
        self.actor.reparentTo(self.render)
        self.actorNode = ActorNode("smiley-physics")
        self.actorNP = self.physNode.attachNewNode(self.actorNode)
        base.physicsMgr.attachPhysicalNode(self.actorNode)
        self.actor.reparentTo(self.actorNP)
        self.actorColNode = self.actor.attachNewNode(CollisionNode('colNode'))
        self.actorColNode.node().addSolid(CollisionRay(0, 0, 2, 0, 0, -1))
        self.actorNP.setPos(0, 0, 15)
        self.gravHandler.addCollider(self.actorColNode, self.actor)
           
DO=DirectObject()
DO.accept("escape",sys.exit)
#player = Player()
w = World()
run()

I thought the collisions mask was missing, so I changed loadActor() to the following, but got errors telling me that libpanda.NodePath doesn’t have the attribute setFromCollideMask.

    def loadActor(self):
        self.physNode = NodePath(PandaNode("physicsNode"))
        self.physNode.reparentTo(self.render)
        self.actor = loader.loadModel("smiley")
        self.actor.reparentTo(self.render)
        self.actorNode = ActorNode("smiley-physics")
        self.actorNP = self.physNode.attachNewNode(self.actorNode)
        base.physicsMgr.attachPhysicalNode(self.actorNode)
        self.actor.reparentTo(self.actorNP)
        self.ray = CollisionRay(0, 0, 2, 0, 0, -1)
        self.actorColNode = self.actor.attachNewNode(CollisionNode('colNode'))
        self.actorColNode.node().addSolid(self.ray)
        self.actorColNode.setFromCollideMask(BitMask32.bit(self.floorBit))
        self.actorNP.setPos(0, 0, 15)
        self.gravHandler.addCollider(self.actorColNode, self.actor)

And this is the code that doesn’t work at all, with classes. Note that I have tried many permutations, including having Player() inherit from ShowBase instead of DirectObject; and using self.render, base.render, and just plain old render.

import sys, random, os, math, time
from math import pi, sin, cos
from direct.showbase.ShowBase import ShowBase
from direct.task import Task
from direct.actor.Actor import Actor
from direct.interval.IntervalGlobal import Sequence
from direct.filter.CommonFilters import CommonFilters
from panda3d.core import Point3
from panda3d.core import Filename,AmbientLight,DirectionalLight
from panda3d.core import LightRampAttrib, AuxBitplaneAttrib
from panda3d.core import Vec3,Vec4,BitMask32, Point3
from direct.showbase.DirectObject import DirectObject
from panda3d.core import PandaNode,NodePath,Camera,TextNode
from direct.gui.OnscreenText import OnscreenText
from direct.interval.IntervalGlobal import *
from pandac.PandaModules import CollisionHandlerFloor, CollisionHandlerPusher, CollisionNode, CollisionSphere
from pandac.PandaModules import CollisionTraverser, BitMask32, CollisionRay, NodePath
from pandac.PandaModules import *

class World(ShowBase):
    def __init__(self):
        ShowBase.__init__(self, player)

        self.player = player

        base.cTrav=CollisionTraverser()
        base.cTrav.setRespectPrevTransform(True)
        base.enableParticles()
        base.cTrav.showCollisions (self.render)
        gravity = ForceNode("globalGravityForce")
        gravityNP = render.attachNewNode(gravity)
        gravityForce = LinearVectorForce (0, 0, -9.8)  # 9.8 m/s gravity
        gravityForce.setMassDependent(False)  # constant acceleration (set true if you think Galileo was wrong)
        gravity.addForce(gravityForce)
        base.physicsMgr.addLinearForce(gravityForce)
        self.gravHandler = CollisionHandlerGravity()
        self.floorBit = 1
        self.wallBit = 0

        self.loadWorld()
        self.loadLight()
        self.loadCamera()
        #self.loadActor()

    def loadLight(self):
        plight = AmbientLight("my plight")
        plight.setColor(VBase4(0.12, 0.12, 0.12, 1))
        plnp = render.attachNewNode(plight)
        render.setLight(plnp)
        light2 = PointLight("pointlight")
        plnp2 = render.attachNewNode(light2)
        plnp2.setPos(0, 0, 25)
        render.setLight(plnp2)

    def loadCamera(self):
        base.disableMouse() 
        base.camera.setPos(30,-200,120)
        base.camera.lookAt(0, 0, 0)

    def loadWorld(self):
        base.setBackgroundColor(0.0,0.3,0.0)
        self.terrain = loader.loadModel("StickSoldiersTestEnvironment")
        self.terrain.reparentTo(self.render)
        self.terrain.setScale(15)

        self.floor = self.terrain.find("**/floorcollider")
        self.floor.setCollideMask(BitMask32.allOff())
        self.floor.node().setCollideMask(BitMask32.bit(self.floorBit))
        self.walls = self.terrain.find("**/wallcollider")
        self.walls.setCollideMask(BitMask32.allOff())
        self.walls.node().setCollideMask(BitMask32.bit(self.wallBit))
        player.actorNP.setPos(0, 0, 15)

class Player(DirectObject):
    def __init__(self):
        self.loadActor()

    def loadActor(self):
        self.physNode = NodePath(PandaNode("physicsNode"))
        self.physNode.reparentTo(self.render)
        self.actor = loader.loadModel("smiley")
        self.actor.reparentTo(self.render)
        self.actorNode = ActorNode("smiley-physics")
        self.actorNP = self.physNode.attachNewNode(self.actorNode)
        base.physicsMgr.attachPhysicalNode(self.actorNode)
        self.actor.reparentTo(self.actorNP)
        self.actorColNode = self.actor.attachNewNode(CollisionNode('colNode'))
        self.actorColNode.node().addSolid(CollisionRay(0, 0, 2, 0, 0, -1))
        self.gravHandler.addCollider(self.actorColNode, self.actor)
        
            
DO=DirectObject()
DO.accept("escape",sys.exit)
player = Player()
w = World()
run()

What am I doing wrong? :frowning: