def __init__(self): self.keyMap = {"left":0, "right":0, "forward":0, "cam-left":0, "cam-right":0} base.win.setClearColor(Vec4(0,0,0,1)) # Post the instructions self.title = addTitle("Panda3D Tutorial: Roaming Ralph (Walking on Uneven Terrain)") self.inst1 = addInstructions(0.95, "[ESC]: Quit") self.inst2 = addInstructions(0.90, "[Left Arrow]: Rotate Ralph Left") self.inst3 = addInstructions(0.85, "[Right Arrow]: Rotate Ralph Right") self.inst4 = addInstructions(0.80, "[Up Arrow]: Run Ralph Forward") self.inst6 = addInstructions(0.70, "[A]: Rotate Camera Left") self.inst7 = addInstructions(0.65, "[S]: Rotate Camera Right") # Set up the environment # # This environment model contains collision meshes. If you look # in the egg file, you will see the following: # # <Collide> { Polyset keep descend } # # This tag causes the following mesh to be converted to a collision # mesh -- a mesh which is optimized for collision, not rendering. # It also keeps the original mesh, so there are now two copies --- # one optimized for rendering, one for collisions. self.environ = loader.loadModel("models/samples/roaming_ralph/world") self.environ.reparentTo(render) self.environ.setPos(0,0,0) # Create the main character, Ralph ralphStartPos = self.environ.find("**/start_point").getPos() self.ralph = Actor("models/samples/roaming_ralph/ralph", {"run":"models/samples/roaming_ralph/ralph_run", "walk":"models/samples/roaming_ralph/ralph_walk"}) self.ralph.reparentTo(render) self.ralph.setScale(.2) self.ralph.setPos(ralphStartPos) # Create a floater object. We use the "floater" as a temporary # variable in a variety of calculations. self.floater = NodePath(PandaNode("floater")) self.floater.reparentTo(render) # Accept the control keys for movement and rotation self.accept("escape", sys.exit) self.accept("arrow_left", self.setKey, ["left",1]) self.accept("arrow_right", self.setKey, ["right",1]) self.accept("arrow_up", self.setKey, ["forward",1]) self.accept("a", self.setKey, ["cam-left",1]) self.accept("s", self.setKey, ["cam-right",1]) self.accept("arrow_left-up", self.setKey, ["left",0]) self.accept("arrow_right-up", self.setKey, ["right",0]) self.accept("arrow_up-up", self.setKey, ["forward",0]) self.accept("a-up", self.setKey, ["cam-left",0]) self.accept("s-up", self.setKey, ["cam-right",0]) taskMgr.add(self.move,"moveTask") # Game state variables self.isMoving = False # Set up the camera base.disableMouse() base.camera.setPos(self.ralph.getX(),self.ralph.getY()+10,2) # 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. self.cTrav = CollisionTraverser() self.ralphGroundRay = CollisionRay() self.ralphGroundRay.setOrigin(0,0,1000) self.ralphGroundRay.setDirection(0,0,-1) self.ralphGroundCol = CollisionNode('ralphRay') self.ralphGroundCol.addSolid(self.ralphGroundRay) self.ralphGroundCol.setFromCollideMask(BitMask32.bit(0)) self.ralphGroundCol.setIntoCollideMask(BitMask32.allOff()) self.ralphGroundColNp = self.ralph.attachNewNode(self.ralphGroundCol) self.ralphGroundHandler = CollisionHandlerQueue() self.cTrav.addCollider(self.ralphGroundColNp, self.ralphGroundHandler) self.camGroundRay = CollisionRay() self.camGroundRay.setOrigin(0,0,1000) self.camGroundRay.setDirection(0,0,-1) self.camGroundCol = CollisionNode('camRay') self.camGroundCol.addSolid(self.camGroundRay) self.camGroundCol.setFromCollideMask(BitMask32.bit(0)) self.camGroundCol.setIntoCollideMask(BitMask32.allOff()) self.camGroundColNp = base.camera.attachNewNode(self.camGroundCol) self.camGroundHandler = CollisionHandlerQueue() self.cTrav.addCollider(self.camGroundColNp, self.camGroundHandler)
def __init__(self): #This code puts the standard title and instruction text on screen self.title = OnscreenText(text="Panda3D: Tutorial - Collision Detection", style=1, fg=(1,1,1,1), pos=(0.7,-0.95), scale = .07, font = font) self.instructions = OnscreenText(text="Mouse pointer tilts the board", pos = (-1.3, .95), fg=(1,1,1,1), font = font, align = TextNode.ALeft, scale = .05) self.accept("escape", sys.exit) #Escape quits base.disableMouse() #Disable mouse-based camera control camera.setPosHpr(0, 0, 25, 0, -90, 0) #Place the camera #Load the maze and place it in the scene self.maze = loader.loadModel("models/samples/ball_in_maze/maze") self.maze.reparentTo(render) #Most times, you want collisions to be tested against invisible geometry #rather than every polygon. This is because testing against every polygon #in the scene is usually too slow. You can have simplified or approximate #geometry for the solids and still get good results. # #Sometimes you'll want to create and position your own collision solids in #code, but it's often easier to have them built automatically. This can be #done by adding special tags into an egg file. Check maze.egg and ball.egg #and look for lines starting with <Collide>. The part is brackets tells #Panda exactly what to do. Polyset means to use the polygons in that group #as solids, while Sphere tells panda to make a collision sphere around them #Keep means to keep the polygons in the group as visable geometry (good #for the ball, not for the triggers), and descend means to make sure that #the settings are applied to any subgroups. # #Once we have the collision tags in the models, we can get to them using #NodePath's find command #Find the collision node named wall_collide self.walls = self.maze.find("**/wall_collide") #Collision objects are sorted using BitMasks. BitMasks are ordinary numbers #with extra methods for working with them as binary bits. Every collision #solid has both a from mask and an into mask. Before Panda tests two #objects, it checks to make sure that the from and into collision masks #have at least one bit in common. That way things that shouldn't interact #won't. Normal model nodes have collision masks as well. By default they #are set to bit 20. If you want to collide against actual visable polygons, #set a from collide mask to include bit 20 # #For this example, we will make everything we want the ball to collide with #include bit 0 self.walls.node().setIntoCollideMask(BitMask32.bit(0)) #CollisionNodes are usually invisible but can be shown. Uncomment the next #line to see the collision walls #self.walls.show() #We will now find the triggers for the holes and set their masks to 0 as #well. We also set their names to make them easier to identify during #collisions self.loseTriggers = [] for i in range(6): trigger = self.maze.find("**/hole_collide" + str(i)) trigger.node().setIntoCollideMask(BitMask32.bit(0)) trigger.node().setName("loseTrigger") self.loseTriggers.append(trigger) #Uncomment this line to see the triggers #trigger.show() #Ground_collide is a single polygon on the same plane as the ground in the #maze. We will use a ray to collide with it so that we will know exactly #what height to put the ball at every frame. Since this is not something #that we want the ball itself to collide with, it has a different #bitmask. self.mazeGround = self.maze.find("**/ground_collide") self.mazeGround.node().setIntoCollideMask(BitMask32.bit(1)) #Load the ball and attach it to the scene #It is on a root dummy node so that we can rotate the ball itself without #rotating the ray that will be attached to it self.ballRoot = render.attachNewNode("ballRoot") self.ball = loader.loadModel("models/samples/ball_in_maze/ball") self.ball.reparentTo(self.ballRoot) #Find the collison sphere for the ball which was created in the egg file #Notice that it has a from collision mask of bit 0, and an into collison #mask of no bits. This means that the ball can only cause collisions, not #be collided into self.ballSphere = self.ball.find("**/ball") self.ballSphere.node().setFromCollideMask(BitMask32.bit(0)) self.ballSphere.node().setIntoCollideMask(BitMask32.allOff()) #No we create a ray to start above the ball and cast down. This is to #Determine the height the ball should be at and the angle the floor is #tilting. We could have used the sphere around the ball itself, but it #would not be as reliable self.ballGroundRay = CollisionRay() #Create the ray self.ballGroundRay.setOrigin(0,0,10) #Set its origin self.ballGroundRay.setDirection(0,0,-1) #And its direction #Collision solids go in CollisionNode self.ballGroundCol = CollisionNode('groundRay') #Create and name the node self.ballGroundCol.addSolid(self.ballGroundRay) #Add the ray self.ballGroundCol.setFromCollideMask(BitMask32.bit(1)) #Set its bitmasks self.ballGroundCol.setIntoCollideMask(BitMask32.allOff()) #Attach the node to the ballRoot so that the ray is relative to the ball #(it will always be 10 feet over the ball and point down) self.ballGroundColNp = self.ballRoot.attachNewNode(self.ballGroundCol) #Uncomment this line to see the ray #self.ballGroundColNp.show() #Finally, we create a CollisionTraverser. CollisionTraversers are what #do the job of calculating collisions self.cTrav = CollisionTraverser() #Collision traverservs tell collision handlers about collisions, and then #the handler decides what to do with the information. We are using a #CollisionHandlerQueue, which simply creates a list of all of the #collisions in a given pass. There are more sophisticated handlers like #one that sends events and another that tries to keep collided objects #apart, but the results are often better with a simple queue self.cHandler = CollisionHandlerQueue() #Now we add the collision nodes that can create a collision to the #traverser. The traverser will compare these to all others nodes in the #scene. There is a limit of 32 CollisionNodes per traverser #We add the collider, and the handler to use as a pair self.cTrav.addCollider(self.ballSphere, self.cHandler) self.cTrav.addCollider(self.ballGroundColNp, self.cHandler) #Collision traversers have a built in tool to help visualize collisions. #Uncomment the next line to see it. #self.cTrav.showCollisions(render) #This section deals with lighting for the ball. Only the ball was lit #because the maze has static lighting pregenerated by the modeler lAttrib = LightAttrib.makeAllOff() ambientLight = AmbientLight( "ambientLight" ) ambientLight.setColor( Vec4(.55, .55, .55, 1) ) lAttrib = lAttrib.addLight( ambientLight ) directionalLight = DirectionalLight( "directionalLight" ) directionalLight.setDirection( Vec3( 0, 0, -1 ) ) directionalLight.setColor( Vec4( 0.375, 0.375, 0.375, 1 ) ) directionalLight.setSpecularColor(Vec4(1,1,1,1)) lAttrib = lAttrib.addLight( directionalLight ) self.ballRoot.node().setAttrib( lAttrib ) #This section deals with adding a specular highlight to the ball to make #it look shiny m = Material() m.setSpecular(Vec4(1,1,1,1)) m.setShininess(96) self.ball.setMaterial(m, 1) #Finally, we call start for more initialization self.start()
def __init__(self): #This code puts the standard title and instruction text on screen self.title = OnscreenText( text="Panda3D: Tutorial - Collision Detection", style=1, fg=(1, 1, 1, 1), pos=(0.7, -0.95), scale=.07, font=font) self.instructions = OnscreenText(text="Mouse pointer tilts the board", pos=(-1.3, .95), fg=(1, 1, 1, 1), font=font, align=TextNode.ALeft, scale=.05) self.accept("escape", sys.exit) #Escape quits base.disableMouse() #Disable mouse-based camera control camera.setPosHpr(0, 0, 25, 0, -90, 0) #Place the camera #Load the maze and place it in the scene self.maze = loader.loadModel("models/samples/ball_in_maze/maze") self.maze.reparentTo(render) #Most times, you want collisions to be tested against invisible geometry #rather than every polygon. This is because testing against every polygon #in the scene is usually too slow. You can have simplified or approximate #geometry for the solids and still get good results. # #Sometimes you'll want to create and position your own collision solids in #code, but it's often easier to have them built automatically. This can be #done by adding special tags into an egg file. Check maze.egg and ball.egg #and look for lines starting with <Collide>. The part is brackets tells #Panda exactly what to do. Polyset means to use the polygons in that group #as solids, while Sphere tells panda to make a collision sphere around them #Keep means to keep the polygons in the group as visable geometry (good #for the ball, not for the triggers), and descend means to make sure that #the settings are applied to any subgroups. # #Once we have the collision tags in the models, we can get to them using #NodePath's find command #Find the collision node named wall_collide self.walls = self.maze.find("**/wall_collide") #Collision objects are sorted using BitMasks. BitMasks are ordinary numbers #with extra methods for working with them as binary bits. Every collision #solid has both a from mask and an into mask. Before Panda tests two #objects, it checks to make sure that the from and into collision masks #have at least one bit in common. That way things that shouldn't interact #won't. Normal model nodes have collision masks as well. By default they #are set to bit 20. If you want to collide against actual visable polygons, #set a from collide mask to include bit 20 # #For this example, we will make everything we want the ball to collide with #include bit 0 self.walls.node().setIntoCollideMask(BitMask32.bit(0)) #CollisionNodes are usually invisible but can be shown. Uncomment the next #line to see the collision walls #self.walls.show() #We will now find the triggers for the holes and set their masks to 0 as #well. We also set their names to make them easier to identify during #collisions self.loseTriggers = [] for i in range(6): trigger = self.maze.find("**/hole_collide" + str(i)) trigger.node().setIntoCollideMask(BitMask32.bit(0)) trigger.node().setName("loseTrigger") self.loseTriggers.append(trigger) #Uncomment this line to see the triggers #trigger.show() #Ground_collide is a single polygon on the same plane as the ground in the #maze. We will use a ray to collide with it so that we will know exactly #what height to put the ball at every frame. Since this is not something #that we want the ball itself to collide with, it has a different #bitmask. self.mazeGround = self.maze.find("**/ground_collide") self.mazeGround.node().setIntoCollideMask(BitMask32.bit(1)) #Load the ball and attach it to the scene #It is on a root dummy node so that we can rotate the ball itself without #rotating the ray that will be attached to it self.ballRoot = render.attachNewNode("ballRoot") self.ball = loader.loadModel("models/samples/ball_in_maze/ball") self.ball.reparentTo(self.ballRoot) #Find the collison sphere for the ball which was created in the egg file #Notice that it has a from collision mask of bit 0, and an into collison #mask of no bits. This means that the ball can only cause collisions, not #be collided into self.ballSphere = self.ball.find("**/ball") self.ballSphere.node().setFromCollideMask(BitMask32.bit(0)) self.ballSphere.node().setIntoCollideMask(BitMask32.allOff()) #No we create a ray to start above the ball and cast down. This is to #Determine the height the ball should be at and the angle the floor is #tilting. We could have used the sphere around the ball itself, but it #would not be as reliable self.ballGroundRay = CollisionRay() #Create the ray self.ballGroundRay.setOrigin(0, 0, 10) #Set its origin self.ballGroundRay.setDirection(0, 0, -1) #And its direction #Collision solids go in CollisionNode self.ballGroundCol = CollisionNode( 'groundRay') #Create and name the node self.ballGroundCol.addSolid(self.ballGroundRay) #Add the ray self.ballGroundCol.setFromCollideMask( BitMask32.bit(1)) #Set its bitmasks self.ballGroundCol.setIntoCollideMask(BitMask32.allOff()) #Attach the node to the ballRoot so that the ray is relative to the ball #(it will always be 10 feet over the ball and point down) self.ballGroundColNp = self.ballRoot.attachNewNode(self.ballGroundCol) #Uncomment this line to see the ray #self.ballGroundColNp.show() #Finally, we create a CollisionTraverser. CollisionTraversers are what #do the job of calculating collisions self.cTrav = CollisionTraverser() #Collision traverservs tell collision handlers about collisions, and then #the handler decides what to do with the information. We are using a #CollisionHandlerQueue, which simply creates a list of all of the #collisions in a given pass. There are more sophisticated handlers like #one that sends events and another that tries to keep collided objects #apart, but the results are often better with a simple queue self.cHandler = CollisionHandlerQueue() #Now we add the collision nodes that can create a collision to the #traverser. The traverser will compare these to all others nodes in the #scene. There is a limit of 32 CollisionNodes per traverser #We add the collider, and the handler to use as a pair self.cTrav.addCollider(self.ballSphere, self.cHandler) self.cTrav.addCollider(self.ballGroundColNp, self.cHandler) #Collision traversers have a built in tool to help visualize collisions. #Uncomment the next line to see it. #self.cTrav.showCollisions(render) #This section deals with lighting for the ball. Only the ball was lit #because the maze has static lighting pregenerated by the modeler lAttrib = LightAttrib.makeAllOff() ambientLight = AmbientLight("ambientLight") ambientLight.setColor(Vec4(.55, .55, .55, 1)) lAttrib = lAttrib.addLight(ambientLight) directionalLight = DirectionalLight("directionalLight") directionalLight.setDirection(Vec3(0, 0, -1)) directionalLight.setColor(Vec4(0.375, 0.375, 0.375, 1)) directionalLight.setSpecularColor(Vec4(1, 1, 1, 1)) lAttrib = lAttrib.addLight(directionalLight) self.ballRoot.node().setAttrib(lAttrib) #This section deals with adding a specular highlight to the ball to make #it look shiny m = Material() m.setSpecular(Vec4(1, 1, 1, 1)) m.setShininess(96) self.ball.setMaterial(m, 1) #Finally, we call start for more initialization self.start()
def __init__(self): self.keyMap = { "left": 0, "right": 0, "forward": 0, "cam-left": 0, "cam-right": 0 } base.win.setClearColor(Vec4(0, 0, 0, 1)) # Post the instructions self.title = addTitle( "Panda3D Tutorial: Roaming Ralph (Walking on Uneven Terrain)") self.inst1 = addInstructions(0.95, "[ESC]: Quit") self.inst2 = addInstructions(0.90, "[Left Arrow]: Rotate Ralph Left") self.inst3 = addInstructions(0.85, "[Right Arrow]: Rotate Ralph Right") self.inst4 = addInstructions(0.80, "[Up Arrow]: Run Ralph Forward") self.inst6 = addInstructions(0.70, "[A]: Rotate Camera Left") self.inst7 = addInstructions(0.65, "[S]: Rotate Camera Right") # Set up the environment # # This environment model contains collision meshes. If you look # in the egg file, you will see the following: # # <Collide> { Polyset keep descend } # # This tag causes the following mesh to be converted to a collision # mesh -- a mesh which is optimized for collision, not rendering. # It also keeps the original mesh, so there are now two copies --- # one optimized for rendering, one for collisions. self.environ = loader.loadModel("models/samples/roaming_ralph/world") self.environ.reparentTo(render) self.environ.setPos(0, 0, 0) # Create the main character, Ralph ralphStartPos = self.environ.find("**/start_point").getPos() self.ralph = Actor( "models/samples/roaming_ralph/ralph", { "run": "models/samples/roaming_ralph/ralph_run", "walk": "models/samples/roaming_ralph/ralph_walk" }) self.ralph.reparentTo(render) self.ralph.setScale(.2) self.ralph.setPos(ralphStartPos) # Create a floater object. We use the "floater" as a temporary # variable in a variety of calculations. self.floater = NodePath(PandaNode("floater")) self.floater.reparentTo(render) # Accept the control keys for movement and rotation self.accept("escape", sys.exit) self.accept("arrow_left", self.setKey, ["left", 1]) self.accept("arrow_right", self.setKey, ["right", 1]) self.accept("arrow_up", self.setKey, ["forward", 1]) self.accept("a", self.setKey, ["cam-left", 1]) self.accept("s", self.setKey, ["cam-right", 1]) self.accept("arrow_left-up", self.setKey, ["left", 0]) self.accept("arrow_right-up", self.setKey, ["right", 0]) self.accept("arrow_up-up", self.setKey, ["forward", 0]) self.accept("a-up", self.setKey, ["cam-left", 0]) self.accept("s-up", self.setKey, ["cam-right", 0]) taskMgr.add(self.move, "moveTask") # Game state variables self.isMoving = False # Set up the camera base.disableMouse() base.camera.setPos(self.ralph.getX(), self.ralph.getY() + 10, 2) # 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. self.cTrav = CollisionTraverser() self.ralphGroundRay = CollisionRay() self.ralphGroundRay.setOrigin(0, 0, 1000) self.ralphGroundRay.setDirection(0, 0, -1) self.ralphGroundCol = CollisionNode('ralphRay') self.ralphGroundCol.addSolid(self.ralphGroundRay) self.ralphGroundCol.setFromCollideMask(BitMask32.bit(0)) self.ralphGroundCol.setIntoCollideMask(BitMask32.allOff()) self.ralphGroundColNp = self.ralph.attachNewNode(self.ralphGroundCol) self.ralphGroundHandler = CollisionHandlerQueue() self.cTrav.addCollider(self.ralphGroundColNp, self.ralphGroundHandler) self.camGroundRay = CollisionRay() self.camGroundRay.setOrigin(0, 0, 1000) self.camGroundRay.setDirection(0, 0, -1) self.camGroundCol = CollisionNode('camRay') self.camGroundCol.addSolid(self.camGroundRay) self.camGroundCol.setFromCollideMask(BitMask32.bit(0)) self.camGroundCol.setIntoCollideMask(BitMask32.allOff()) self.camGroundColNp = base.camera.attachNewNode(self.camGroundCol) self.camGroundHandler = CollisionHandlerQueue() self.cTrav.addCollider(self.camGroundColNp, self.camGroundHandler)