def getObjectsInBox(self, mins, maxs): objects = [] # Create a one-off collision box, traverser, and queue to test against all MapObjects box = CollisionBox(mins, maxs) node = CollisionNode("selectToolCollBox") node.addSolid(box) node.setFromCollideMask(self.Mask) node.setIntoCollideMask(BitMask32.allOff()) boxNp = self.doc.render.attachNewNode(node) queue = CollisionHandlerQueue() base.clickTraverse(boxNp, queue) queue.sortEntries() key = self.Key entries = queue.getEntries() # Select every MapObject our box intersected with for entry in entries: np = entry.getIntoNodePath().findNetPythonTag(key) if not np.isEmpty(): obj = np.getPythonTag(key) actual = self.getActualObject(obj, entry) if isinstance(actual, list): for a in actual: if not any(a == x[0] for x in objects): objects.append((a, entry)) else: objects.append((actual, entry)) boxNp.removeNode() return objects
def is_collided(objcm_list0, objcm_list1, toggle_contact_points=False, toggle_plot_cdprimit=False): """ detect the collision between collision models :param: objcm_list0, a single collision model or a list of collision models :param: objcm_list1 :return: True or False author: weiwei date: 20190312osaka, 20201214osaka """ if not isinstance(objcm_list0, list): objcm_list0 = [objcm_list0] if not isinstance(objcm_list1, list): objcm_list1 = [objcm_list1] if toggle_plot_cdprimit: for one_objcm in objcm_list0: one_objcm.show_cdprimit() for one_objcm in objcm_list1: one_objcm.show_cdprimit() tmpnp = NodePath("collision nodepath") ctrav = CollisionTraverser() chan = CollisionHandlerQueue() for one_objcm in objcm_list0: ctrav.addCollider(one_objcm.copy_cdnp_to(tmpnp), chan) for one_objcm in objcm_list1: one_objcm.copy_cdnp_to(tmpnp) ctrav.traverse(tmpnp) if chan.getNumEntries() > 0: if toggle_contact_points: contact_points = [ da.pdv3_to_npv3(cd_entry.getSurfacePoint(base.render)) for cd_entry in chan.getEntries() ] return True, contact_points else: return True else: return False
class moveMario(ShowBase): def __init__(self): # Set up the window, camera, etc. ShowBase.__init__(self) self.ser = serial.Serial('/dev/tty.usbmodem1421', 9600) # Set the background color to black self.win.setClearColor((0, 0, 0, 1)) # This is used to store which keys are currently pressed. self.keyMap = { "left": 0, "right": 0, "forward": 0, "reverse": 0, "cam-left": 0, "cam-right": 0 } #Initialize Track self.track = self.loader.loadModel("luigi_circuit") self.track.setScale(1.5) self.track.reparentTo(render) #Intitial where Mario needs to be #marioStartPos = self.track.find("**/start_point").getPos() marioStartPos = Vec3(50, -29, 0.35) #Actual start possition #Using ralph because the model is made with correct collision masking and animation self.marioActor = Actor("models/ralph", { "run": "models/ralph-run", "walk": "models/ralph-walk" }) self.marioActor.setScale(0.1, 0.1, 0.1) self.marioActor.setH(self.marioActor, 270) self.marioActor.reparentTo(self.render) self.marioActor.setPos(marioStartPos + (0, 0, 0.5)) #Floater above so Camera has something to look at self.floater = NodePath(PandaNode("floater")) self.floater.reparentTo(self.marioActor) self.floater.setZ(2.0) taskMgr.add(self.move, "moveTask") # Game state variables self.isMoving = False # Set up the camera self.disableMouse() self.camera.setPos(self.marioActor.getX() + 100, self.marioActor.getY(), 1) #Collision Rays self.cTrav = CollisionTraverser() self.marioGroundRay = CollisionRay() self.marioGroundRay.setOrigin(0, 0, 9) self.marioGroundRay.setDirection(0, 0, -1) self.marioGroundCol = CollisionNode('marioRay') self.marioGroundCol.addSolid(self.marioGroundRay) self.marioGroundCol.setFromCollideMask(CollideMask.bit(0)) self.marioGroundCol.setIntoCollideMask(CollideMask.allOff()) self.marioGroundColNp = self.marioActor.attachNewNode( self.marioGroundCol) self.marioGroundHandler = CollisionHandlerQueue() self.cTrav.addCollider(self.marioGroundColNp, self.marioGroundHandler) self.camGroundRay = CollisionRay() self.camGroundRay.setOrigin(0, 0, 9) self.camGroundRay.setDirection(0, 0, -1) self.camGroundCol = CollisionNode('camRay') self.camGroundCol.addSolid(self.camGroundRay) self.camGroundCol.setFromCollideMask(CollideMask.bit(0)) self.camGroundCol.setIntoCollideMask(CollideMask.allOff()) self.camGroundColNp = self.camera.attachNewNode(self.camGroundCol) self.camGroundHandler = CollisionHandlerQueue() self.cTrav.addCollider(self.camGroundColNp, self.camGroundHandler) # Records the state of the arrow keys def setKey(self, key, value): self.keyMap[key] = value def move(self, task): elapsed = globalClock.getDt() # If a move-key is pressed, move Mario in the specified direction. startpos = self.marioActor.getPos() line = self.ser.readline() listOfCoord = line.split(":") if (len(listOfCoord) == 7): x = listOfCoord[1] g = listOfCoord[3] r = listOfCoord[5] if (10 <= float(x) <= 180): #MAKE IT TURN RIGHT self.setKey("right", True) self.setKey("left", False) elif (180 < float(x) <= 350): #MAKE IT TURN LEFT self.setKey("right", False) self.setKey("left", True) else: self.setKey("right", False) self.setKey("left", False) #Make it move forward if (int(g) == 1): self.setKey("forward", True) else: self.setKey("forward", False) #Make it move in Reverse if (int(r) == 1): self.setKey("reverse", True) else: self.setKey("reverse", False) if self.keyMap["left"]: self.marioActor.setH(self.marioActor.getH() + 50 * elapsed) self.camera.setX(self.camera, +5 * elapsed) if self.keyMap["right"]: self.marioActor.setH(self.marioActor.getH() - 50 * elapsed) self.camera.setX(self.camera, -5 * elapsed) if self.keyMap["forward"]: self.marioActor.setY(self.marioActor, -100 * elapsed) if self.keyMap["reverse"]: self.marioActor.setY(self.marioActor, 100 * elapsed) #When moving - run the animation - Taken from roaming ralph example if self.keyMap["forward"] or self.keyMap["left"] or self.keyMap[ "right"]: if self.isMoving is False: self.marioActor.loop("run") self.isMoving = True else: if self.isMoving: self.marioActor.stop() self.marioActor.pose("walk", 5) self.isMoving = False #Camera uses - modified from roaming ralph camvec = self.marioActor.getPos() - self.camera.getPos() camvec.setZ(0) camdist = camvec.length() camvec.normalize() if camdist > 5.0: self.camera.setPos(self.camera.getPos() + camvec * (camdist - 5)) camdist = 5.0 if camdist < 2.5: self.camera.setPos(self.camera.getPos() - camvec * (2.5 - camdist)) camdist = 2.5 #Collission terrain checking - taken from roaming ralph entries = list(self.marioGroundHandler.getEntries()) entries.sort(key=lambda x: x.getSurfacePoint(render).getZ()) if len(entries) > 0 and entries[0].getIntoNode().getName( ) == "terrain": self.marioActor.setZ(entries[0].getSurfacePoint(render).getZ()) else: self.marioActor.setPos(startpos) # Keep the camera at level - taken from roaming ralph entries = list(self.camGroundHandler.getEntries()) entries.sort(key=lambda x: x.getSurfacePoint(render).getZ()) if len(entries) > 0 and entries[0].getIntoNode().getName( ) == "terrain": self.camera.setZ(entries[0].getSurfacePoint(render).getZ() + 1.0) if self.camera.getZ() < self.marioActor.getZ() + 1.0: self.camera.setZ(self.marioActor.getZ() + 1.0) self.camera.lookAt(self.floater) return task.cont
class RoamingRalphDemo(ShowBase): def __init__(self): # Set up the window, camera, etc. ShowBase.__init__(self) # Set the background color to black self.win.setClearColor((0, 0, 0, 1)) # Post the instructions self.title = addTitle( "Panda3D Tutorial: Roaming Ralph (Walking on Uneven Terrain)") self.inst1 = addInstructions(0.06, "[ESC]: Quit") self.inst2 = addInstructions(0.12, "[Left trackpad]: Rotate Left") self.inst3 = addInstructions(0.18, "[Right trackpad]: Rotate Right") self.inst4 = addInstructions(0.24, "[Up trackpad]: Walk Forward") self.inst4 = addInstructions(0.30, "[Down trackpad]: Walk Backward") # 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/world") self.environ.reparentTo(render) # Create the main character, Ralph self.vr = RoamingRalphVR() self.vr.init(msaa=4) self.ralph = render.attachNewNode('ralph') self.ralphStartPos = self.environ.find("**/start_point").getPos() self.vr.tracking_space.setPos(self.ralphStartPos) self.ralph.setPos(self.vr.hmd_anchor.getPos(render)) self.accept("escape", sys.exit) taskMgr.add(self.collision, "collisionTask") # Set up the camera self.disableMouse() # 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, 9) self.ralphGroundRay.setDirection(0, 0, -1) self.ralphGroundCol = CollisionNode('ralphRay') self.ralphGroundCol.addSolid(self.ralphGroundRay) self.ralphGroundCol.setFromCollideMask(CollideMask.bit(0)) self.ralphGroundCol.setIntoCollideMask(CollideMask.allOff()) self.ralphGroundColNp = self.ralph.attachNewNode(self.ralphGroundCol) self.ralphGroundHandler = CollisionHandlerQueue() self.cTrav.addCollider(self.ralphGroundColNp, self.ralphGroundHandler) # Uncomment this line to see the collision rays #self.ralphGroundColNp.show() # Uncomment this line to show a visual representation of the # collisions occuring #self.cTrav.showCollisions(render) # Create some lighting ambientLight = AmbientLight("ambientLight") ambientLight.setColor((.3, .3, .3, 1)) directionalLight = DirectionalLight("directionalLight") directionalLight.setDirection((-5, -5, -5)) directionalLight.setColor((1, 1, 1, 1)) directionalLight.setSpecularColor((1, 1, 1, 1)) render.setLight(render.attachNewNode(ambientLight)) render.setLight(render.attachNewNode(directionalLight)) # Grid checking and collision detection def collision(self, task): # Normally, we would have to call traverse() to check for collisions. # However, the class ShowBase that we inherit from has a task to do # this for us, if we assign a CollisionTraverser to self.cTrav. #self.cTrav.traverse(render) # Adjust ralph's Z coordinate. If ralph's ray hit terrain, # update his Z. If it hit anything else, or didn't hit anything, put # him back where he was last frame. entries = list(self.ralphGroundHandler.getEntries()) entries.sort(key=lambda x: x.getSurfacePoint(render).getZ()) if len(entries) > 0 and entries[0].getIntoNode().getName( ) == "terrain": self.vr.tracking_space.setZ( entries[0].getSurfacePoint(render).getZ()) else: self.vr.tracking_space.setPos(self.ralphStartPos) self.ralph.setPos(self.vr.hmd_anchor.getPos(render)) # save ralph's initial position so that we can restore it, # in case he falls off the map or runs into something. self.ralphStartPos = self.vr.tracking_space.getPos() return task.cont
class MousePicker(object): def __init__(self, pickTag="MyPickingTag", nodeName="pickRay", showCollisions=False): self.pickTag = pickTag self.nodeName = nodeName self.showCollisions = showCollisions def create(self): self.mPickerTraverser = CollisionTraverser() self.mCollisionQue = CollisionHandlerQueue() self.mPickRay = CollisionRay() self.mPickRay.setOrigin(base.camera.getPos(base.render)) self.mPickRay.setDirection(base.render.getRelativeVector(base.camera, Vec3(0, 1, 0))) # create our collison Node to hold the ray self.mPickNode = CollisionNode(self.nodeName) self.mPickNode.addSolid(self.mPickRay) # Attach that node to the camera since the ray will need to be positioned # relative to it, returns a new nodepath # well use the default geometry mask # this is inefficent but its for mouse picking only self.mPickNP = base.camera.attachNewNode(self.mPickNode) # we'll use what panda calls the "from" node. This is reall a silly convention # but from nodes are nodes that are active, while into nodes are usually passive environments # this isnt a hard rule, but following it usually reduces processing # Everything to be picked will use bit 1. This way if we were doing other # collision we could seperate it, we use bitmasks to determine what we check other objects against # if they dont have a bitmask for bit 1 well skip them! self.mPickNode.setFromCollideMask(BitMask32(1)) # Register the ray as something that can cause collisions self.mPickerTraverser.addCollider(self.mPickNP, self.mCollisionQue) # Setup 2D picker self.mPickerTraverser2D = CollisionTraverser() self.mCollisionQue2D = CollisionHandlerQueue() self.mPickNode2D = CollisionNode("2D PickNode") self.mPickNode2D.setFromCollideMask(BitMask32(1)) self.mPickNode2D.setIntoCollideMask(BitMask32.allOff()) self.mPick2DNP = base.camera2d.attachNewNode(self.mPickNode2D) self.mPickRay2D = CollisionRay() self.mPickNode2D.addSolid(self.mPickRay2D) self.mPickerTraverser2D.addCollider(self.mPick2DNP, self.mCollisionQue2D) if self.showCollisions: self.mPickerTraverser.showCollisions(base.render) self.mPickerTraverser2D.showCollisions(base.aspect2d) def mousePick(self, traverse=None, tag=None): # do we have a mouse if base.mouseWatcherNode.hasMouse() == False: return None, None traverse = traverse or base.render tag = tag or self.pickTag mpos = base.mouseWatcherNode.getMouse() # Set the position of the ray based on the mouse position self.mPickRay.setFromLens(base.camNode, mpos.getX(), mpos.getY()) self.mPickerTraverser.traverse(traverse) if self.mCollisionQue.getNumEntries() > 0: self.mCollisionQue.sortEntries() for entry in self.mCollisionQue.getEntries(): pickedObj = entry.getIntoNodePath() pickedObj = pickedObj.findNetTag(tag) if not pickedObj.isEmpty(): pos = entry.getSurfacePoint(base.render) return pickedObj, pos return None, None def mousePick2D(self, traverse=None, tag=None, all=False): # do we have a mouse if base.mouseWatcherNode.hasMouse() == False: return None, None traverse = traverse or base.render tag = tag or self.pickTag mpos = base.mouseWatcherNode.getMouse() self.mPickRay2D.setFromLens(base.cam2d.node(), mpos.getX(), mpos.getY()) self.mPickerTraverser2D.traverse(base.aspect2d) if self.mCollisionQue2D.getNumEntries() > 0: self.mCollisionQue2D.sortEntries() if all: return ( [ (entry.getIntoNodePath().findNetTag(tag), entry.getSurfacePoint(base.aspect2d)) for entry in self.mCollisionQue2D.getEntries() ], None, ) else: entry = self.mCollisionQue2D.getEntry(0) pickedObj = entry.getIntoNodePath() pickedObj = pickedObj.findNetTag(tag) if not pickedObj.isEmpty(): pos = entry.getSurfacePoint(base.aspect2d) return pickedObj, pos return None, None
class SpaceFlight(ShowBase): def __init__(self): ShowBase.__init__(self) self.text = OnscreenText \ ( parent = base.a2dBottomCenter, align=TextNode.ARight, fg=(1, 1, 1, 1), pos=(0.2, 1.), scale=0.1, shadow=(0, 0, 0, 0.5) ) self.setBackgroundColor(0, 0, 0) self.disableMouse() self.fog = Fog('distanceFog') self.fog.setColor(0, 0, 0) self.fog.setExpDensity(.002) # self.queue = CollisionHandlerQueue() self.trav = CollisionTraverser('traverser') base.cTrav = self.trav self.loadSky() self.reloadGame() self.keyMap = {'left' : 0, 'right' : 0, 'up' : 0, 'down' : 0} self.gamePause = False # self.accept('escape', sys.exit) self.accept('p', self.pause) self.accept('r', self.reloadGame) self.accept('arrow_left', self.setKey, ['left', True]) self.accept('arrow_right', self.setKey, ['right', True]) self.accept('arrow_up', self.setKey, ['up', True]) self.accept('arrow_down', self.setKey, ['down', True]) self.accept('arrow_left-up', self.setKey, ['left', False]) self.accept('arrow_right-up', self.setKey, ['right', False]) self.accept('arrow_up-up', self.setKey, ['up', False]) self.accept('arrow_down-up', self.setKey, ['down', False]) # taskMgr.add(self.moveShip, 'moveShip') taskMgr.add(self.moveAsteroids, 'moveAsteroids') taskMgr.add(self.handleCollisions, 'handleCollisions') # if DEBUG: self.trav.showCollisions(render) render.find('**/ship_collision').show() for asteroid in render.findAllMatches('**/asteroid_collision*'): asteroid.show() def loadSky(self): self.sky = loader.loadModel('models/solar_sky_sphere.egg.pz') self.sky_tex = loader.loadTexture('models/stars_1k_tex.jpg') self.sky.setTexture(self.sky_tex, 1) self.sky.reparentTo(render) self.sky.setScale(500) def loadShip(self): self.ship = loader.loadModel('models/alice-scifi--fighter/fighter.egg') self.ship.reparentTo(render) self.ship.setPos(START_X, START_Y, START_Z) self.ship.setScale(0.25) # add some physics ship_col = self.ship.attachNewNode(CollisionNode('ship_collision')) col_sphere = CollisionSphere(START_X, START_Y, 0, SHIP_SPHERE_RADIUS) ship_col.node().addSolid(col_sphere) self.trav.addCollider(ship_col, self.queue) def spawnAsteroid(self): asteroid = loader.loadModel(choice(ASTEROID_SHAPES)) asteroid_tex = loader.loadTexture('models/rock03.jpg') asteroid.setTexture(asteroid_tex, 1) asteroid.reparentTo(render) asteroid.setFog(self.fog) self.asteroids.append(asteroid) self.asteroids_rotation.append(randint(ASTEROID_ROTATE_MIN, ASTEROID_ROTATE_MAX)) # num = len(self.asteroids) - 1 asteroid_col = asteroid.attachNewNode(CollisionNode('asteroid_collision_%d' % num)) col_sphere = CollisionSphere(0, 0, 0, ASTEROID_SPHERE_RADIUS) asteroid_col.node().addSolid(col_sphere) # asteroid.setX(randint(MIN_X, MAX_X)) asteroid.setY(randint(ASTEROID_SPAWN_MIN_Y, ASTEROID_SPAWN_MAX_Y)) asteroid.setZ(randint(MIN_Z, MAX_Z)) def setKey(self, key, value): self.keyMap[key] = value if key in ['left', 'right'] and value == False: self.ship.setH(0) if key in ['up', 'down'] and value == False: self.ship.setP(0) def updateCamera(self): x, y, z = self.ship.getPos() self.camera.setPos(x, y - 40, z + 25) self.camera.lookAt(x, y, z + 10) def moveAsteroids(self, task): dt = globalClock.getDt() if not self.gamePause: for num, asteroid in enumerate(self.asteroids): asteroid.setY(asteroid.getY() - ASTEROID_SPEED * dt) rotation = self.asteroids_rotation[num] asteroid.setH(asteroid.getH() - rotation * ASTEROID_SPEED * dt) if asteroid.getY() < self.camera.getY() + 10: asteroid.setX(randint(MIN_X, MAX_X)) asteroid.setY(randint(ASTEROID_SPAWN_MIN_Y, ASTEROID_SPAWN_MAX_Y)) asteroid.setZ(randint(MIN_Z, MAX_Z)) return task.cont def rollbackOnBoard(self, minPos, maxPos, getFunc, setFunc): if getFunc() < minPos: setFunc(minPos) if getFunc() > maxPos: setFunc(maxPos) def applyBound(self): self.rollbackOnBoard(MIN_X, MAX_X, self.ship.getX, self.ship.setX) self.rollbackOnBoard(MIN_Z, MAX_Z, self.ship.getZ, self.ship.setZ) def moveShip(self, task): dt = globalClock.getDt() if not self.gamePause: if self.keyMap['left']: self.ship.setX(self.ship.getX() - SHIP_SPEED * dt) self.ship.setH(TURN_SPEED) elif self.keyMap['right']: self.ship.setX(self.ship.getX() + SHIP_SPEED * dt) self.ship.setH(-TURN_SPEED) elif self.keyMap['up']: self.ship.setZ(self.ship.getZ() + SHIP_SPEED * dt) self.ship.setP(TURN_SPEED) elif self.keyMap['down']: self.ship.setZ(self.ship.getZ() - 5 * SHIP_SPEED * dt) self.ship.setP(-TURN_SPEED) self.sky.setP(self.sky.getP() - dt * 10) self.applyBound() self.updateCamera() return task.cont def handleCollisions(self, task): if not self.gamePause: for entry in self.queue.getEntries(): node = entry.getFromNodePath() if node.getName() == 'ship_collision': self.gamePause = True self.text.setText('You lose :(') return task.cont def pause(self): self.gamePause = not self.gamePause def reloadGame(self): self.gamePause = False self.text.clearText() if hasattr(self, 'asteroids'): for asteroid in self.asteroids: asteroid.removeNode() self.asteroids = [] self.asteroids_rotation = [] if hasattr(self, 'ship'): self.ship.removeNode() self.loadShip() for _ in xrange(ASTEROID_MAX_CNT): self.spawnAsteroid()
class Raycaster(Entity): def __init__(self): super().__init__( name = 'raycaster', eternal = True ) self._picker = CollisionTraverser() # Make a traverser self._pq = CollisionHandlerQueue() # Make a handler self._pickerNode = CollisionNode('raycaster') self._pickerNP = self.attach_new_node(self._pickerNode) self._collision_ray = CollisionRay() # Make our ray self._pickerNode.addSolid(self._collision_ray) self._picker.addCollider(self._pickerNP, self._pq) self._pickerNP.show() def distance(self, a, b): return math.sqrt(sum( (a - b)**2 for a, b in zip(a, b))) def raycast(self, origin, direction=(0,0,1), dist=math.inf, traverse_target=scene, ignore=list(), debug=False): self.position = origin self.look_at(self.position + direction) # need to do this for it to work for some reason self._collision_ray.set_origin(Vec3(0,0,0)) self._collision_ray.set_direction(Vec3(0,1,0)) if debug: self._pickerNP.show() else: self._pickerNP.hide() self._picker.traverse(traverse_target) if self._pq.get_num_entries() == 0: self.hit = Hit(hit=False) return self.hit self._pq.sort_entries() self.entries = [ # filter out ignored entities e for e in self._pq.getEntries() if e.get_into_node_path().parent not in ignore ] if len(self.entries) == 0: self.hit = Hit(hit=False) return self.hit self.collision = self.entries[0] nP = self.collision.get_into_node_path().parent point = self.collision.get_surface_point(nP) point = Vec3(point[0], point[2], point[1]) world_point = self.collision.get_surface_point(render) world_point = Vec3(world_point[0], world_point[2], world_point[1]) hit_dist = self.distance(self.world_position, world_point) if hit_dist <= dist: if nP.name.endswith('.egg'): nP = nP.parent self.hit = Hit(hit=True) for e in scene.entities: if e == nP: # print('cast nP to Entity') self.hit.entity = e self.hit.point = point self.hit.world_point = world_point self.hit.distance = hit_dist normal = self.collision.get_surface_normal(self.collision.get_into_node_path().parent) self.hit.normal = (normal[0], normal[2], normal[1]) normal = self.collision.get_surface_normal(render) self.hit.world_normal = (normal[0], normal[2], normal[1]) return self.hit self.hit = Hit(hit=False) return self.hit
class RoamingRalphDemo(ShowBase): def __init__(self): # Set up the window, camera, etc. ShowBase.__init__(self) self.orbCollisionHandler = CollisionHandlerQueue() self.cTrav = CollisionTraverser() #hbPath = NodePath() utils3.setUpKeys(self) utils3.loadModels(self) utils3.setUpLighting(self) utils3.setUpFloatingSpheres(self) utils3.setUpRalphsShot(self) utils3.setUpCamera(self) self.healthTxt = utils3.addInstructions(.06,"Health: 100") self.orbTxt = utils3.addInstructions(.18,"Orbs: 0") self.vec = LVector3(0,1,0)#vector for pawns shot # Create a frame #frame = DirectFrame(text = "main", scale = 0.001) # Add button #bar = DirectWaitBar(text = "", value = 50, pos = (0,.4,.4)) #bar.reparent(render) # Game state variables self.isMoving = False self.jumping = False self.vz = 0 self.numOrbs = 0 self.healthCount = 100 #self.shotList = [] taskMgr.add(self.move, "moveTask") #taskMgr.add(utils2.moveChris,"moveChrisTask") self.sphere = CollisionSphere(0,0,4,2) self.sphere2 = CollisionSphere(0,0,2,2) self.cnodePath = self.ralph.attachNewNode((CollisionNode('ralphColNode'))) self.cnodePath.node().addSolid(self.sphere) self.cnodePath.node().addSolid(self.sphere2) #self.cnodePath.show() self.pusher = CollisionHandlerPusher() self.pusher.addCollider(self.cnodePath, self.ralph) #self.cTrav.addCollider(self.cnodePath, self.ralphCollisionHandler) self.cTrav.addCollider(self.cnodePath, self.pusher) ca = CollisionSphere(0,0,0,20) cb = self.chik.attachNewNode(CollisionNode('chikCollisionNode')) cb.node().addSolid(ca) cb.show() cc = CollisionSphere(3,5,12,25) cd = self.gianteye.attachNewNode(CollisionNode('gianteyeCollisionNode')) cd.node().addSolid(cc) cd.show() ci = CollisionSphere(0,0,0,2) coi = self.catidol.attachNewNode(CollisionNode('catidolCollisionNode')) coi.node().addSolid(ci) coi.show() chi = CollisionSphere(-1,3,3,3) chco = self.chris.attachNewNode(CollisionNode('chrisColPath')) chco.node().addSolid(chi) self.cTrav.addCollider(chco, self.orbCollisionHandler) #chco.show() self.chris.setH(90) self.chris.setR(-90) self.chris.setZ(2) #cbox = CollisionBox((-50,30,20),10,85,20) #cboxPath = self.room.attachNewNode(CollisionNode('roomSide1')) #cboxPath.node().addSolid(cbox) #cboxPath.show() #cbox2 = CollisionBox((200,30,20),10,85,20) #cboxPath2 = self.room.attachNewNode(CollisionNode('roomSide1')) #cboxPath2.node().addSolid(cbox2) #cboxPath2.show() #cbox3 = CollisionBox((80,-60,20),120,20,20) #cboxPath3 = self.room.attachNewNode(CollisionNode('roomSide1')) #cboxPath3.node().addSolid(cbox3) #cboxPath3.show() ct = CollisionSphere(0,0,0,1) cn = self.pawn.attachNewNode(CollisionNode('pawnCollisionNode')) cn.node().addSolid(ct) cn.show() cs2 = CollisionSphere(0,0,0,.2) cs2path = self.plnp.attachNewNode((CollisionNode('orbColPath'))) cs2path.node().addSolid(cs2) cs2path.show() self.cTrav.addCollider(cs2path, self.orbCollisionHandler) #cs3 = CollisionSphere(0,0,0,1) cs3path = self.plnp2.attachNewNode((CollisionNode('orbColPath'))) cs3path.node().addSolid(cs2) cs3path.show() chrisShotNp = self.chrisShot.attachNewNode((CollisionNode("enemyOrbColPath"))) chrisShotNp.node().addSolid(cs2) chrisShotNp.show() self.cTrav.addCollider(cs3path, self.orbCollisionHandler) self.cTrav.addCollider(chrisShotNp, self.orbCollisionHandler) # Uncomment this line to show a visual representation of the # collisions occuring self.cTrav.showCollisions(render) self.chrisLastShotTime = globalClock.getFrameTime() self.chrisTimer = globalClock.getDt() # Records the state of the arrow keys def setKey(self, key, value): self.keyMap[key] = value # Accepts arrow keys to move either the player or the menu cursor, # Also deals with grid checking and collision detection def move(self, task): # Get the time that elapsed since last frame. We multiply this with # the desired speed in order to find out with which distance to move # in order to achieve that desired speed. dt = globalClock.getDt() utils3.moveChris(self,dt) # If the camera-left key is pressed, move camera left. # If the camera-right key is pressed, move camera right. if self.keyMap["cam-left"]: self.camera.setZ(self.camera, -20 * dt) if self.keyMap["cam-right"]: self.camera.setZ(self.camera, +20 * dt) # save ralph's initial position so that we can restore it, # in case he falls off the map or runs into something. startpos = self.ralph.getPos() # If a move-key is pressed, move ralph in the specified direction. if self.keyMap["left"]: self.ralph.setH(self.ralph.getH() + 150 * dt) #self.camera.setX(self.camera, +15.5 * dt) if self.keyMap["right"]: self.ralph.setH(self.ralph.getH() - 150 * dt) #self.camera.setX(self.camera, -15.5 * dt) if self.keyMap["forward"]: self.ralph.setY(self.ralph, -35 * dt) #self.camera.setY(self.camera, -35 * dt) if self.keyMap["back"]: self.ralph.setY(self.ralph, +35 * dt) #self.camera.setY(self.camera, 35 * dt) if self.keyMap["c"]: if self.jumping is False: #self.ralph.setZ(self.ralph.getZ() + 100 * dt) self.jumping = True self.vz = 7 if self.keyMap["space"]: self.keyMap["space"] = False self.shotList[self.shotCount].lpivot.setPos(self.ralph.getPos()) self.shotList[self.shotCount].lpivot.setZ(self.ralph.getZ() + .5) self.shotList[self.shotCount].lpivot.setX(self.ralph.getX() - .25) #self.shotList.append(rShot) #self.lightpivot3.setPos(self.ralph.getPos()) #self.lightpivot3.setZ(self.ralph.getZ() + .5) #self.lightpivot3.setX(self.ralph.getX() - .25) #self.myShot.setHpr(self.ralph.getHpr()) #parent to ralph #node = NodePath("tmp") #node.setHpr(self.ralph.getHpr()) #vec = render.getRelativeVector(node,(0,-1,0)) #self.myShotVec = vec node = NodePath("tmp") node.setHpr(self.ralph.getHpr()) vec = render.getRelativeVector(node,(0,-1,0)) self.shotList[self.shotCount].vec = vec self.shotCount = (self.shotCount + 1) % 5 for rs in self.shotList: rs.lpivot.setPos(rs.lpivot.getPos() + rs.vec * dt * 15 ) #if shot is too far stop updating if self.jumping is True: self.vz = self.vz - 16* dt self.ralph.setZ(self.ralph.getZ() + self.vz * dt ) if self.ralph.getZ() < 0: self.ralph.setZ(0) self.jumping = False else: if self.ralph.getZ() < 0: self.ralph.setZ(0) elif self.ralph.getZ() > 0: self.ralph.setZ(self.ralph.getZ() -7 * dt) # If ralph is moving, loop the run animation. # If he is standing still, stop the animation. if self.keyMap["forward"] or self.keyMap["left"] or self.keyMap["right"] or self.keyMap["c"] or self.keyMap["forward"] or self.keyMap["back"]: if self.isMoving is False: self.ralph.loop("run") self.isMoving = True else: if self.isMoving: self.ralph.stop() self.ralph.pose("walk", 5) self.isMoving = False # update pawns shot or set up new shot after it reaches a certain distance node = NodePath("tmp") node.setHpr(self.pawn.getHpr()) vec = render.getRelativeVector(node,(random.random() * -0.8,random.random() + 1,0)) self.shot.setPos(self.shot.getPos() + self.vec * dt * 10 ) if self.shot.getY() < -15 or self.shot.getY() > 30 or self.shot.getX() < 5 or self.shot.getX() > 15: self.shot.setPos(self.pawn.getPos() + (0,0,0)) self.vec = render.getRelativeVector(node,(random.random() * -0.8,random.random() + 1,0)) self.vec = render.getRelativeVector(node,(random.random() * random.randrange(-1,2),random.random() + 1,0)) # If the camera is too far from ralph, move it closer. # If the camera is too close to ralph, move it farther. #self.camera.lookAt(self.floater) camvec = self.ralph.getPos() - self.camera.getPos() #camvec = Vec3(0,camvec.getY(),0) camdist = camvec.length() x = self.camera.getZ() camvec.normalize() #if camdist > 6.0: # self.camera.setPos(self.camera.getPos() + camvec * (camdist - 6)) #if camdist < 6.0: # self.camera.setPos(self.camera.getPos() - camvec * (6 - camdist)) # Normally, we would have to call traverse() to check for collisions. # However, the class ShowBase that we inherit from has a task to do # this for us, if we assign a CollisionTraverser to self.cTrav. #self.cTrav.traverse(render) # Adjust camera so it stays at same height if self.camera.getZ() < self.ralph.getZ() + 1 or self.camera.getZ() > self.ralph.getZ() + 1: self.camera.setZ(self.ralph.getZ() + 1) # The camera should look in ralph's direction, # but it should also try to stay horizontal, so look at # a floater which hovers above ralph's head. self.camera.lookAt(self.floater) entries = list(self.orbCollisionHandler.getEntries()) if(len(entries) > 0): #self.lightpivot.reparentTo(NodePath()) for entry in self.orbCollisionHandler.getEntries(): #print(entry) fromColNp = entry.getFromNodePath() toColNp = entry.getIntoNodePath() if fromColNp.getName() == "orbColPath" and toColNp.getName() == "ralphColNode": fromColNp.getParent().reparentTo(NodePath()) self.orbTxt.destroy() self.numOrbs += 1 str1 = "Orbs: " + str(self.numOrbs) self.orbTxt = utils3.addInstructions(.18, str1) elif toColNp.getName() == "orbColPath" and fromColNp.getName() == "ralphColNode": toColNp.getParent().reparentTo(NodePath()) self.orbTxt.destroy() self.numOrbs += 1 str1 = "Orbs: " + str(self.numOrbs) self.orbTxt = utils3.addInstructions(.18, str1) elif toColNp.getName() == "ralphOrbColPath" and fromColNp.getName() == "chrisColPath": toColNp.getParent().setPos(-50,0,2) self.chrisHealth = self.chrisHealth - 1 self.chris.setColor(1,0,0,1) self.chrisHit = True self.chrisRedTime = globalClock.getFrameTime() #print self.chrisRedTime if self.chrisHealth < 0: fromColNp.getParent().removeNode() self.chrisAlive = False elif toColNp.getName() == "chrisColPath" and fromColNp.getName() == "ralphOrbColPath": fromColNp.getParent().setPos(-50,0,2) self.chrisHealth = self.chrisHealth - 1 self.chris.setColor(1,0,0,1) self.chrisHit = True self.chrisRedTime = globalClock.getFrameTime() #print self.chrisRedTime if self.chrisHealth < 0: fromColNp.getParent().removeNode() self.chrisAlive = False self.chrisShot.setZ(26) elif toColNp.getName() == "enemyOrbColPath" and fromColNp.getName() == "ralphColNode": toColNp.getParent().setZ(26) self.healthTxt.destroy() self.healthCount -= 3 str1 = "Health: " + str(self.healthCount) self.healthTxt = utils3.addInstructions(.06, str1) elif toColNp.getName() == "ralphColNode" and fromColNp.getName() == "enemyOrbColPath": fromColNp.getParent().setZ(26) self.healthTxt.destroy() self.healthCount -= 3 str1 = "Health: " + str(self.healthCount) self.healthTxt = utils3.addInstructions(.06, str1) return task.cont
class RoamingRalphDemo(ShowBase): def __init__(self): # Set up the window, camera, etc. ShowBase.__init__(self) # Create and configure the VR environment self.ovr = P3DOpenVR() self.ovr.init(msaa=4) main_dir = ExecutionEnvironment.getEnvironmentVariable("MAIN_DIR") # Setup the application manifest, it will identify and configure the app # We force it in case it has changed. self.ovr.identify_application(os.path.join(main_dir, "ralph.vrmanifest"), "p3dopenvr.demo.ralph", force=True) # Load the actions manifest, it must be the same as the manifest referenced in the application manifest self.ovr.load_action_manifest( os.path.join(main_dir, "manifest/actions.json")) # Use the '/actions/platformer' action set. This action set will be updated each frame self.ovr.add_action_set("/actions/platformer") # Get the handle of the action '/actions/platformer/in/Move'. This hande will be used to retrieve the data of the action. self.action_move = self.ovr.vr_input.getActionHandle( '/actions/platformer/in/Move') # Set the background color to black self.win.setClearColor((0, 0, 0, 1)) # Post the instructions self.title = addTitle( "Panda3D Tutorial: Roaming Ralph (Walking on Uneven Terrain)") self.inst1 = addInstructions(0.06, "[ESC]: Quit") self.inst2 = addInstructions(0.12, "[Left trackpad]: Rotate Left") self.inst3 = addInstructions(0.18, "[Right trackpad]: Rotate Right") self.inst4 = addInstructions(0.24, "[Up trackpad]: Walk Forward") self.inst4 = addInstructions(0.30, "[Down trackpad]: Walk Backward") # 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/world") self.environ.reparentTo(render) # Create the main character, Ralph self.ralph = render.attachNewNode('ralph') self.ralphStartPos = self.environ.find("**/start_point").getPos() self.ovr.tracking_space.setPos(self.ralphStartPos) self.ralph.setPos(self.ovr.hmd_anchor.getPos(render)) self.accept("escape", sys.exit) taskMgr.add(self.move, "moveTask") taskMgr.add(self.collision, "collisionTask") # Set up the camera self.disableMouse() # 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, 9) self.ralphGroundRay.setDirection(0, 0, -1) self.ralphGroundCol = CollisionNode('ralphRay') self.ralphGroundCol.addSolid(self.ralphGroundRay) self.ralphGroundCol.setFromCollideMask(CollideMask.bit(0)) self.ralphGroundCol.setIntoCollideMask(CollideMask.allOff()) self.ralphGroundColNp = self.ralph.attachNewNode(self.ralphGroundCol) self.ralphGroundHandler = CollisionHandlerQueue() self.cTrav.addCollider(self.ralphGroundColNp, self.ralphGroundHandler) # Uncomment this line to see the collision rays #self.ralphGroundColNp.show() # Uncomment this line to show a visual representation of the # collisions occuring #self.cTrav.showCollisions(render) # Create some lighting ambientLight = AmbientLight("ambientLight") ambientLight.setColor((.3, .3, .3, 1)) directionalLight = DirectionalLight("directionalLight") directionalLight.setDirection((-5, -5, -5)) directionalLight.setColor((1, 1, 1, 1)) directionalLight.setSpecularColor((1, 1, 1, 1)) render.setLight(render.attachNewNode(ambientLight)) render.setLight(render.attachNewNode(directionalLight)) # Move camera according to user's input def move(self, task): # Get the time that elapsed since last frame. We multiply this with # the desired speed in order to find out with which distance to move # in order to achieve that desired speed. dt = globalClock.getDt() # If a move-button is touched, move in the specified direction. move_data, device_path = self.ovr.get_analog_action_value( self.action_move) if move_data is not None: x, y = move_data.x, move_data.y # The x coordinate is used to turn the camera self.ovr.tracking_space.setH(self.ovr.tracking_space.getH() - x * 60 * dt) # The y coordinate is used to move the camera along the view vector # We retrieve the orientation of the headset and we generate a 2D direction orientation = self.ovr.hmd_anchor.get_quat(render) vector = orientation.xform(LVector3(0, 1, 0)) vector[2] = 0 vector.normalize() # Use the vector and the x value to move the camera relative to itself self.ovr.tracking_space.setPos(self.ovr.tracking_space.getPos() + vector * (y * 5 * dt)) return task.cont # Grid checking and collision detection def collision(self, task): # Normally, we would have to call traverse() to check for collisions. # However, the class ShowBase that we inherit from has a task to do # this for us, if we assign a CollisionTraverser to self.cTrav. #self.cTrav.traverse(render) # Adjust ralph's Z coordinate. If ralph's ray hit terrain, # update his Z. If it hit anything else, or didn't hit anything, put # him back where he was last frame. entries = list(self.ralphGroundHandler.getEntries()) entries.sort(key=lambda x: x.getSurfacePoint(render).getZ()) if len(entries) > 0 and entries[0].getIntoNode().getName( ) == "terrain": self.ovr.tracking_space.setZ( entries[0].getSurfacePoint(render).getZ()) else: self.ovr.tracking_space.setPos(self.ralphStartPos) self.ralph.setPos(self.ovr.hmd_anchor.getPos(render)) # save ralph's initial position so that we can restore it, # in case he falls off the map or runs into something. self.ralphStartPos = self.ovr.tracking_space.getPos() return task.cont
class Entity(NodePath): rotation_directions = (-1,-1,1) default_shader = None def __init__(self, add_to_scene_entities=True, **kwargs): super().__init__(self.__class__.__name__) self.name = camel_to_snake(self.type) self.enabled = True # disabled entities wil not be visible nor run code self.visible = True self.ignore = False # if True, will not try to run code self.eternal = False # eternal entities does not get destroyed on scene.clear() self.ignore_paused = False self.ignore_input = False self.parent = scene self.add_to_scene_entities = add_to_scene_entities # set to False to be ignored by the engine, but still get rendered. if add_to_scene_entities: scene.entities.append(self) self.model = None # set model with model='model_name' (without file type extention) self.color = color.white self.texture = None # set model with texture='texture_name'. requires a model to be set beforehand. self.reflection_map = scene.reflection_map self.reflectivity = 0 self.render_queue = 0 self.double_sided = False self.shader = Entity.default_shader # self.always_on_top = False self.collision = False # toggle collision without changing collider. self.collider = None # set to 'box'/'sphere'/'mesh' for auto fitted collider. self.scripts = list() # add with add_script(class_instance). will assign an 'entity' variable to the script. self.animations = list() self.hovered = False # will return True if mouse hovers entity. self.origin = Vec3(0,0,0) self.position = Vec3(0,0,0) # right, up, forward. can also set self.x, self.y, self.z self.rotation = Vec3(0,0,0) # can also set self.rotation_x, self.rotation_y, self.rotation_z self.scale = Vec3(1,1,1) # can also set self.scale_x, self.scale_y, self.scale_z self.line_definition = None # returns a Traceback(filename, lineno, function, code_context, index). if application.trace_entity_definition and add_to_scene_entities: from inspect import getframeinfo, stack _stack = stack() caller = getframeinfo(_stack[1][0]) if len(_stack) > 2 and _stack[1].code_context and 'super().__init__()' in _stack[1].code_context[0]: caller = getframeinfo(_stack[2][0]) self.line_definition = caller if caller.code_context: self.code_context = caller.code_context[0] if (self.code_context.count('(') == self.code_context.count(')') and ' = ' in self.code_context and not 'name=' in self.code_context and not 'Ursina()' in self.code_context): self.name = self.code_context.split(' = ')[0].strip().replace('self.', '') # print('set name to:', self.code_context.split(' = ')[0].strip().replace('self.', '')) if application.print_entity_definition: print(f'{Path(caller.filename).name} -> {caller.lineno} -> {caller.code_context}') for key, value in kwargs.items(): setattr(self, key, value) def _list_to_vec(self, value): if isinstance(value, (int, float, complex)): return Vec3(value, value, value) if len(value) % 2 == 0: new_value = Vec2() for i in range(0, len(value), 2): new_value.add_x(value[i]) new_value.add_y(value[i+1]) if len(value) % 3 == 0: new_value = Vec3() for i in range(0, len(value), 3): new_value.add_x(value[i]) new_value.add_y(value[i+1]) new_value.add_z(value[i+2]) return new_value def enable(self): self.enabled = True def disable(self): self.enabled = False def __setattr__(self, name, value): if name == 'enabled': try: # try calling on_enable() on classes inheriting from Entity if value == True: self.on_enable() else: self.on_disable() except: pass if value == True: if hasattr(self, 'is_singleton') and not self.is_singleton(): self.unstash() else: if hasattr(self, 'is_singleton') and not self.is_singleton(): self.stash() if name == 'eternal': for c in self.children: c.eternal = value if name == 'world_parent': self.reparent_to(value) if name == 'model': if value is None: if hasattr(self, 'model') and self.model: self.model.removeNode() # print('removed model') object.__setattr__(self, name, value) return None if isinstance(value, NodePath): # pass procedural model if self.model is not None and value != self.model: self.model.removeNode() object.__setattr__(self, name, value) elif isinstance(value, str): # pass model asset name m = load_model(value, application.asset_folder) if not m: m = load_model(value, application.internal_models_compressed_folder) if m: if self.model is not None: self.model.removeNode() object.__setattr__(self, name, m) # if isinstance(m, Mesh): # m.recipe = value # print('loaded model successively') else: # if '.' in value: # print(f'''trying to load model with specific filename extention. please omit it. '{value}' -> '{value.split('.')[0]}' ''') print('missing model:', value) return if self.model: self.model.reparentTo(self) self.model.setTransparency(TransparencyAttrib.M_dual) self.color = self.color # reapply color after changing model self.texture = self.texture # reapply texture after changing model self._vert_cache = None if isinstance(value, Mesh): if hasattr(value, 'on_assign'): value.on_assign(assigned_to=self) return if name == 'color' and value is not None: if isinstance(value, str): value = color.hex(value) if not isinstance(value, Vec4): value = Vec4(value[0], value[1], value[2], value[3]) if self.model: self.model.setColorScaleOff() # prevent inheriting color from parent self.model.setColorScale(value) object.__setattr__(self, name, value) if name == 'collision' and hasattr(self, 'collider') and self.collider: if value: self.collider.node_path.unstash() else: self.collider.node_path.stash() object.__setattr__(self, name, value) return if name == 'render_queue': if self.model: self.model.setBin('fixed', value) if name == 'double_sided': self.setTwoSided(value) try: super().__setattr__(name, value) except: pass # print('failed to set attribiute:', name) @property def parent(self): try: return self._parent except: return None @parent.setter def parent(self, value): self._parent = value if value is None: destroy(self) else: try: self.reparentTo(value) except: print('invalid parent:', value) @property def type(self): # get class name. return self.__class__.__name__ @property def types(self): # get all class names including those this inhertits from. from inspect import getmro return [c.__name__ for c in getmro(self.__class__)] @property def visible(self): return self._visible @visible.setter def visible(self, value): self._visible = value if value: self.show() else: self.hide() @property def visible_self(self): # set visibility of self, without affecting children. if not hasattr(self, '_visible_self'): return True return self._visible_self @visible_self.setter def visible_self(self, value): self._visible_self = value if not self.model: return if value: self.model.show() else: self.model.hide() @property def collider(self): return self._collider @collider.setter def collider(self, value): # destroy existing collider if value and hasattr(self, 'collider') and self._collider: self._collider.remove() self._collider = value if value == 'box': if self.model: self._collider = BoxCollider(entity=self, center=-self.origin, size=self.model_bounds) else: self._collider = BoxCollider(entity=self) self._collider.name = value elif value == 'sphere': self._collider = SphereCollider(entity=self, center=-self.origin) self._collider.name = value elif value == 'mesh' and self.model: self._collider = MeshCollider(entity=self, mesh=None, center=-self.origin) self._collider.name = value elif isinstance(value, Mesh): self._collider = MeshCollider(entity=self, mesh=value, center=-self.origin) elif isinstance(value, str): m = load_model(value) if not m: return self._collider = MeshCollider(entity=self, mesh=m, center=-self.origin) self._collider.name = value self.collision = bool(self.collider) return @property def origin(self): return self._origin @origin.setter def origin(self, value): if not self.model: self._origin = Vec3(0,0,0) return if not isinstance(value, (Vec2, Vec3)): value = self._list_to_vec(value) if isinstance(value, Vec2): value = Vec3(*value, self.origin_z) self._origin = value self.model.setPos(-value[0], -value[1], -value[2]) @property def origin_x(self): return self.origin[0] @origin_x.setter def origin_x(self, value): self.origin = (value, self.origin_y, self.origin_z) @property def origin_y(self): return self.origin[1] @origin_y.setter def origin_y(self, value): self.origin = (self.origin_x, value, self.origin_z) @property def origin_z(self): return self.origin[2] @origin_z.setter def origin_z(self, value): self.origin = (self.origin_x, self.origin_y, value) @property def world_position(self): return Vec3(self.get_position(render)) @world_position.setter def world_position(self, value): if not isinstance(value, (Vec2, Vec3)): value = self._list_to_vec(value) if isinstance(value, Vec2): value = Vec3(*value, self.z) self.setPos(render, Vec3(value[0], value[1], value[2])) @property def world_x(self): return self.getX(render) @property def world_y(self): return self.getY(render) @property def world_z(self): return self.getZ(render) @world_x.setter def world_x(self, value): self.setX(render, value) @world_y.setter def world_y(self, value): self.setY(render, value) @world_z.setter def world_z(self, value): self.setZ(render, value) @property def position(self): return Vec3(*self.getPos()) @position.setter def position(self, value): if not isinstance(value, (Vec2, Vec3)): value = self._list_to_vec(value) if isinstance(value, Vec2): value = Vec3(*value, self.z) self.setPos(value[0], value[1], value[2]) @property def x(self): return self.getX() @x.setter def x(self, value): self.setX(value) @property def y(self): return self.getY() @y.setter def y(self, value): self.setY(value) @property def z(self): return self.getZ() @z.setter def z(self, value): self.setZ(value) @property def world_rotation(self): rotation = self.getHpr(base.render) return Vec3(rotation[1], rotation[0], rotation[2]) * Entity.rotation_directions @world_rotation.setter def world_rotation(self, value): rotation = self.setHpr(Vec3(value[1], value[0], value[2]) * Entity.rotation_directions, base.render) @property def world_rotation_x(self): return self.world_rotation[0] @world_rotation_x.setter def world_rotation_x(self, value): self.world_rotation = Vec3(value, self.world_rotation[1], self.world_rotation[2]) @property def world_rotation_y(self): return self.world_rotation[1] @world_rotation_y.setter def world_rotation_y(self, value): self.world_rotation = Vec3(self.world_rotation[0], value, self.world_rotation[2]) @property def world_rotation_z(self): return self.world_rotation[2] @world_rotation_z.setter def world_rotation_z(self, value): self.world_rotation = Vec3(self.world_rotation[0], self.world_rotation[1], value) @property def rotation(self): rotation = self.getHpr() return Vec3(rotation[1], rotation[0], rotation[2]) * Entity.rotation_directions @rotation.setter def rotation(self, value): if not isinstance(value, (Vec2, Vec3)): value = self._list_to_vec(value) if isinstance(value, Vec2): value = Vec3(*value, self.rotation_z) self.setHpr(Vec3(value[1], value[0], value[2]) * Entity.rotation_directions) @property def rotation_x(self): return self.rotation.x @rotation_x.setter def rotation_x(self, value): self.rotation = Vec3(value, self.rotation[1], self.rotation[2]) @property def rotation_y(self): return self.rotation.y @rotation_y.setter def rotation_y(self, value): self.rotation = Vec3(self.rotation[0], value, self.rotation[2]) @property def rotation_z(self): return self.rotation.z @rotation_z.setter def rotation_z(self, value): self.rotation = Vec3(self.rotation[0], self.rotation[1], value) @property def world_scale(self): return Vec3(*self.getScale(base.render)) @world_scale.setter def world_scale(self, value): if isinstance(value, (int, float, complex)): value = Vec3(value, value, value) self.setScale(base.render, value) @property def world_scale_x(self): return self.getScale(base.render)[0] @world_scale_x.setter def world_scale_x(self, value): self.setScale(base.render, Vec3(value, self.world_scale_y, self.world_scale_z)) @property def world_scale_y(self): return self.getScale(base.render)[1] @world_scale_y.setter def world_scale_y(self, value): self.setScale(base.render, Vec3(self.world_scale_x, value, self.world_scale_z)) @property def world_scale_z(self): return self.getScale(base.render)[2] @world_scale_z.setter def world_scale_z(self, value): self.setScale(base.render, Vec3(self.world_scale_x, value, self.world_scale_z)) @property def scale(self): scale = self.getScale() return Vec3(scale[0], scale[1], scale[2]) @scale.setter def scale(self, value): if not isinstance(value, (Vec2, Vec3)): value = self._list_to_vec(value) if isinstance(value, Vec2): value = Vec3(*value, self.scale_z) value = [e if e!=0 else .001 for e in value] self.setScale(value[0], value[1], value[2]) @property def scale_x(self): return self.scale[0] @scale_x.setter def scale_x(self, value): self.setScale(value, self.scale_y, self.scale_z) @property def scale_y(self): return self.scale[1] @scale_y.setter def scale_y(self, value): self.setScale(self.scale_x, value, self.scale_z) @property def scale_z(self): return self.scale[2] @scale_z.setter def scale_z(self, value): self.setScale(self.scale_x, self.scale_y, value) @property def forward(self): # get forward direction. return render.getRelativeVector(self, (0, 0, 1)) @property def back(self): # get backwards direction. return -self.forward @property def right(self): # get right direction. return render.getRelativeVector(self, (1, 0, 0)) @property def left(self): # get left direction. return -self.right @property def up(self): # get up direction. return render.getRelativeVector(self, (0, 1, 0)) @property def down(self): # get down direction. return -self.up @property def screen_position(self): # get screen position(ui space) from world space. from ursina import camera p3 = camera.getRelativePoint(self, Vec3.zero()) full = camera.lens.getProjectionMat().xform(Vec4(*p3, 1)) recip_full3 = 1 / full[3] p2 = Vec3(full[0], full[1], full[2]) * recip_full3 screen_pos = Vec3(p2[0]*camera.aspect_ratio/2, p2[1]/2, 0) return screen_pos @property def shader(self): return self._shader @shader.setter def shader(self, value): self._shader = value if value is None: self.setShaderAuto() return if isinstance(value, Panda3dShader): #panda3d shader self.setShader(value) return if isinstance(value, Shader): if not value.compiled: value.compile() self.setShader(value._shader) value.entity = self for key, value in value.default_input.items(): self.set_shader_input(key, value) def set_shader_input(self, name, value): if isinstance(value, Texture): value = value._texture # make sure to send the panda3d texture to the shader super().set_shader_input(name, value) @property def texture(self): if not hasattr(self, '_texture'): return None return self._texture @texture.setter def texture(self, value): if value is None and self._texture: # print('remove texture') self._texture = None self.setTextureOff(True) return if value.__class__ is Texture: texture = value elif isinstance(value, str): texture = load_texture(value) # print('loaded texture:', texture) if texture is None: print('no texture:', value) return if texture.__class__ is MovieTexture: self._texture = texture self.model.setTexture(texture, 1) return self._texture = texture if self.model: self.model.setTexture(texture._texture, 1) @property def texture_scale(self): if not hasattr(self, '_texture_scale'): return Vec2(1,1) return self._texture_scale @texture_scale.setter def texture_scale(self, value): self._texture_scale = value if self.model and self.texture: self.model.setTexScale(TextureStage.getDefault(), value[0], value[1]) @property def texture_offset(self): return self._texture_offset @texture_offset.setter def texture_offset(self, value): if self.model and self.texture: self.model.setTexOffset(TextureStage.getDefault(), value[0], value[1]) self.texture = self.texture self._texture_offset = value @property def alpha(self): return self.color[3] @alpha.setter def alpha(self, value): if value > 1: value = value / 255 self.color = color.color(self.color.h, self.color.s, self.color.v, value) @property def always_on_top(self): return self._always_on_top @always_on_top.setter def always_on_top(self, value): self._always_on_top = value self.set_bin("fixed", 0) self.set_depth_write(not value) self.set_depth_test(not value) @property def billboard(self): # set to True to make this Entity always face the camera. return self._billboard @billboard.setter def billboard(self, value): self._billboard = value if value: self.setBillboardPointEye(value) @property def reflection_map(self): return self._reflection_map @reflection_map.setter def reflection_map(self, value): if value.__class__ is Texture: texture = value elif isinstance(value, str): texture = load_texture(value) self._reflection_map = texture @property def reflectivity(self): return self._reflectivity @reflectivity.setter def reflectivity(self, value): self._reflectivity = value if value == 0: self.texture = None if value > 0: # if self.reflection_map == None: # self.reflection_map = scene.reflection_map # # if not self.reflection_map: # print('error setting reflectivity. no reflection map') # return if not self.normals: self.model.generate_normals() # ts = TextureStage('env') # ts.setMode(TextureStage.MAdd) # self.model.setTexGen(ts, TexGenAttrib.MEyeSphereMap) # print('---------------set reflectivity', self.reflection_map) # self.model.setTexture(ts, self.reflection_map) self.texture = self._reflection_map # print('set reflectivity') def generate_sphere_map(self, size=512, name=f'sphere_map_{len(scene.entities)}'): from ursina import camera _name = 'textures/' + name + '.jpg' org_pos = camera.position camera.position = self.position base.saveSphereMap(_name, size=size) camera.position = org_pos print('saved sphere map:', name) self.model.setTexGen(TextureStage.getDefault(), TexGenAttrib.MEyeSphereMap) self.reflection_map = name def generate_cube_map(self, size=512, name=f'cube_map_{len(scene.entities)}'): from ursina import camera _name = 'textures/' + name org_pos = camera.position camera.position = self.position base.saveCubeMap(_name+'.jpg', size=size) camera.position = org_pos print('saved cube map:', name + '.jpg') self.model.setTexGen(TextureStage.getDefault(), TexGenAttrib.MWorldCubeMap) self.reflection_map = _name + '#.jpg' self.model.setTexture(loader.loadCubeMap(_name + '#.jpg'), 1) @property def model_bounds(self): if self.model: bounds = self.model.getTightBounds() bounds = Vec3( Vec3(bounds[1][0], bounds[1][1], bounds[1][2]) # max point - Vec3(bounds[0][0], bounds[0][1], bounds[0][2]) # min point ) return bounds return (0,0,0) @property def bounds(self): return Vec3( self.model_bounds[0] * self.scale_x, self.model_bounds[1] * self.scale_y, self.model_bounds[2] * self.scale_z ) def reparent_to(self, entity): if entity is not None: self.wrtReparentTo(entity) self._parent = entity def get_position(self, relative_to=scene): return self.getPos(relative_to) def set_position(self, value, relative_to=scene): self.setPos(relative_to, Vec3(value[0], value[1], value[2])) def add_script(self, class_instance): if isinstance(class_instance, object) and type(class_instance) is not str: class_instance.entity = self class_instance.enabled = True setattr(self, camel_to_snake(class_instance.__class__.__name__), class_instance) self.scripts.append(class_instance) # print('added script:', camel_to_snake(name.__class__.__name__)) return class_instance def combine(self, analyze=False, auto_destroy=True, ignore=[]): from ursina.scripts.combine import combine self.model = combine(self, analyze, auto_destroy, ignore) return self.model def flip_faces(self): if not hasattr(self, '_vertex_order'): self._vertex_order = True self._vertex_order = not self._vertex_order if self._vertex_order: self.setAttrib(CullFaceAttrib.make(CullFaceAttrib.MCullClockwise)) else: self.setAttrib(CullFaceAttrib.make(CullFaceAttrib.MCullCounterClockwise)) def look_at(self, target, axis='forward'): from panda3d.core import Quat if not isinstance(target, Entity): target = Vec3(*target) self.lookAt(target) if axis == 'forward': return rotation_offset = { 'back' : Quat(0,0,1,0), 'down' : Quat(-.707,.707,0,0), 'up' : Quat(-.707,-.707,0,0), 'right' : Quat(-.707,0,.707,0), 'left' : Quat(-.707,0,-.707,0), }[axis] self.setQuat(rotation_offset * self.getQuat()) def look_at_2d(self, target, axis='z'): from math import degrees, atan2 if isinstance(target, Entity): target = Vec3(target.world_position) pos = target - self.world_position if axis == 'z': self.rotation_z = degrees(atan2(pos[0], pos[1])) def has_ancestor(self, possible_ancestor): p = self if isinstance(possible_ancestor, Entity): # print('ENTITY') for i in range(100): if p.parent: if p.parent == possible_ancestor: return True p = p.parent if isinstance(possible_ancestor, list) or isinstance(possible_ancestor, tuple): # print('LIST OR TUPLE') for e in possible_ancestor: for i in range(100): if p.parent: if p.parent == e: return True break p = p.parent elif isinstance(possible_ancestor, str): print('CLASS NAME', possible_ancestor) for i in range(100): if p.parent: if p.parent.__class__.__name__ == possible_ancestor: return True break p = p.parent return False @property def children(self): return [e for e in scene.entities if e.parent == self] @property def attributes(self): # attribute names. used by duplicate() for instance. return ('name', 'enabled', 'eternal', 'visible', 'parent', 'origin', 'position', 'rotation', 'scale', 'model', 'color', 'texture', 'texture_scale', 'texture_offset', # 'world_position', 'world_x', 'world_y', 'world_z', # 'world_rotation', 'world_rotation_x', 'world_rotation_y', 'world_rotation_z', # 'world_scale', 'world_scale_x', 'world_scale_y', 'world_scale_z', # 'x', 'y', 'z', # 'origin_x', 'origin_y', 'origin_z', # 'rotation_x', 'rotation_y', 'rotation_z', # 'scale_x', 'scale_y', 'scale_z', 'render_queue', 'always_on_top', 'collision', 'collider', 'scripts') #------------ # ANIMATIONS #------------ def animate(self, name, value, duration=.1, delay=0, curve=curve.in_expo, loop=False, resolution=None, interrupt='kill', time_step=None, auto_destroy=True): animator_name = name + '_animator' # print('start animating value:', name, animator_name ) if interrupt and hasattr(self, animator_name): getattr(getattr(self, animator_name), interrupt)() # call kill() or finish() depending on what the interrupt value is. # print('interrupt', interrupt, animator_name) sequence = Sequence(loop=loop, time_step=time_step, auto_destroy=auto_destroy) setattr(self, animator_name, sequence) self.animations.append(sequence) sequence.append(Wait(delay)) if not resolution: resolution = max(int(duration * 60), 1) for i in range(resolution+1): t = i / resolution t = curve(t) sequence.append(Wait(duration / resolution)) sequence.append(Func(setattr, self, name, lerp(getattr(self, name), value, t))) sequence.start() return sequence def animate_position(self, value, duration=.1, **kwargs): x = self.animate('x', value[0], duration, **kwargs) y = self.animate('y', value[1], duration, **kwargs) z = None if len(value) > 2: z = self.animate('z', value[2], duration, **kwargs) return x, y, z def animate_rotation(self, value, duration=.1, **kwargs): x = self.animate('rotation_x', value[0], duration, **kwargs) y = self.animate('rotation_y', value[1], duration, **kwargs) z = self.animate('rotation_z', value[2], duration, **kwargs) return x, y, z def animate_scale(self, value, duration=.1, **kwargs): if isinstance(value, (int, float, complex)): value = Vec3(value, value, value) return self.animate('scale', value, duration, **kwargs) # generate animation functions for e in ('x', 'y', 'z', 'rotation_x', 'rotation_y', 'rotation_z', 'scale_x', 'scale_y', 'scale_z'): exec(dedent(f''' def animate_{e}(self, value, duration=.1, delay=0, **kwargs): return self.animate('{e}', value, duration=duration, delay=delay, **kwargs) ''')) def shake(self, duration=.2, magnitude=1, speed=.05, direction=(1,1)): import random s = Sequence() original_position = self.position for i in range(int(duration / speed)): s.append(Func(self.set_position, Vec3( original_position[0] + (random.uniform(-.1, .1) * magnitude * direction[0]), original_position[1] + (random.uniform(-.1, .1) * magnitude * direction[1]), original_position[2], ))) s.append(Wait(speed)) s.append(Func(self.set_position, original_position)) s.start() return s def animate_color(self, value, duration=.1, interrupt='finish', **kwargs): return self.animate('color', value, duration, interrupt=interrupt, **kwargs) def fade_out(self, value=0, duration=.5, **kwargs): return self.animate('color', Vec4(self.color[0], self.color[1], self.color[2], value), duration, **kwargs) def fade_in(self, value=1, duration=.5, **kwargs): return self.animate('color', Vec4(self.color[0], self.color[1], self.color[2], value), duration, **kwargs) def blink(self, value=color.clear, duration=.1, delay=0, curve=curve.in_expo_boomerang, interrupt='finish', **kwargs): return self.animate_color(value, duration=duration, delay=delay, curve=curve, interrupt=interrupt, **kwargs) def intersects(self, traverse_target=scene, ignore=(), debug=False): from ursina.hit_info import HitInfo if not self.collision or not self.collider: self.hit = HitInfo(hit=False) return self.hit from ursina import distance if not hasattr(self, '_picker'): from panda3d.core import CollisionTraverser, CollisionNode, CollisionHandlerQueue from panda3d.core import CollisionRay, CollisionSegment, CollisionBox self._picker = CollisionTraverser() # Make a traverser self._pq = CollisionHandlerQueue() # Make a handler self._pickerNode = CollisionNode('raycaster') self._pickerNode.set_into_collide_mask(0) self._pickerNP = self.attach_new_node(self._pickerNode) self._picker.addCollider(self._pickerNP, self._pq) self._pickerNP.show() self._pickerNode.addSolid(self._collider.shape) if debug: self._pickerNP.show() else: self._pickerNP.hide() self._picker.traverse(traverse_target) if self._pq.get_num_entries() == 0: self.hit = HitInfo(hit=False) return self.hit ignore += (self, ) ignore += tuple([e for e in scene.entities if not e.collision]) self._pq.sort_entries() self.entries = [ # filter out ignored entities e for e in self._pq.getEntries() if e.get_into_node_path().parent not in ignore ] if len(self.entries) == 0: self.hit = HitInfo(hit=False, distance=0) return self.hit collision = self.entries[0] nP = collision.get_into_node_path().parent point = collision.get_surface_point(nP) point = Vec3(*point) world_point = collision.get_surface_point(render) world_point = Vec3(*world_point) hit_dist = distance(self.world_position, world_point) self.hit = HitInfo(hit=True) self.hit.entity = next(e for e in scene.entities if e == nP) self.hit.point = point self.hit.world_point = world_point self.hit.distance = hit_dist normal = collision.get_surface_normal(collision.get_into_node_path().parent).normalized() self.hit.normal = Vec3(*normal) normal = collision.get_surface_normal(render).normalized() self.hit.world_normal = Vec3(*normal) self.hit.entities = [] for collision in self.entries: self.hit.entities.append(next(e for e in scene.entities if e == collision.get_into_node_path().parent)) return self.hit
class App(ShowBase): def __init__(self): ShowBase.__init__(self) self.actorBullet = 0 self.keyMap = { "cam-up": False, "cam-down": False, "cam-left": False, "cam-right": False, "up": False, "down": False, "left": False, "right": False, "shoot": False } #load environment model self.environ = loader.loadModel("models/world") self.environ.reparentTo(render) #load actor actorStartPos = self.environ.find("**/start_point").getPos() self.actor = Actor("models/ralph", { "run": "models/ralph-run", "walk": "models/ralph-walk" }) self.actor.reparentTo(render) self.actor.setScale(.2) self.actor.setPos(actorStartPos + (0, 0, 0)) #load bullets for bullets in range(self.actorBullet): print("making bullet") bullet = loader.loadModel("models/smiley") bullet.reparentTo(render) bullet.setPos(self.actor.getPos()) #load floater which is the point of focus for the camera self.floater = NodePath(PandaNode("floater")) self.floater.reparentTo(self.actor) self.floater.setZ(2.0) self.disableMouse() # base.useDrive() self.camera.setPos(self.actor.getX(), self.actor.getY() + 10, 2.0) self.camera.lookAt(self.floater) #event handlers self.accept("arrow_up", self.setKey, ["cam-up", True]) self.accept("arrow_up-up", self.setKey, ["cam-up", False]) self.accept("arrow_down", self.setKey, ["cam-down", True]) self.accept("arrow_down-up", self.setKey, ["cam-down", False]) self.accept("arrow_left", self.setKey, ["cam-left", True]) self.accept("arrow_left-up", self.setKey, ["cam-left", False]) self.accept("arrow_right", self.setKey, ["cam-right", True]) self.accept("arrow_right-up", self.setKey, ["cam-right", False]) self.accept("w", self.setKey, ["up", True]) self.accept("w-up", self.setKey, ["up", False]) self.accept("a", self.setKey, ["left", True]) self.accept("a-up", self.setKey, ["left", False]) self.accept("s", self.setKey, ["down", True]) self.accept("s-up", self.setKey, ["down", False]) self.accept("d", self.setKey, ["right", True]) self.accept("d-up", self.setKey, ["right", False]) self.accept("space", self.setKey, ["shoot", True]) self.accept("space-up", self.setKey, ["shoot", False]) taskMgr.add(self.moveCamera, "moveCamera") self.isMoving = False #create collision traverser which handles all the collision stuff self.Traverser = CollisionTraverser() #create solid self.actorGroundRay = CollisionRay() #set origin and direction of the collision ray self.actorGroundRay.setOrigin(0, 0, 9) self.actorGroundRay.setDirection(0, 0, -1) #create the collision node self.actorGroundCol = CollisionNode('ralphRay') #attach solid to node self.actorGroundCol.addSolid(self.actorGroundRay) #attach node to actor. Create the node path self.actorGroundColNp = self.actor.attachNewNode(self.actorGroundCol) #create queue self.actorGroundHandler = CollisionHandlerQueue() #add nodepath and queue to traverser self.Traverser.addCollider(self.actorGroundColNp, self.actorGroundHandler) #show collision node # self.actorGroundColNp.show() def setKey(self, key, val): print(key, val) self.keyMap[key] = val def moveCamera(self, task): dt = globalClock.getDt() startPos = self.actor.getPos() #camera control if self.keyMap["cam-up"]: self.camera.setZ(self.camera, 20 * dt) if self.keyMap["cam-down"]: self.camera.setZ(self.camera, -20 * dt) if self.keyMap["cam-left"]: self.camera.setH(self.camera.getH() + 20 * dt) if self.keyMap["cam-right"]: self.camera.setH(self.camera.getH() - 20 * dt) print(self.camera.getH()) #character control if self.keyMap["up"]: self.actor.setY(self.actor, -20 * dt) if self.keyMap["down"]: self.actor.setY(self.actor, +20 * dt) if self.keyMap["left"]: self.actor.setH(self.actor.getH() + 300 * dt) if self.keyMap["right"]: self.actor.setH(self.actor.getH() - 300 * dt) if self.keyMap["shoot"]: self.actorBullet += 1 #make the animation of ralph running if self.keyMap["up"] or self.keyMap["down"] or self.keyMap["right"]: if self.isMoving == False: self.actor.loop("run") self.isMoving = True else: if self.isMoving: self.actor.stop() self.actor.pose("walk", 5) self.isMoving = False #control the camera so that it is always a distance away from ralph camvec = self.actor.getPos() - self.camera.getPos() camvec.setZ(0) camdist = camvec.length() camvec.normalize() if camdist > 10.0: self.camera.setPos(self.camera.getPos() + camvec * (camdist - 10)) camdist = 10.0 if camdist < 5.0: self.camera.setPos(self.camera.getPos() - camvec * (5 - camdist)) camdist = 5.0 self.Traverser.traverse(render) actorEntries = list(self.actorGroundHandler.getEntries()) actorEntries.sort(key=lambda x: x.getSurfacePoint(render).getZ()) if len(actorEntries) > 0 and actorEntries[0].getIntoNode().getName( ) == "terrain": self.actor.setZ(actorEntries[0].getSurfacePoint(render).getZ()) else: self.actor.setPos(startPos) self.camera.lookAt(self.floater) return task.cont
class RoamingRalphDemo(ShowBase): def __init__(self): # Set up the window, camera, etc. ShowBase.__init__(self) self.orbCollisionHandler = CollisionHandlerQueue() self.cTrav = CollisionTraverser() self.cTrav.setRespectPrevTransform(True) self.startgame = False self.sound=loader.loadSfx("models/0614.ogg") self.sound2=loader.loadSfx("models/01-main-theme.mp3") self.sound2.play() status=self.sound2.status() #hbPath = NodePath() utils.setUpKeys(self) utils.loadModels(self) utils.setUpLighting(self) #utils.setUpFloatingSpheres(self) utils.setUpRalphsShot(self) utils.setUpCamera(self) utils.setUpCollisionSpheres(self) self.healthTxt = utils.addInstructions(.06,"Health: 100") self.orbTxt = utils.addInstructions(.18,"Orbs: 0") self.hitsTxt = utils.addInstructions(.28,"Enemy Hits: 0") self.strHealthStatus = str(self.healthTxt) # Create a frame frame = DirectFrame(text = "main", scale = 0.001) # Add button self.flagstartbutton = 0 self.imageObject = OnscreenImage(image = 'models/instapage.jpg', pos = (0, 0, 0), scale=1.1) self.imageObject2 = OnscreenImage(image = 'models/gap.jpg', pos = (-2.15, 0, 0), scale=1.1) self.imageObject3 = OnscreenImage(image = 'models/gap.jpg', pos = (2.15, 0, 0), scale=1.1) self.helpOn = DirectButton(text = ("Start", "on/off", "Start", "disabled"), scale=.10, pos=(-1.1,0,-.9), command=utils.buttonClickedOn, extraArgs=[self, self.imageObject,self.imageObject2,self.imageObject3, self.flagstartbutton]) #helpOff = DirectButton(text = ("helpOff", "on/off", "helpOff", "disabled"), scale=.10, pos=(-0.5,0,-1), command=utils.buttonClickedOff, extraArgs=[self, self.imageObject, self.buttonflag]) # mytimer = DirectLabel() # mytimer.reparentTo(render) # mytimer.setY(7) #Create 4 buttons #print self.strHealthStatus #incBar(100) self.vec = LVector3(0,1,0)#vector for pawns shot # Create a frame #frame = DirectFrame(text = "main", scale = 0.001) # Add button #bar = DirectWaitBar(text = "", value = 50, pos = (0,.4,.4)) #bar.reparent(render) # Game state variables self.isMoving = False self.jumping = False self.vz = 0 self.numOrbs = 0 self.healthCount = 100 self.enemyhits = 0 #self.shotList = [] #self.sphere = CollisionBox((self.ralph.getX() + -10,self.ralph.getY(),self.ralph.getZ()),10,10,10) self.ralphBox1 = CollisionBox((0,2.5,3.5),1.5,0.5,1.5) cnodepath = self.ralph.attachNewNode((CollisionNode("ralphColNode"))) cnodepath.node().addSolid(self.ralphBox1) cnodepath.node().addSolid(CollisionBox((0,-2.5,3.5),1.5,0.5,1.5)) cnodepath.node().addSolid(CollisionBox((2.5,0,3.5),0.5,1.5,1.5)) cnodepath.node().addSolid(CollisionBox((-2.5,0,3.5),0.5,1.5,1.5)) #cnodepath.show() #self.cTrav.addCollider(cnodepath, self.orbCollisionHandler) self.sphere = CollisionSphere(0,-5,4,3) self.sphere3 = CollisionSphere(0,5,5,3) self.sphere4 = CollisionSphere(-4,0,5,2) self.sphere5 = CollisionSphere(4,0,5,2) self.sphere2 = CollisionSphere(0,0,3,2) self.cnodePath = self.ralph.attachNewNode((CollisionNode("ralphColNode"))) self.cnodePath2 = self.ralph.attachNewNode((CollisionNode("ralphWallCheck"))) self.cnodePath3 = self.ralph.attachNewNode((CollisionNode("ralphWallCheck2"))) self.cnodePath4 = self.ralph.attachNewNode((CollisionNode("ralphWallCheck3"))) self.cnodePath5 = self.ralph.attachNewNode((CollisionNode("ralphWallCheck4"))) self.cnodePath.node().addSolid(self.sphere2) self.cnodePath2.node().addSolid(self.sphere) self.cnodePath3.node().addSolid(self.sphere3) self.cnodePath4.node().addSolid(self.sphere4) self.cnodePath5.node().addSolid(self.sphere5) #self.cnodePath.node().addSolid(self.sphere2) #self.cnodePath.show()#ralph pusher #self.cnodePath2.show() #self.cnodePath3.show() #self.cnodePath4.show() #self.cnodePath5.show() self.cTrav.addCollider(self.cnodePath2, self.orbCollisionHandler) self.cTrav.addCollider(self.cnodePath3, self.orbCollisionHandler) self.cTrav.addCollider(self.cnodePath4, self.orbCollisionHandler) self.cTrav.addCollider(self.cnodePath5, self.orbCollisionHandler) self.pusher = CollisionHandlerPusher() self.pusher.addCollider(self.cnodePath, self.ralph) #self.cTrav.addCollider(self.cnodePath, self.ralphCollisionHandler) self.cTrav.addCollider(self.cnodePath, self.pusher) self.chrisLastShotTime = globalClock.getFrameTime() self.chrisTimer = globalClock.getDt() #def __init__(self, pos,showbase, colPathName, dir, length): self.chrisList = [utils.cheken((-249,419,0),self,"chrisColPath0","X",5), #earthroom utils.chris((-404,343,2),self,"chrisColPath1","X",5), #yellowroom utils.fetus((-141,-69,1),self,"chrisColPath2","X",5), #lightblueroom utils.cheken((-277,356,0),self,"chrisColPath3","Y",5), #between earth and y utils.rose((-102,-5,1),self,"chrisColPath4","Y",5), #between r and lb utils.cheken((-133,83,0),self,"chrisColPath5","Y",5), #blue hall utils.fetus((-246,280,1),self,"chrisColPath6","X",5), #earth hall utils.cheken((-330,241,0),self,"chrisColPath7","X",5), #yellow hall utils.chris((-60,110,2),self,"chrisColPath8","Y",5), #red hall cheken z 0 utils.fetus((-75,52,1),self, "chrisColPath9", "X", 5), utils.cheken((-75,141,0),self, "chrisColPath10", "X", 5), utils.rose((-302,202,1),self,"chrisColPath11","X",5), utils.chris((-303,304,2),self,"chrisColPath12","Y",5) ] #rose z = 1 #cheken z = 0 #chris z = 2 #fetus z = 1 #def _init_(self,showbase,pos,color,speed,radius): self.orbList = [utils.orb(self,( 18, 29,2.5),(1,0,0,1),20,2.5), #first red utils.orb(self,( -249, 419,2.5),(1,1,1,1),20,2.5),#earthroom utils.orb(self,( -404, 343,2.5),(1,1,0,1),20,2.5), #yellowroom utils.orb(self,( -141, -69,2.5),(0,0,1,1),20,2.5),#light blue room utils.orb(self,( -277, 356,2.5),(1,1,0,1),20,2.5), #between y and earth utils.orb(self,( -102, -5,2.5),(0,0,1,1),20,2.5), #between red and lb utils.orb(self,( -135, 22,2.5),(0,0,1,1),20,2.5), #lb hall utils.orb(self,( -248, 329,2.5),(1,1,1,1),20,2.5), #earthhall utils.orb(self,( -330, 241,2.5),(1,1,0,1),20,2.5), #yellow hall utils.orb(self,( -60, 110,2.5),(1,0,0,1),20,2.5) #red hall ] self.donutList = [utils.donut(self, (0,0,1),20, 2.5), utils.donut(self,( -330, 250,2.5),20,2.5), #yellow hall utils.donut(self,( -141, -80,2.5),20,2.5),#light blue room utils.donut(self,( -249, 430,2.5),20,2.5),#earthroom utils.donut(self,( -102, -10,2.5),20,2.5), #between red and lb ] self.cameraCollided = False self.ralphSpeed = 60 self.ralphHit = False self.ralphRedTime = 0 self.textTime = -1 self.textTime2 = -1 self.textTime3 = -1 self.mTextPath = utils.addInstructions2(.44,"") self.mTextPath2 = utils.addInstructions2(.55,"") self.winText2 = utils.addInstructions2(.55, "") self.timerText = utils.addInstructions4(.26,"0:00") self.introText = utils.addInstructions2(.55,"") self.minutes = 4 self.seconds = 0 self.timerTime = globalClock.getFrameTime() taskMgr.add(self.move, "moveTask") taskMgr.add(self.moveChris,"moveChrisTask") taskMgr.add(self.timerTask,"timerTask") #taskMgr.add(self.timerTask, "timerTask") # Records the state of the arrow keys def setKey(self, key, value): self.keyMap[key] = value def clickResponse(): pass #startgame=1; def timerTask(self,task): if self.startgame == False: return task.cont dt = globalClock.getFrameTime() if dt - self.timerTime > 1: self.seconds -= 1 if self.seconds == -1: self.seconds = 59 self.minutes -=1 self.timerText.destroy() if self.seconds < 10: str1 = "0" + str(self.minutes) + ":0" + str(self.seconds) else: str1 = "0" + str(self.minutes) + ":" + str(self.seconds) self.timerText = utils.addInstructions4(.26,str1) self.timerTime = globalClock.getFrameTime() - ((dt - self.timerTime) - 1) if self.minutes == 0 and self.seconds == 0: self.startgame = False #utils.addInstructions3(.45,"You Lose") self.imageObject2 = OnscreenImage(image = 'models/gameover.jpg', pos = (0, 0, 0), scale=1.1) self.imageObject2 = OnscreenImage(image = 'models/gap.jpg', pos = (-2.15, 0, 0), scale=1.1) self.imageObject3 = OnscreenImage(image = 'models/gap.jpg', pos = (2.15, 0, 0), scale=1.1) return task.cont def moveChris(self,task): if self.startgame == False: return task.cont else: dt = globalClock.getDt() self.gianteye.setH(self.gianteye.getH() + 100 * dt) for chris in self.chrisList: chris.moveChris(dt,self,self.chrisList) return task.cont # Accepts arrow keys to move either the player or the menu cursor, # Also deals with grid checking and collision detection def move(self, task): if self.sound2.status() != self.sound2.PLAYING: self.sound2.play() if self.startgame == False: return task.cont else: # Get the time that elapsed since last frame. We multiply this with # the desired speed in order to find out with which distance to move # in order to achieve that desired speed. dt = globalClock.getDt() dt2 = globalClock.getFrameTime() #utils.moveChris(self,dt) #self.chris2.moveChris(dt,self) #self.startEnemyThread() if dt2 - self.textTime > 2 and self.textTime != -1: self.textTime = -1; self.mTextPath.destroy() if dt2 - self.textTime2 > 2 and self.textTime2 != -1: self.textTime2 = -1; self.mTextPath2.destroy() if dt2 - self.textTime3 > 5 and self.textTime3 != -1: self.textTime3 = -1; self.introText.destroy() if globalClock.getFrameTime()- self.ralphRedTime > .3 and self.ralphHit == True: self.ralph.clearColor() self.ralphHit = False # If the camera-left key is pressed, move camera left. # If the camera-right key is pressed, move camera right. if self.keyMap["cam-left"]: self.camera.setZ(self.camera, -20 * dt) if self.keyMap["cam-right"]: self.camera.setZ(self.camera, +20 * dt) # save ralph's initial position so that we can restore it, # in case he falls off the map or runs into something. startpos = self.ralph.getPos() # If a move-key is pressed, move ralph in the specified direction. if self.keyMap["left"]: self.ralph.setH(self.ralph.getH() + 75 * dt) #self.camera.setX(self.camera, +15.5 * dt) if self.keyMap["right"]: self.ralph.setH(self.ralph.getH() - 75 * dt) #self.camera.setX(self.camera, -15.5 * dt) if self.keyMap["forward"]:#-1 self.ralph.setFluidY(self.ralph, -1*self.ralphSpeed * dt) #self.camera.setY(self.camera, -35 * dt) if self.keyMap["back"]: self.ralph.setFluidY(self.ralph, self.ralphSpeed * dt) #self.camera.setY(self.camera, 35 * dt) if self.keyMap["space"]: if self.jumping is False: #self.ralph.setZ(self.ralph.getZ() + 100 * dt) self.jumping = True self.vz = 8 if self.keyMap["enter"]: self.keyMap["enter"] = False self.sound.play() self.shotList[self.shotCount].lpivot.setPos(self.ralph.getPos()) self.shotList[self.shotCount].lpivot.setZ(self.ralph.getZ() + .5) self.shotList[self.shotCount].lpivot.setX(self.ralph.getX() - .25) print self.ralph.getPos() #self.shotList.append(rShot) #self.lightpivot3.setPos(self.ralph.getPos()) #self.lightpivot3.setZ(self.ralph.getZ() + .5) #self.lightpivot3.setX(self.ralph.getX() - .25) #self.myShot.setHpr(self.ralph.getHpr()) #parent to ralph #node = NodePath("tmp") #node.setHpr(self.ralph.getHpr()) #vec = render.getRelativeVector(node,(0,-1,0)) #self.myShotVec = vec node = NodePath("tmp") node.setHpr(self.ralph.getHpr()) vec = render.getRelativeVector(node,(0,-1,0)) self.shotList[self.shotCount].vec = vec self.shotCount = (self.shotCount + 1) % 10 else: self.sound.stop() for rs in self.shotList: rs.lpivot.setPos(rs.lpivot.getPos() + rs.vec * dt * 25 ) #if shot is too far stop updating if self.jumping is True: self.vz = self.vz - 16* dt self.ralph.setZ(self.ralph.getZ() + self.vz * dt ) if self.ralph.getZ() < 0: self.ralph.setZ(0) self.jumping = False else: if self.ralph.getZ() < 0.25: self.ralph.setZ(0.25) elif self.ralph.getZ() > 0.25: self.ralph.setZ(self.ralph.getZ() -7 * dt) # If ralph is moving, loop the run animation. # If he is standing still, stop the animation. if self.keyMap["forward"] or self.keyMap["left"] or self.keyMap["right"] or self.keyMap["space"] or self.keyMap["forward"] or self.keyMap["back"]: if self.isMoving is False: self.ralph.loop("run") self.isMoving = True else: if self.isMoving: self.ralph.stop() self.ralph.pose("walk", 5) self.isMoving = False # update pawns shot or set up new shot after it reaches a certain distance node = NodePath("tmp") node.setHpr(self.pawn.getHpr()) vec = render.getRelativeVector(node,(random.random() * -0.8,random.random() + 1,0)) self.shot.setPos(self.shot.getPos() + self.vec * dt * 10 ) if self.shot.getY() < -15 or self.shot.getY() > 30 or self.shot.getX() < 5 or self.shot.getX() > 15: self.shot.setPos(self.pawn.getPos() + (0,0,0)) self.vec = render.getRelativeVector(node,(random.random() * -0.8,random.random() + 1,0)) self.vec = render.getRelativeVector(node,(random.random() * random.randrange(-1,2),random.random() + 1,0)) # If the camera is too far from ralph, move it closer. # If the camera is too close to ralph, move it farther. #self.camera.lookAt(self.floater) camvec = self.ralph.getPos() - self.camera.getPos() #camvec = Vec3(0,camvec.getY(),0) camdist = camvec.length() x = self.camera.getZ() camvec.normalize() #if camdist > 6.0: # self.camera.setPos(self.camera.getPos() + camvec * (camdist - 6)) #if camdist < 6.0: # self.camera.setPos(self.camera.getPos() - camvec * (6 - camdist)) # Normally, we would have to call traverse() to check for collisions. # However, the class ShowBase that we inherit from has a task to do # this for us, if we assign a CollisionTraverser to self.cTrav. #self.cTrav.traverse(render) # Adjust camera so it stays at same height if self.cameraCollided == False: if self.camera.getZ() < self.ralph.getZ() + 1 or self.camera.getZ() > self.ralph.getZ() + 1: self.camera.setZ(self.ralph.getZ() + 1) # The camera should look in ralph's direction, # but it should also try to stay horizontal, so look at # a floater which hovers above ralph's head. self.camera.lookAt(self.floater) entries = list(self.orbCollisionHandler.getEntries()) if(len(entries) > 0): #self.lightpivot.reparentTo(NodePath()) orbCollected = False self.cameraCollided = False self.ralphSpeed = 85 ralphHit = False for entry in self.orbCollisionHandler.getEntries(): #print(entry) fromColNp = entry.getFromNodePath() toColNp = entry.getIntoNodePath() if fromColNp.getName() == "orbColPath" and toColNp.getName() == "ralphColNode": if orbCollected == False: fromColNp.getParent().reparentTo(NodePath()) self.orbTxt.destroy() self.numOrbs += 1 str1 = "Orbs: " + str(self.numOrbs) self.orbTxt = utils.addInstructions(.18, str1) orbCollected = True elif toColNp.getName() == "orbColPath" and fromColNp.getName() == "ralphColNode": if orbCollected == False: toColNp.getParent().reparentTo(NodePath()) self.orbTxt.destroy() self.numOrbs += 1 str1 = "Orbs: " + str(self.numOrbs) self.orbTxt = utils.addInstructions(.18, str1) orbCollected = True elif fromColNp.getName() == "donutCollisionNode" and toColNp.getName() == "ralphColNode": fromColNp.getParent().reparentTo(NodePath()) self.healthCount += 15 if(self.healthCount > 100): self.healthCount = 100 self.healthTxt.destroy() str1 = "Health: " + str(self.healthCount) self.healthTxt = utils.addInstructions(.06, str1) elif toColNp.getName() == "donutCollisionNode" and fromColNp.getName() == "ralphColNode": toColNp.getParent().reparentTo(NodePath()) self.healthCount += 15 if(self.healthCount > 100): self.healthCount = 100 self.healthTxt.destroy() str1 = "Health: " + str(self.healthCount) self.healthTxt = utils.addInstructions(.06, str1) elif toColNp.getName() == "ralphOrbColPath" and (fromColNp.getName()[:-1] == "chrisColPath" or fromColNp.getName()[:-2] == "chrisColPath"): toColNp.getParent().setZ(20) for chriss in self.chrisList: if chriss.chrisColName == fromColNp.getName(): chris = chriss break chris.chrisHealth = chris.chrisHealth - 1 chris.chris.setColor(1,0,0,1) chris.chrisHit = True chris.chrisRedTime = globalClock.getFrameTime() #print chris.chrisRedTime if chris.chrisHealth < 0 and chris.chrisAlive == True: fromColNp.getParent().removeNode() chris.chrisAlive = False self.hitsTxt.destroy() self.enemyhits += 1 str1 = "Enemy Hits: " + str(self.enemyhits) self.hitsTxt = utils.addInstructions(.28, str1) chris.chrisShot.setZ(26) elif (toColNp.getName()[:-1] == "chrisColPath" or toColNp.getName()[:-2] == "chrisColPath") and fromColNp.getName() == "ralphOrbColPath": fromColNp.getParent().setZ(20) for chriss in self.chrisList: if chriss.chrisColName == toColNp.getName(): chris = chriss break chris.chrisHealth = chris.chrisHealth - 1 chris.chris.setColor(1,0,0,1) chris.chrisHit = True chris.chrisRedTime = globalClock.getFrameTime() #print chris.chrisRedTime if chris.chrisHealth < 0 and chris.chrisAlive == True: toColNp.getParent().removeNode() chris.chrisAlive = False self.hitsTxt.destroy() self.enemyhits += 1 str1 = "Enemy Hits: " + str(self.enemyhits) self.hitsTxt = utils.addInstructions(.28, str1) chris.chrisShot.setZ(26) elif toColNp.getName() == "enemyOrbColPath" and fromColNp.getName() == "ralphColNode": if ralphHit == False: toColNp.getParent().setZ(26) self.healthTxt.destroy() self.healthCount -= 5 str1 = "Health: " + str(self.healthCount) self.healthTxt = utils.addInstructions(.06, str1) self.ralphHit = True self.ralph.setColor((1,0,0,1)) self.ralphRedTime = globalClock.getFrameTime() if self.healthCount <= 0: self.startgame = False #utils.addInstructions3(.45,"You Lose") self.imageObject2 = OnscreenImage(image = 'models/gameover.jpg', pos = (0, 0, 0), scale=1.1) self.imageObject2 = OnscreenImage(image = 'models/gap.jpg', pos = (-2.15, 0, 0), scale=1.1) self.imageObject3 = OnscreenImage(image = 'models/gap.jpg', pos = (2.15, 0, 0), scale=1.1) elif toColNp.getName() == "ralphColNode" and fromColNp.getName() == "enemyOrbColPath": fromColNp.getParent().setZ(26) if ralphHit == False: self.healthTxt.destroy() self.healthCount -= 5 str1 = "Health: " + str(self.healthCount) self.healthTxt = utils.addInstructions(.06, str1) self.ralphHit = True self.ralph.setColor((1,0,0,1)) self.ralphRedTime = globalClock.getFrameTime() ralphHit = True if self.healthCount <= 0: self.startgame = False #utils.addInstructions3(.45,"You Lose") self.imageObject2 = OnscreenImage(image = 'models/gameover.jpg', pos = (0, 0, 0), scale=1.1) self.imageObject2 = OnscreenImage(image = 'models/gap.jpg', pos = (-2.15, 0, 0), scale=1.1) self.imageObject3 = OnscreenImage(image = 'models/gap.jpg', pos = (2.15, 0, 0), scale=1.1) elif toColNp.getName() == "ralphColNode" and fromColNp.getName() == "portalColPath": if self.numOrbs < 3 and self.enemyhits < 4: self.mTextPath.destroy() self.mTextPath = utils.addInstructions2(.30, "Not enough orbs.") self.textTime = globalClock.getFrameTime() self.mTextPath2.destroy() self.mTextPath2 = utils.addInstructions2(.45, "Not enough kills.") self.textTime2 = globalClock.getFrameTime() elif self.numOrbs < 3: self.mTextPath.destroy() self.mTextPath = utils.addInstructions2(.30, "Not enough orbs.") self.textTime = globalClock.getFrameTime() elif self.enemyhits < 4: self.mTextPath2.destroy() self.mTextPath2 = utils.addInstructions2(.45, "Not enough kills.") self.textTime2 = globalClock.getFrameTime() else: self.winText = utils.addInstructions3(.45, "You Win") self.startgame = False #self.ralph.setPos(-196, 177, 3) if self.isMoving == True: self.ralph.stop() self.ralph.pose("walk", 5) self.isMoving = False elif fromColNp.getName() == "ralphOrbColPath" and toColNp.getName() == "allinclusive": fromColNp.getParent().setZ(50) elif toColNp.getName() == "ralphOrbColPath" and fromColNp.getName() == "allinclusive": toColNp.getParent().setZ(50) elif fromColNp.getName() == "enemyOrbWallCheck" and toColNp.getName() == "allinclusive": fromColNp.getParent().setZ(50) #print toColNp.getName() elif toColNp.getName() == "enemyOrbWallCheck" and fromColNp.getName() == "allinclusive": toColNp.getParent().setZ(50) #print fromColNp.getName() elif fromColNp.getName() == "ralphWallCheck" and toColNp.getName() == "allinclusive": #node = NodePath("tmp") #node.setHpr(self.ralph.getHpr()) #vec = render.getRelativeVector(node,(0,-1,0)) #self.ralph.setPos(self.ralph.getPos()-vec) #fromColNp.getParent().setZ(26) self.ralphSpeed = 60 elif toColNp.getName() == "ralphWallCheck" and fromColNp.getName() == "allinclusive": #node = NodePath("tmp") #node.setHpr(self.ralph.getHpr()) #vec = render.getRelativeVector(node,(0,-1,0)) #self.ralph.setPos(self.ralph.getPos()-vec) #print "wtf" #toColNp.getParent().setZ(26) self.ralphSpeed = 60 elif fromColNp.getName() == "ralphWallCheck2" and toColNp.getName() == "allinclusive": #node = NodePath("tmp") #node.setHpr(self.ralph.getHpr()) #vec = render.getRelativeVector(node,(0,1,0)) #self.ralph.setPos(self.ralph.getPos()-vec) #fromColNp.getParent().setZ(26) self.ralphSpeed = 60 elif toColNp.getName() == "ralphWallCheck2" and fromColNp.getName() == "allinclusive": #node = NodePath("tmp") #node.setHpr(self.ralph.getHpr()) #vec = render.getRelativeVector(node,(0,1,0)) #self.ralph.setPos(self.ralph.getPos()-vec) #self.camera.setPos(self.ralph.getPos()) #self.cameraCollided = True self.ralphSpeed = 60 elif fromColNp.getName() == "ralphWallCheck3" and toColNp.getName() == "allinclusive": #node = NodePath("tmp") #node.setHpr(self.ralph.getHpr()) #vec = render.getRelativeVector(node,(-1,0,0)) #self.ralph.setPos(self.ralph.getPos()-vec) #fromColNp.getParent().setZ(26) self.ralphSpeed = 60 elif toColNp.getName() == "ralphWallCheck3" and fromColNp.getName() == "allinclusive": #node = NodePath("tmp") #node.setHpr(self.ralph.getHpr()) #vec = render.getRelativeVector(node,(-1,0,0)) #self.ralph.setPos(self.ralph.getPos()-vec) #self.camera.setPos(self.ralph.getPos()) #self.cameraCollided = True self.ralphSpeed = 60 elif fromColNp.getName() == "ralphWallCheck4" and toColNp.getName() == "allinclusive": #node = NodePath("tmp") #node.setHpr(self.ralph.getHpr()) #vec = render.getRelativeVector(node,(1,0,0)) #self.ralph.setPos(self.ralph.getPos()-vec) #fromColNp.getParent().setZ(26) self.ralphSpeed = 60 elif toColNp.getName() == "ralphWallCheck4" and fromColNp.getName() == "allinclusive": #node = NodePath("tmp") #node.setHpr(self.ralph.getHpr()) #vec = render.getRelativeVector(node,(1,0,0)) #self.ralph.setPos(self.ralph.getPos()-vec) #self.camera.setPos(self.ralph.getPos()) #self.cameraCollided = True self.ralphSpeed = 60 utils.updateHealthBar(self.healthCount, self.bar) #utils.turnofstartbutton(self.flagstartbutton) #if self.flagstartbutton ==1: # self.helpOn.destory() return task.cont
class Tunnel(ShowBase): def __init__(self): ShowBase.__init__(self) base.disableMouse() self.camera.setPosHpr(0, 0, 10, 0, -90, 0) self.setBackgroundColor(0, 0, 0) self.fog = Fog('distanceFog') self.fog.setColor(0, 0, 0) self.fog.setExpDensity(.08) render.setFog(self.fog) self.keyMap = {'left' : 0, 'right' : 0, 'up' : 0, 'down' : 0} # self.accept('escape', sys.exit) self.accept('arrow_left', self.setKey, ['left', True]) self.accept('arrow_right', self.setKey, ['right', True]) self.accept('arrow_up', self.setKey, ['up', True]) self.accept('arrow_down', self.setKey, ['down', True]) self.accept('arrow_left-up', self.setKey, ['left', False]) self.accept('arrow_right-up', self.setKey, ['right', False]) self.accept('arrow_up-up', self.setKey, ['up', False]) self.accept('arrow_down-up', self.setKey, ['down', False]) self.queue = CollisionHandlerQueue() self.trav = CollisionTraverser('traverser') base.cTrav = self.trav # self.makeTunnel() self.makeSphere() self.continueTunnel() # self.crystals = [] for _ in xrange(CRYSTALS_CNT): self.makeRandomCrystal(randint(1, TUNNEL_CNT - 1)) self.collisionCnt = 0 # taskMgr.add(self.moveSphere, 'moveSphere') taskMgr.add(self.handleCollisions, 'handleCollisions') # if DEBUG: self.trav.showCollisions(render) render.find('**/sphere_collision').show() def makeTunnel(self): self.tunnel = [None] * TUNNEL_CNT for x in range(TUNNEL_CNT): self.tunnel[x] = loader.loadModel('models/tunnel') if x == 0: self.tunnel[x].reparentTo(render) else: self.tunnel[x].reparentTo(self.tunnel[x - 1]) self.tunnel[x].setPos(0, 0, -TUNNEL_SEGMENT_LENGTH) def makeSphere(self): self.sphere = loader.loadModel('models/alice-shapes--sphere-highpoly/sphere-highpoly.egg') self.sphere.reparentTo(render) self.sphere.setScale(0.07) self.sphere.setZ(2) # col_node = self.sphere.attachNewNode(CollisionNode('sphere_collision')) col_sphere = CollisionSphere(0, 0, 2, SPHERE_RADIUS) col_node.node().addSolid(col_sphere) self.trav.addCollider(col_node, self.queue) def makeRandomCrystal(self, tun): crystal = loader.loadModel('models/bvw-f2004--purplecrystal/purplecrystal.egg') crystal.reparentTo(self.tunnel[tun]) # pMin, pMax = LPoint3f(), LPoint3f() crystal.calcTightBounds(pMin, pMax) col_node = crystal.attachNewNode(CollisionNode('crystal_collision')) col_box = CollisionBox(pMin, pMax) col_node.node().addSolid(col_box) crystal.setScale(0.1) if DEBUG: col_node.show() pos = ['rx', '-rx', 'ry', 'down', 'up'] rnd = choice(pos) Z = randint(0, 10) if rnd == 'rx': R = randint(45, 90) X = uniform(-2, 6) crystal.setR(R) crystal.setX(X) elif rnd == '-rx': R = randint(45, 90) X = uniform(-2, 8) crystal.setR(-R) crystal.setX(-X) elif rnd == 'ry': R = randint(45, 120) Y = uniform(2, 6) crystal.setR(R) crystal.setY(Y) elif rnd == '-py': R = randint(45, 120) Y = uniform(3, 8) crystal.setR(-R) crystal.setY(-Y) elif rnd == 'down': Y = uniform(1, 6) P = randint(70, 120) crystal.setY(-Y) crystal.setP(P) elif rnd == 'up': Y = uniform(-1, 6) P = randint(-130, -60) crystal.setY(Y) crystal.setP(P) crystal.setZ(Z) self.crystals.append(crystal) def continueTunnel(self): self.tunnel = self.tunnel[1:] + self.tunnel[0:1] self.tunnel[0].setZ(0) self.tunnel[0].reparentTo(render) self.tunnel[0].setScale(.155, .155, .305) self.tunnel[3].reparentTo(self.tunnel[2]) self.tunnel[3].setZ(-TUNNEL_SEGMENT_LENGTH) self.tunnel[3].setScale(1) for child in self.tunnel[3].getChildren(): if child.getName() == 'purplecrystal.egg': self.crystals.remove(child) child.removeNode() self.makeRandomCrystal(3) self.tunnelMove = Sequence \ ( LerpFunc \ ( self.tunnel[0].setZ, duration=TUNNEL_TIME, fromData=0, toData=TUNNEL_SEGMENT_LENGTH * .305 ), Func(self.continueTunnel) ) self.tunnelMove.start() def setKey(self, key, value): self.keyMap[key] = value def moveSphere(self, task): dt = globalClock.getDt() addVec = LVector3f(0, 0, 0) if self.keyMap['left']: addVec[0] -= SPHERE_SPEED * dt elif self.keyMap['right']: addVec[0] += SPHERE_SPEED * dt elif self.keyMap['up']: addVec[1] += SPHERE_SPEED * dt elif self.keyMap['down']: addVec[1] -= SPHERE_SPEED * dt if ((self.sphere.getPos() + addVec) - LPoint3f(TUNNEL_CENTER)).length() < TUNNEL_RADIUS: self.sphere.setPos(self.sphere.getPos() + addVec) return task.cont def handleCollisions(self, task): for entry in self.queue.getEntries(): node = entry.getFromNodePath() if node.getName() == 'sphere_collision': self.collisionCnt += 1 print 'Oops! Collision counter: %d' % self.collisionCnt return task.cont
class IStage(metaclass=abc.ABCMeta): def __init__(self): self.charHeading = {"up": 180, "down": 0, "left": 90, "right": 90} self.pusher = base.pusher self.heroGroundHandler = CollisionHandlerQueue() self.dialogBox = GUIDialog() self.stage = None self.hero = None self.enemyActors = [] self.npcActors = [] self.bossActors = [] def initStage(self): if self.stage: self.stage.removeNode() this_map = base.mapData.maps[base.gameData.currentMap] self.stage = loader.loadModel(this_map['model']) if "pattern" in this_map: self.stage_grid = StageGrid( this_map['pattern'], self.stage, this_map['blockTypes']) self.startPos = self.stage_grid.startPosList[base.gameData.heroPos] else: self.startPos = self.stage.find( "**/" + base.gameData.heroPos).getPos() self.stage.reparentTo(render) self.stage.hide() def setup(self): self.initStage() self.initHero() self.initEnemy() self.initBoss() self.initNPC() def initHero(self): self.hero = loader.loadModel("charaRoot") self.hero.reparentTo(self.stage) model = base.gameData.heroModel self.heroArmature = Actor( "{}".format(model), { "idle": "{}-idle".format(model), "walk": "{}-run".format(model) }) self.heroArmature.reparentTo(self.hero) self.hero.setPos(self.startPos) cNode = CollisionNode('hero') cNode.addSolid(CollisionSphere(0, 0, 1.5, 1)) heroCollision = self.hero.attachNewNode(cNode) ######################################################### # heroCollision.show() self.pusher.addCollider( heroCollision, self.hero, base.drive.node()) base.cTrav.addCollider(heroCollision, self.pusher) heroGroundRay = CollisionRay() heroGroundRay.setOrigin(0, 0, 9) heroGroundRay.setDirection(0, 0, -1) heroGroundCol = CollisionNode('heroRay') heroGroundCol.addSolid(heroGroundRay) heroGroundCol.setFromCollideMask(CollideMask.bit(0)) heroGroundCol.setIntoCollideMask(CollideMask.allOff()) heroGroundColNp = self.hero.attachNewNode(heroGroundCol) ######################################################### # heroGroundColNp.show() base.cTrav.addCollider(heroGroundColNp, self.heroGroundHandler) self.controlCamera() def fixActorsHPR(self): for e in self.enemyActors: if not e: continue h, p, r = e.actor.getHpr() e.actor.setHpr(h, 0, 0) def AIUpdate(self, task): base.AIworld.update() self.fixActorsHPR() return task.cont def initEnemy(self): this_map = base.mapData.maps[base.gameData.currentMap] if not "enemyList" in this_map: return enemyNumber = len(this_map["enemyList"]) if enemyNumber <= 0: return self.enemyActors = [] die = BetterThanDice(2) for enemyIndex in list(range(0, enemyNumber)): enemyModel = this_map["enemyList"][enemyIndex]['model'] fa = FieldActor('enemy', enemyIndex, enemyModel, self.stage, self.enemyActors, self.hero) if die.getValue() == 0: fa.request('Pursue') else: fa.request('Wander') def initBoss(self): this_map = base.mapData.maps[base.gameData.currentMap] if not "bossList" in this_map: return bossNumber = len(this_map["bossList"]) if bossNumber <= 0: return self.bossActors = [] for bossIndex in list(range(0, bossNumber)): if not base.gameData.flags[this_map["bossList"][bossIndex]['flag']]: continue bossModel = this_map["bossList"][bossIndex]['model'] fa = FieldActor('boss', bossIndex, bossModel, self.stage, self.bossActors, self.hero) def initNPC(self): this_map = base.mapData.maps[base.gameData.currentMap] if not "npcList" in this_map: return npcNumber = len(this_map["npcList"]) if npcNumber == 0: return self.npcActors = [] for npcIndex in list(range(0, npcNumber)): npcModel = this_map["npcList"][npcIndex]['model'] fa = FieldActor('npc', npcIndex, npcModel, self.stage, self.npcActors, self.hero) fa.request('Idle') def controlCamera(self): base.camera.setPos(self.hero.getX(), self.hero.getY() - 50, 55) base.camera.lookAt(self.heroArmature) base.sunNp.setPos(self.hero.getX()-4, self.hero.getY()-4, 50) base.sunNp.lookAt(self.hero) def moveHero(self, task): animName = self.heroArmature.getCurrentAnim() if self.dialogBox.isShowing: if animName != "idle": self.heroArmature.loop("idle") self.controlDialogBox() return task.cont keysPressed = sum(base.directionMap.values()) if keysPressed == 0: if animName != "idle": self.heroArmature.loop("idle") if base.commandMap["confirm"]: pass elif base.commandMap["cancel"]: self.cancelCommand() base.resetButtons() return task.cont self.heroHandleFloor() self.heroCalculateDisplacement() self.heroHeading(keysPressed) self.controlCamera() if animName != "walk": self.heroArmature.loop("walk") return task.cont def heroCalculateDisplacement(self): dt = globalClock.getDt() xSpeed = 0.0 ySpeed = 0.0 speed = 25 if base.directionMap["left"]: xSpeed -= speed elif base.directionMap["right"]: xSpeed += speed if base.directionMap["up"]: ySpeed += speed elif base.directionMap["down"]: ySpeed -= speed self.hero.setX(self.hero, xSpeed * dt) self.hero.setY(self.hero, ySpeed * dt) def heroHandleFloor(self): entries = list(self.heroGroundHandler.getEntries()) entries.sort(key=lambda x: x.getSurfacePoint(render).getZ()) if len(entries) > 0 and entries[0].getIntoNode().getName() == "floor": self.hero.setZ(entries[0].getSurfacePoint(render).getZ()) def controlDialogBox(self): if self.dialogBox.isChoices: pass else: if base.commandMap["confirm"] or base.commandMap["cancel"]: self.dialogBox.next() base.resetButtons() def heroHeading(self, keysPressed=None): if not keysPressed: return heading, pitch, roll = self.heroArmature.getHpr() sumAngles = 0 for key, value in base.directionMap.items(): if value: sumAngles += self.charHeading[key] direction = -1 if base.directionMap["left"] else 1 heading = (sumAngles * direction) / keysPressed self.heroArmature.setHpr(heading, pitch, roll) def setHeroCollision(self): inEvent = "{}-into-{}".format('hero', 'world') self.pusher.addInPattern(inEvent) base.accept(inEvent, self.intoEvent) # againEvent = "{}-again-{}".format(enemyColName, heroColName) # self.pusher.addAgainPattern(againEvent) # base.accept(againEvent, self.printAgain) # outEvent = "{}-out-{}".format(enemyColName, heroColName) # self.pusher.addOutPattern(outEvent) # base.accept(outEvent, self.printOut) def changeMap(self): base.callLoadingScreen() self.resetMap() def resetMap(self): self.setup() self.start() base.initController() def start(self): render.setLight(base.alnp) render.setLight(base.sunNp) self.controlCamera() self.stage.show() self.hero.show() for enemy in self.enemyActors: if enemy: enemy.request('Show') for boss in self.bossActors: if boss: boss.request('Show') for npc in self.npcActors: npc.request('Show') # change how to play different music later base.messenger.send("playMap") base.removeLoadingScreen() def stop(self): self.stage.hide() self.hero.hide() for enemy in self.enemyActors: if enemy: enemy.request('Hide') for boss in self.bossActors: if boss: boss.request('Hide') for npc in self.npcActors: npc.request('Hide') render.clearLight() def quit(self): self.stage.removeNode() self.dialogBox.removeNode() @abc.abstractmethod def cancelCommand(self): raise NotImplementedError('subclass must define this method') @abc.abstractmethod def intoEvent(self, entry): raise NotImplementedError('subclass must define this method')
class Game(ShowBase): def __init__(self): # Set up the window, camera, etc. ShowBase.__init__(self) # Set the background color to black self.win.setClearColor(BACKGROUND_COLOR) # 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/world") self.environ.reparentTo(render) # Create the main character playerStartPos = self.environ.find("**/start_point").getPos() self.player = Actor("models/player", {"run": "models/player-run", "walk": "models/player-walk"}) self.player.reparentTo(render) self.player.setScale(0.2) self.player.setPos(playerStartPos + (0, 0, 0.5)) # Create a floater object, which floats 2 units above player. We # use this as a target for the camera to look at. self.floater = NodePath(PandaNode("floater")) self.floater.reparentTo(self.player) self.floater.setZ(CAMERA_TARGET_HEIGHT_DELTA) self.first_person = False def key_dn(name): return lambda: self.setKey(name, True) def key_up(name): return lambda: self.setKey(name, False) def quit(): self.destroy() def toggle_first(): self.first_person = not self.first_person # Accept the control keys for movement and rotation key_map = [ # key command action help # --- ------- ------ ---- ("escape", "esc", lambda: quit(), "[ESC]: Quit"), ("arrow_left", "left", key_dn("left"), "[Left Arrow]: Rotate Left"), ("arrow_left-up", "left", key_up("left"), None), ("arrow_right", "right", key_dn("right"), "[Right Arrow]: Rotate Right"), ("arrow_right-up", "right", key_up("right"), None), ("arrow_up", "forward", key_dn("forward"), "[Up Arrow]: Run Forward"), ("arrow_up-up", "forward", key_up("forward"), None), ("arrow_down", "backward", key_dn("backward"), "[Down Arrow]: Run Backward"), ("arrow_down-up", "backward", key_up("backward"), None), ("a", "cam-left", key_dn("cam-left"), "[A]: Rotate Camera Left"), ("a-up", "cam-left", key_up("cam-left"), None), ("s", "cam-right", key_dn("cam-right"), "[S]: Rotate Camera Right"), ("s-up", "cam-right", key_up("cam-right"), None), ("f", "first-pers", lambda: toggle_first(), "[F]: Toggle first-person"), ] self.keyMap = {} inst = Instructions() for key, command, action, description in key_map: if command: self.setKey(command, False) self.accept(key, action) if description: inst.add(description) taskMgr.add(self.move, "moveTask") # Game state variables self.isMoving = False # Set up the camera self.disableMouse() self.camera.setPos(self.player.getX(), self.player.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 player'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.playerGroundRay = CollisionRay() self.playerGroundRay.setOrigin(0, 0, 9) self.playerGroundRay.setDirection(0, 0, -1) self.playerGroundCol = CollisionNode("playerRay") self.playerGroundCol.addSolid(self.playerGroundRay) self.playerGroundCol.setFromCollideMask(CollideMask.bit(0)) self.playerGroundCol.setIntoCollideMask(CollideMask.allOff()) self.playerGroundColNp = self.player.attachNewNode(self.playerGroundCol) self.playerGroundHandler = CollisionHandlerQueue() self.cTrav.addCollider(self.playerGroundColNp, self.playerGroundHandler) self.camGroundRay = CollisionRay() self.camGroundRay.setOrigin(0, 0, 9) self.camGroundRay.setDirection(0, 0, -1) self.camGroundCol = CollisionNode("camRay") self.camGroundCol.addSolid(self.camGroundRay) self.camGroundCol.setFromCollideMask(CollideMask.bit(0)) self.camGroundCol.setIntoCollideMask(CollideMask.allOff()) self.camGroundColNp = self.camera.attachNewNode(self.camGroundCol) self.camGroundHandler = CollisionHandlerQueue() self.cTrav.addCollider(self.camGroundColNp, self.camGroundHandler) # Uncomment this line to see the collision rays self.playerGroundColNp.show() self.camGroundColNp.show() # Uncomment this line to show a visual representation of the # collisions occuring self.cTrav.showCollisions(render) # Create some lighting ambientLight = AmbientLight("ambientLight") ambientLight.setColor((0.3, 0.3, 0.3, 1)) directionalLight = DirectionalLight("directionalLight") directionalLight.setDirection((-5, -5, -5)) directionalLight.setColor((1, 1, 1, 1)) directionalLight.setSpecularColor((1, 1, 1, 1)) render.setLight(render.attachNewNode(ambientLight)) render.setLight(render.attachNewNode(directionalLight)) # Records the state of the arrow keys def setKey(self, key, value): self.keyMap[key] = value # Accepts arrow keys to move either the player or the menu cursor, # Also deals with grid checking and collision detection def move(self, task): # Get the time that elapsed since last frame. We multiply this with # the desired speed in order to find out with which distance to move # in order to achieve that desired speed. dt = globalClock.getDt() # If the camera-left key is pressed, move camera left. # If the camera-right key is pressed, move camera right. if self.keyMap["cam-left"]: self.camera.setX(self.camera, -20 * dt) if self.keyMap["cam-right"]: self.camera.setX(self.camera, +20 * dt) # save player's initial position so that we can restore it, # in case he falls off the map or runs into something. startpos = self.player.getPos() # If a move-key is pressed, move player in the specified direction. if self.keyMap["left"]: self.player.setH(self.player.getH() + 300 * dt) if self.keyMap["right"]: self.player.setH(self.player.getH() - 300 * dt) if self.keyMap["forward"]: self.player.setY(self.player, -25 * dt) if self.keyMap["backward"]: self.player.setY(self.player, 25 * dt) # If player is moving, loop the run animation. # If he is standing still, stop the animation. if self.keyMap["forward"] or self.keyMap["backward"] or self.keyMap["left"] or self.keyMap["right"]: if self.isMoving is False: self.player.loop("run") self.isMoving = True else: if self.isMoving: self.player.stop() self.player.pose("walk", 5) self.isMoving = False # If the camera is too far from player, move it closer. # If the camera is too close to player, move it farther. if self.first_person: self.camera.setPos(self.player.getPos()) else: camvec = self.player.getPos() - self.camera.getPos() camvec.setZ(0) camdist = camvec.length() camvec.normalize() if camdist > CAMERA_DISTANCE_MAX: self.camera.setPos(self.camera.getPos() + camvec * (camdist - int(CAMERA_DISTANCE_MAX))) camdist = CAMERA_DISTANCE_MAX if camdist < CAMERA_DISTANCE_MIN: self.camera.setPos(self.camera.getPos() - camvec * (int(CAMERA_DISTANCE_MIN) - camdist)) camdist = CAMERA_DISTANCE_MIN # Normally, we would have to call traverse() to check for collisions. # However, the class ShowBase that we inherit from has a task to do # this for us, if we assign a CollisionTraverser to self.cTrav. self.cTrav.traverse(render) # Adjust player's Z coordinate. If player's ray hit terrain, # update his Z. If it hit anything else, or didn't hit anything, put # him back where he was last frame. entries = list(self.playerGroundHandler.getEntries()) entries.sort(key=lambda x: x.getSurfacePoint(render).getZ()) if len(entries) > 0 and entries[0].getIntoNode().getName() == "terrain": self.player.setZ(entries[0].getSurfacePoint(render).getZ()) else: self.player.setPos(startpos) # Keep the camera at one foot above the terrain, # or two feet above player, whichever is greater. entries = list(self.camGroundHandler.getEntries()) entries.sort(key=lambda x: x.getSurfacePoint(render).getZ()) if len(entries) > 0 and entries[0].getIntoNode().getName() == "terrain": self.camera.setZ(entries[0].getSurfacePoint(render).getZ() + CAMERA_POSITION_HEIGHT_DELTA_MIN) if self.camera.getZ() < self.player.getZ() + CAMERA_POSITION_HEIGHT_DELTA_MAX: self.camera.setZ(self.player.getZ() + CAMERA_POSITION_HEIGHT_DELTA_MAX) # The camera should look in player's direction, # but it should also try to stay horizontal, so look at # a floater which hovers above player's head. self.camera.lookAt(self.floater) return task.cont
class mode_head(): def __init__(self,base,Golog, folder_path = None, parent = None): # Set up basic attributes self.base = base self.golog = Golog self.bools = {'textboxes':True} self.buttons = dict() self.window_tasks = dict() self.bt = None self.mw = None self.listener = DirectObject() self.folder_path = folder_path #absolute path of golog folder '/path/to/golog/folder' if self.folder_path: self.file_path = os.path.abspath(self.folder_path + '/' + self.golog.label+ '.golog') autosave = True self.has_window = False self.parent = parent #for autosaving up to original golog self.reset = self.basic_reset self.garbage = [] #list of deleted math_data/graphics_data etc. #create a 2d rende self.render2d = NodePath('2d render') self.camera2D = self.render2d.attachNewNode(Camera('2d Camera')) self.camera2D.setDepthTest(False) self.camera2D.setDepthWrite(False) lens = OrthographicLens() lens.setFilmSize(2, 2) lens.setNearFar(-1000, 1000) self.camera2D.node().setLens(lens) # make a dictionary of mode_heads in the underlying golog if hasattr(self.golog,'mode_heads'): m = 0 while m in self.golog.mode_heads.keys(): m+=1 #get smallest unused mode_head index self.index = m self.label = self.golog.label+"_mode_head_"+str(self.index) self.golog.mode_heads[self.index] = self else: self.golog.mode_heads = dict() self.index = 0 self.label = self.golog.label+"_mode_head_"+ str(self.index) self.golog.mode_heads[self.index] = self ########## ### set up collision handling ### self.queue = CollisionHandlerQueue() ### set up selection tools self.create_list = [[],[]] #select nodes and add to create_list in order to create higher simplecies self.bools = {'selecter':False,'textboxes':True,'shift_clicked':False} #some bools that will be usefull self.dict = {'shift_pt':[None,None]} # set up mouse picker self.pickerNode = CollisionNode('mouseRay') self.pickerNP = self.golog.camera.attachNewNode(self.pickerNode) #attach collision node to camera self.pickerRay = CollisionRay() self.pickerNode.addSolid(self.pickerRay) self.pickerNode.set_into_collide_mask(0) #so that collision rays don't collide into each other if there are two mode_heads self.golog.cTrav.addCollider(self.pickerNP,self.queue) #send collisions to self.queue # set up plane for picking self.planeNode = self.golog.render.attachNewNode("plane") self.planeNode.setTag("mode_head",self.label) # tag to say it belongs to this mode_head self.planeNode.setTag("mode_node", 'plane') self.planeFromObject = self.planeNode.attachNewNode(CollisionNode("planeColNode")) self.planeFromObject.node().addSolid(CollisionPlane(Plane(Vec3(0,-1,0),Point3(0,0,0)))) self.plane = self.planeFromObject.node().getSolid(0) ### # set up preview text self.textNP = self.render2d.attachNewNode(TextNode('text node')) self.textNP.setScale(.2) self.textNP.setPos(-1,0,0) self.textNP.show() #set up dragging info self.grabbed_dict = dict() self.drag_dict = dict() #a mapping from selected nodes to their original positions for dragging self.mouse_down_loc = (0,0,0) self.lowest_level = 3 def open_math_data(self,math_data): if math_data.type == 'golog': golog_dict = math_data() #calls the mathdata (which is a golog_dict) subgolog = golog_dict['golog'] subfolder_path = golog_dict['folder_path'] #### just to be safe, but probably not needed subgolog_folder_path = os.path.join(self.folder_path,'subgologs') if not os.path.exists(subgolog_folder_path): os.mkdir(subgolog_folder_path) #### folder_path = os.path.join(self.folder_path, *golog_dict['folder_path']) print(folder_path) controllable_golog = mode_head(self.base, subgolog, folder_path = folder_path, parent = self) window_manager.modeHeadToWindow(self.base, controllable_golog) if math_data.type == 'file': file_name, file_extension = os.path.splitext(math_data()[-1]) # if file_extension == '.txt': # tk_funcs.edit_txt(os.path.join(self.folder_path,*math_data())) # else: #prompt user to select a program tk_funcs.run_program('',os.path.join(self.folder_path,*math_data())) if math_data.type == 'latex': file_dict = math_data() tex_folder = os.path.join(os.path.abspath(self.folder_path),*file_dict['folder']) tex_file = os.path.join(os.path.abspath(self.folder_path),*file_dict['tex']) if 'pdf' in file_dict.keys(): pdf_file = os.path.join(self.folder_path, *file_dict['pdf']) elif os.path.exists(tex_file.split('.tex')[0]+'.pdf'): #if there is a pdf in the folder with the same name file_dict['pdf'] = file_dict['tex'] file_dict['pdf'][-1] = file_dict['pdf'][-1].split('.tex')[0]+'.pdf' #change extension to .pdf pdf_file = os.path.join(self.folder_path, *file_dict['pdf']) else: pdf_file = None tk_funcs.pdf_or_tex(pdf_file, tex_file) if math_data.type == 'weblink': open_new_tab(math_data()) def update_math_data(self,simplex, math_data_type, **kwargs): if autosave == True: self.save() if 'label' in kwargs: simplex.label = kwargs['label'] self.golog.Simplex_to_Graphics[simplex].textNP.node().setText(simplex.label) self.golog.text_preview_set(self.bools['textboxes']) if simplex.math_data.type != 'None': answer = tk_funcs.are_you_sure('are you sure you want to delete the current math_data?') if answer == 'yes': self.delete_math_data(simplex) else: return if math_data_type == 'None': simplex.math_data = hcat.Math_Data(type = 'None') if math_data_type == 'golog': #create a path for all subgologs, if it doesn't already exist subgolog_folder_path = os.path.join(self.folder_path,'subgologs') if not os.path.exists(subgolog_folder_path): os.mkdir(subgolog_folder_path) new_golog = golog.golog(self.base, label = kwargs['label']) #create a new golog #create a unique folder path list in subgolog_folder_path unique_path = tk_funcs.unique_path(subgolog_folder_path,[kwargs['label']]) new_folder_path = ['subgologs', *unique_path] os.mkdir(os.path.join(self.folder_path , *new_folder_path)) #make the directory as above #create a new golog save at new_folder_path/label.golog new_save_location = os.path.join(self.folder_path, *new_folder_path, kwargs['label']+'.golog') #new_save_location should be a subfolder of subgologs gexport(new_golog, new_save_location) #math data is a dictionary of the physical golog and it's relative save path list golog_dict = {'golog':new_golog, 'folder_path':new_folder_path} simplex.math_data = hcat.Math_Data(math_data = golog_dict, type = 'golog') if math_data_type == 'file': if not os.path.exists(os.path.join(self.folder_path,'files')): os.mkdir(os.path.join(self.folder_path,'files')) file_folder_path = ['files'] file_location = tk_funcs.ask_file_location() if not file_location: return #if user cancels file_name = os.path.split(file_location)[1] #file name with extension file_path = tk_funcs.unique_path(os.path.join(self.folder_path),[*file_folder_path, file_name]) #get a unique file path starting from the file_folder copyfile(file_location, os.path.join(self.folder_path,*file_path)) simplex.math_data = hcat.Math_Data(math_data = file_path, type = 'file') #? add handler for if user exits text editor #? make asynchronous if math_data_type == 'latex': #ensure latex folder exists if not os.path.exists(os.path.join(self.folder_path,'latex')): os.mkdir(os.path.join(self.folder_path,'latex')) # create a uniquely named folder in self.folder_path/latex/ based on simplex.label tex_folder_path = tk_funcs.unique_path(root = self.folder_path, path = ['latex',simplex.label]) os.mkdir(os.path.join(self.folder_path, *tex_folder_path)) #create a tex file in tex folder tex_file_path = [*tex_folder_path, simplex.label+'.tex'] # ask if want new or to load one location = tk_funcs.load_tex(self.folder_path) # if new, returns True and copies template tex file # if load, returns a path and copies the path into tex_file_path true_path = os.path.join(self.folder_path,*tex_file_path) if location == True: copyfile(os.path.abspath('./misc_data/config_files/template.tex'), true_path) # # open( true_path , 'w').close() if isinstance(location, str): copyfile(location, true_path) # make a file dictionary with just tex file in it file_dict = {'tex':tex_file_path, 'folder':tex_folder_path} simplex.math_data = hcat.Math_Data(math_data = file_dict, type = 'latex') if math_data_type == 'weblink': weblink = tk_funcs.ask_weblink() simplex.math_data = hcat.Math_Data(math_data = weblink, type = 'weblink') if math_data_type == 'text': #create a text file if not os.path.exists(os.path.join(self.folder_path,'files')): os.mkdir(os.path.join(self.folder_path,'files')) file_folder_path = ['files'] file_path = tk_funcs.unique_path(root = self.folder_path, path = ['files',simplex.label+'.txt' ]) with open(os.path.join(self.folder_path, *file_path),'w') as file: pass #create a math_data for it simplex.math_data = hcat.Math_Data(math_data = file_path, type = 'file') #save golog if autosave == True: self.save() return simplex.math_data def delete_math_data(self,simplex,**kwargs): simplex.math_data.delete(self.folder_path) simplex.math_data = hcat.Math_Data(type = 'None') def setup_window(self, windict): self.windict = windict for button in self.buttons.keys(): self.listener.accept(self.windict['bt'].prefix+button, self.buttons[button], extraArgs = [self.windict['mw']]) for window_task in self.window_tasks.keys(): base.taskMgr.add(self.window_tasks[window_task], window_task, extraArgs = [self.windict['mw']], appendTask = True) self.has_window = True def save(self, *ask): #if has a parent, save the parent. If not, save itself if not self.parent: #if doesn't have a parent mode_head, save parent #if we pass something to ask it will ask (this includes mousewatchers) if ask: save_location = tk_funcs.ask_file_location(initial_dir = self.folder_path) if not save_location: return #if user cancels print('saving to:\n'+save_location) gexport(self.golog, self.folder_path) else: gexport(self.golog, self.file_path) else: self.parent.save()# if parent has no mode_head, save itself #basic reset function which shuts off the listener and removes button bindings. Should only be called if no "reset" function exists def basic_reset(self,*args): self.buttons = dict() self.window_tasks = dict() self.listener.ignoreAll() #function to call before deleting the mode_head, for example when closing a window def clean(self): self.reset() #close window if self.has_window == True: if hasattr(mode_head,'windict'): if 'win' in mode_head.windict.keys(): self.base.closeWindow(mode_head.windict['win'], keepCamera = True, removeWindow = True) self.has_window = False del self.golog.mode_heads[self.index] del self.reset # function to return the only the relevant collision data from the mouseRay def get_relevant_entries(self, mw): # get list of entries by distance if not mw.node().hasMouse(): return mpos = mw.node().getMouse() self.pickerRay.setFromLens(self.golog.camNode,mpos.getX(),mpos.getY()) #mouse ray goes from camera through the 'lens plane' at position of mouse self.golog.cTrav.traverse(self.golog.render) self.queue.sortEntries() # get the first relevant node traversed by mouseRay #### ignore everything with a mode_head tag that is not defined by this mode_head for e in self.queue.getEntries(): if e.getIntoNodePath().getParent().hasTag("mode_head"): if e.getIntoNodePath().getParent().getTag("mode_head") == self.label: return (e.getIntoNodePath().getParent(), e.getIntoNodePath().getParent().getTag("mode_node"),e.getSurfacePoint(e.getIntoNodePath())) break else: entry = e break #return node create_list in the golog entryNP = entry.getIntoNodePath().getParent() if entryNP.hasTag('level'): return (entryNP, entryNP.getTag('level'),entryNP.getPos()) #function to 'pick up' a node by adding it to the dragged dictionary def pickup(self, mw): if not mw.node().hasMouse(): return (entryNP, node_type, entry_pos) = self.get_relevant_entries(mw) ### get position on plane for mouseloc mpos = mw.node().getMouse() self.pickerRay.setFromLens(self.golog.camNode,mpos.getX(),mpos.getY()) #mouse ray goes from camera through the 'lens plane' at position of mouse self.golog.cTrav.traverse(self.golog.render) self.queue.sortEntries() for e in self.queue.getEntries(): if e.getIntoNodePath().getParent().hasTag("mode_head"): if e.getIntoNodePath().getParent().getTag("mode_head") == self.label: if e.getIntoNodePath().getParent().getTag("mode_node") == 'plane': self.mouse_down_loc = e.getSurfacePoint(e.getIntoNodePath()) break #if selected node is in the drag_dict, use it to set a mouse location # if entryNP in self.drag_dict.keys(): self.mouse_down_loc = entryNP.getPos() if node_type in ['0','1']: self.grabbed_dict = {'graphics': self.golog.NP_to_Graphics[entryNP],'dragged':False, 'orig_pos': entryNP.getPos()} if node_type == 'plane': for node in self.create_list[0]: node.setColorScale(1,1,1,1) #turn white self.create_list = [[],[]] for node in self.drag_dict.keys(): node.setColorScale(1,1,1,1) self.drag_dict = dict() #drag dict is used for multi-dragging a = self.plane.distToPlane(self.golog.camera.getPos())/(2*self.golog.camera.node().getLens().getNear())# ratio for camera movement self.grabbed_dict = {'graphics':self.golog.camera, 'dragged':False, 'orig_pos': self.golog.camera.getPos(), 'mpos': LPoint3f(a*mpos.getX(),a*0,a*mpos.getY())} self.lowest_level = 3 #function to 'put down' a node, returns true if it's dragged something def putdown(self, mw): for node in self.drag_dict.keys(): self.drag_dict[node] = self.golog.NP_to_Graphics[node].graphics_kwargs['pos'] self.lowest_level = min(self.lowest_level, int(node.getTag('level'))) if 'dragged' in self.grabbed_dict.keys(): if self.grabbed_dict['dragged'] == True: self.grabbed_dict = dict() return True self.grabbed_dict = dict() #function to select a node and add a 1-simplex between 2 create_list 0-simplecies def select_for_creation(self, mw): if not mw.node().hasMouse(): return #remove selected for node in self.drag_dict.keys(): node.setColorScale(1,1,1,1) self.drag_dict = dict() self.lowest_level = 3 ### selection ### (entryNP, node_type, entry_pos) = self.get_relevant_entries(mw) if node_type == '0':# and set(create_list[1:]) = {[]}: if entryNP not in self.create_list[0]: #? don't just append, re-sort self.create_list[0].append(entryNP) entryNP.setColorScale(1,0,0,0) #turn red if len(self.create_list[0]) == 2: # NP -> graphics -> simplex faces = tuple([self.golog.Graphics_to_Simplex[self.golog.NP_to_Graphics[faceNP]] for faceNP in self.create_list[0][-1:-3:-1]]) asked_list = tk_funcs.ask_math_data('1-Simplex') if not asked_list: #[label, math_data_type,] return simplex = self.golog.add(faces, label = asked_list[0]) #reversed create_list objects and creates a 1 - simplex from them self.update_math_data(simplex, asked_list[1], label = asked_list[0]) for node in self.create_list[0]: node.setColorScale(1,1,1,1) self.create_list[0] = [] #reset create_list #open math_data of simplex under mouse def mouse_open(self, mw): (entryNP, node_type, entry_pos) = self.get_relevant_entries(mw) # if spaced on a 0 simplex, open it's math data, or create it if node_type in ['0','1']: simplex = self.golog.Graphics_to_Simplex[self.golog.NP_to_Graphics[entryNP]] if not simplex.math_data(): asked_list = tk_funcs.ask_math_data(simplex.label) if not asked_list: return self.update_math_data(simplex, math_data_type = asked_list[1], label = asked_list[0]) #? make asynchronous else: self.open_math_data(simplex.math_data) # delete a simplex, prompt to delete the math_data and (recursively) it's supported simplecies def delete(self,mw): (entryNP, node_type, entry_pos) = self.get_relevant_entries(mw) if node_type in ['0','1']: #warning about deleting supported simplecies graphics = self.golog.NP_to_Graphics[entryNP] simplex = self.golog.Graphics_to_Simplex[graphics] if simplex.supports: answer = tk_funcs.are_you_sure(simplex.label+' still supports other simplecies. Are you sure you wish to delete?') if answer != 'yes': return self.delete_math_data(simplex) graphics._remove() #update the math_data of a simplex under the mouse def mouse_update(self, mw): (entryNP, node_type, entry_pos) = self.get_relevant_entries(mw) if node_type in ['0','1']: simplex = self.golog.Graphics_to_Simplex[self.golog.NP_to_Graphics[entryNP]] asked_list = tk_funcs.ask_math_data(simplex.label) if not asked_list: return self.update_math_data(simplex, math_data_type = asked_list[1], label = asked_list[0]) #create a simplex given a mouse position def create(self, mw): (entryNP, node_type, entry_pos) = self.get_relevant_entries(mw) if node_type == 'plane': for node in self.create_list[0]: node.setColorScale(1,1,1,1) #turn white self.create_list = [[],[]] asked_list = tk_funcs.ask_math_data('0-Simplex') if not asked_list: return #if canceled, do not create a simplex simplex = self.golog.add(0, pos = entry_pos, label = asked_list[0]) #create a simplex self.update_math_data(simplex, math_data_type = asked_list[1], label = asked_list[0]) #function which checks the drag dictionary and drags stuff def drag(self, mw): if self.grabbed_dict: #get mouse_loc mpos = mw.node().getMouse() self.pickerRay.setFromLens(self.golog.camNode,mpos.getX(),mpos.getY()) #mouse ray goes from camera through the 'lens plane' at position of mouse self.golog.cTrav.traverse(self.golog.render) self.queue.sortEntries() mouseloc = None for e in self.queue.getEntries(): if e.getIntoNodePath().getParent().getTag("mode_node") == 'plane': mouseloc = e.getSurfacePoint(e.getIntoNodePath()) break if not mouseloc:return self.bools['dragging'] = True # if there is something in the drag dict (for multiselect) if self.drag_dict: self.grabbed_dict['dragged'] = True #only drag lowest dim simplecies for node in self.drag_dict.keys(): if int(node.getTag('level')) == self.lowest_level: self.golog.NP_to_Graphics[node].update({'pos':self.drag_dict[node]+mouseloc-self.mouse_down_loc}) #if nothing is selected, drag the thing below you elif self.grabbed_dict['graphics'] != self.golog.camera: #radius of drag selection offset = mouseloc - self.grabbed_dict['graphics'].parent_pos_convolution() delta = offset - self.grabbed_dict['orig_pos'] norm = delta.getX()**2 +delta.getY()**2 +delta.getZ()**2 if self.grabbed_dict['dragged'] == True or norm > 1: self.grabbed_dict['dragged'] = True #if offset magnitude is greater than 1 or dragged == true, actually drag it if self.grabbed_dict['dragged'] == True: self.grabbed_dict['graphics'].update({'pos':offset}) elif self.grabbed_dict['graphics'] == self.golog.camera: a = self.plane.distToPlane(self.golog.camera.getPos())/(2*self.golog.camera.node().getLens().getNear())# ratio for camera movement self.golog.camera.setPos(self.grabbed_dict['orig_pos']-(a*mpos.getX(),0,a*mpos.getY())+self.grabbed_dict['mpos']) #send preview of math_data of simplex under the mouse def preview(self, mw): (entryNP, node_type, entry_pos) = self.get_relevant_entries(mw) if node_type == '0': simplex = self.golog.Graphics_to_Simplex[self.golog.NP_to_Graphics[entryNP]] elif node_type == '1': #? again consider what needs to be shown with 1-simplecies simplex = self.golog.Graphics_to_Simplex[self.golog.NP_to_Graphics[entryNP]] else: return # set preview up if self.has_window: if simplex.math_data.type == 'golog': self.windict['preview_dr'].setCamera(simplex.math_data()['golog'].camera) else: self.windict['preview_dr'].setCamera(self.camera2D) self.textNP.node().setText("label:\n" +simplex.label+"\n\n math data type:\n" + simplex.math_data.type) return def pprint(self,mw): (entryNP, node_type, entry_pos) = self.get_relevant_entries(mw) if node_type == '0': simplex = self.golog.Graphics_to_Simplex[self.golog.NP_to_Graphics[entryNP]] elif node_type == '1': #? again consider what needs to be shown with 1-simplecies simplex = self.golog.Graphics_to_Simplex[self.golog.NP_to_Graphics[entryNP]] else: return simplex.pprint() #tool for selecting multiple simplecies def multi_select(self,mw): if isinstance(mw, NodePath): entryNP = mw node_type = entryNP.getTag('level') else: return #reset select and create if node_type in ['0','1']: if entryNP in self.drag_dict.keys(): del self.drag_dict[entryNP] entryNP.setColorScale(1,1,1,1) self.lowest_level = min([*[int(node.getTag('level')) for node in self.drag_dict.keys()],3]) else: self.drag_dict[entryNP] = self.golog.NP_to_Graphics[entryNP].graphics_kwargs['pos'] entryNP.setColorScale(.5,.5,0,1) self.lowest_level = min(self.lowest_level, int(entryNP.getTag('level'))) def shift_box(self,mw): if None in self.dict['shift_pt']: self.dict['shift_pt'] = [None,None] return pt_1 = self.dict['shift_pt'][0] pt_2 = self.dict['shift_pt'][1] if sum([x**2 for x in list(pt_2-pt_1)]) <= 1: (entryNP, node_type, entry_pos) = self.get_relevant_entries(mw) self.multi_select(entryNP) #create vectors N = self.planeFromObject.node().getSolid(0).getNormal() x = [(pt_2-pt_1).getX(),0,0] z = [0,0,(pt_2-pt_1).getZ()] campos = list(self.golog.camera.getPos()) for simplex in self.golog.sSet.rawSimps: node = self.golog.Simplex_to_Graphics[simplex].node_list[0] P = list(node.getPos()) if t_int(pt_1,x,z,campos,P): self.multi_select(node) self.dict['shift_pt'] = [None,None] def consolidate(self, selected = False,sel_simp = None): #ask for label, or cancel G = self.golog # if a collection isn't provided, use the (multi-select) drag dictionary if not selected: selected = [G.Graphics_to_Simplex[G.NP_to_Graphics[node]] for node in self.drag_dict.keys()] if not selected: return #return if there was nothing passed, and nothing in the drag_dict for simp in selected: for face in simp.faces: if face not in selected: selected.append(face) # #? select a simplex to consolidate into # sel_simp = None # for simp in selected: # if simp.math_data.type == 'None': # sel_simp = simp #make a golog from selected new_golog = golog.golog(self.base, label ='test') def add(simplex): for face in simplex.faces: add(face) new_golog.add(simplex, pos = G.Simplex_to_Graphics[simplex].graphics_kwargs['pos']) for simplex in selected: add(simplex) #consolidate into 1 simplex if sel_simp: #consolidate into sel_simp subgolog_folder_path = os.path.join(self.folder_path,'subgologs') unique_path = tk_funcs.unique_path(subgolog_folder_path,[sel_simp.label]) new_folder_path = ['subgologs', *unique_path] sel_simp.math_data = hcat.Math_Data(math_data = {'golog':new_golog, 'folder_path':new_folder_path}, type = 'golog') #? remove simplexes and place at selected simplex location return sel_simp #create an entirely new simplex to put the golog into else: #? ask for label / cancel label = "test" subgolog_folder_path = os.path.join(self.folder_path,'subgologs') unique_path = tk_funcs.unique_path(subgolog_folder_path,[label]) new_folder_path = ['subgologs', *unique_path] #create a simplex with average position of things in golog avg = LPoint3f(*[sum([G.Simplex_to_Graphics[simplex].graphics_kwargs['pos'][i]/len(new_golog.sSet.simplecies[()]) for simplex in new_golog.sSet.simplecies[()]]) for i in range(3)]) s = self.golog.add(0, label ='test', math_data = hcat.Math_Data(math_data = {'golog':new_golog, 'folder_path':new_folder_path}, type = 'golog'), pos = LPoint3f(avg)) return s ########## BEGIN DEFINING MODES ########## #selection and creation mode def selection_and_creation(self, windict): def mouse1(mw): if not mw: return self.bools['shift_clicked'] = False self.pickup(mw) def shift_mouse1(mw): if not mw: return #on click, begin a rectagle dragging function (entryNP, node_type, entry_pos) = self.get_relevant_entries(mw) self.dict['shift_pt'][0] = entry_pos self.bools['shift_clicked'] = True def mouse1_up(mw): dropped_bool = self.putdown(mw) if self.bools['shift_clicked']: (entryNP, node_type, entry_pos) = self.get_relevant_entries(mw) self.dict['shift_pt'][1] = entry_pos self.shift_box(mw) self.bools['shift_clicked'] = False elif dropped_bool: pass else: self.select_for_creation(mw) def space(mw): if not mw: return self.mouse_open(mw) def u(mw): if not mw: return #? do a change not just update self.mouse_update(mw) def c(mw): self.consolidate() def p(mw): if not mw: return self.pprint(mw) def mouse3(mw): if not mw: return self.create(mw) def backspace(mw): if not mw: return self.delete(mw) def mouse_watch_task(mw,task): if not mw: return task.cont if not mw.node().hasMouse(): return task.cont self.drag(mw) self.preview(mw) return task.cont def wheel_up(mw): if not mw:return a = self.plane.distToPlane(self.golog.camera.getPos())/(2*self.golog.camera.node().getLens().getNear())# ratio for camera movement self.golog.camera.setPos( self.golog.camera.getPos() + (0,10,0) ) #fix for offset by storing a global camera - plane ratio def wheel_down(mw): if not mw:return self.golog.camera.setPos( self.golog.camera.getPos() - (0,10,0) ) def reset(*args): self.buttons = dict() self.window_task = dict() self.reset = self.basic_reset self.reset = reset self.buttons = {'mouse1':mouse1, 'mouse1-up':mouse1_up, 'mouse3':mouse3,'c':c, 'space':space, 'escape':self.reset, 's':self.save, 'u':u,'backspace':backspace, 'shift-mouse1':shift_mouse1,'p':p,'wheel_up':wheel_up, "wheel_down":wheel_down} self.window_tasks = {'mouse_watch_task':mouse_watch_task} self.setup_window(windict)
class KeyboardController(DirectObject): def __init__(self, base): DirectObject.__init__(self) self.base = base self.lastUpdate = 0.0 # Parameters self.wheelBaseOffset = 3.4 self.wheelSideOffset = 3.1 self.speedMax = 50.0 self.steerAngleMax = 20.0 self.numSensors = 5 self.sensorHeight = 2.0 # Collisions self.collisionHandler = CollisionHandlerQueue() # Load car model self.car = self.base.loader.loadModel("models/carBody") self.car.reparentTo(self.base.render) self.wheelFrontLeft = self.base.loader.loadModel("models/carWheel") self.wheelFrontLeft.reparentTo(self.car) self.wheelFrontLeft.setX(self.wheelBaseOffset) self.wheelFrontLeft.setY(self.wheelSideOffset) self.wheelFrontRight = self.base.loader.loadModel("models/carWheel") self.wheelFrontRight.reparentTo(self.car) self.wheelFrontRight.setX(self.wheelBaseOffset) self.wheelFrontRight.setY(-self.wheelSideOffset) self.wheelBackLeft = self.base.loader.loadModel("models/carWheel") self.wheelBackLeft.reparentTo(self.car) self.wheelBackLeft.setX(-self.wheelBaseOffset) self.wheelBackLeft.setY(self.wheelSideOffset) self.wheelBackRight = self.base.loader.loadModel("models/carWheel") self.wheelBackRight.reparentTo(self.car) self.wheelBackRight.setX(-self.wheelBaseOffset) self.wheelBackRight.setY(-self.wheelSideOffset) # Car properties self.steerAngle = 0.0 self.speed = 0.0 self.wheelFront = Vec3(1.0, 0.0, 0.0) * self.wheelBaseOffset self.wheelBack = Vec3(-1.0, 0.0, 0.0) * self.wheelBaseOffset self.initSensors(self.numSensors, self.sensorHeight) # Controls self.arrowUpDown = False self.arrowLeftDown = False self.arrowRightDown = False self.accept("arrow_up", self.onUpArrow, [True]) self.accept("arrow_up-up", self.onUpArrow, [False]) self.accept("arrow_left", self.onLeftArrow, [True]) self.accept("arrow_left-up", self.onLeftArrow, [False]) self.accept("arrow_right", self.onRightArrow, [True]) self.accept("arrow_right-up", self.onRightArrow, [False]) self.base.taskMgr.add(self.updateCar, "UpdateCarTask") # DEBUG self.drawAxis(10) self.drawWheelBase() def initSensors(self, n, h): origin = Point3(0.0, 0.0, h) angles = [math.pi * t / (n - 1) for t in range(n)] sensors = [Vec3(math.sin(ang), math.cos(ang), h) for ang in angles] # Create collision nodes for sensors sensorNode = CollisionNode('sensor') sensorRay = CollisionRay(origin, Vec3(1.0, 0.0, 0.0)) sensorNode.addSolid(sensorRay) # Add collision node to car and handler sensorNodePath = self.car.attachNewNode(sensorNode) self.base.cTrav.addCollider(sensorNodePath, self.collisionHandler) self.base.taskMgr.add(self.checkCollisions, "CheckCollisionsTask") def checkCollisions(self, task): for entry in self.collisionHandler.getEntries(): pass return task.cont def getNodePath(self): return self.car def onUpArrow(self, down): self.arrowUpDown = down def onLeftArrow(self, down): self.arrowLeftDown = down def onRightArrow(self, down): self.arrowRightDown = down def degToRad(self, deg): return deg * (math.pi / 180.0) def radToDeg(self, rad): return rad * (180.0 / math.pi) def updateCar(self, task): # Register controls if self.arrowUpDown: self.speed = self.speedMax else: self.speed = 0.0 if self.arrowLeftDown and not self.arrowRightDown: self.steerAngle = self.steerAngleMax if self.arrowRightDown and not self.arrowLeftDown: self.steerAngle = -self.steerAngleMax if not self.arrowLeftDown and not self.arrowRightDown: self.steerAngle = 0.0 # Update car pose dt = task.time - self.lastUpdate self.lastUpdate = task.time wheelFrontUpdate = self.wheelFront + Vec3( math.cos(self.degToRad(self.steerAngle)), math.sin(self.degToRad(self.steerAngle)), 0.0) * self.speed * dt wheelBackUpdate = self.wheelBack + Vec3(1.0, 0.0, 0.0) * self.speed * dt positionLocalUpdate = (wheelFrontUpdate + wheelBackUpdate) / 2.0 headingLocalUpdate = math.atan2( wheelFrontUpdate.getY() - wheelBackUpdate.getY(), wheelFrontUpdate.getX() - wheelBackUpdate.getX()) self.car.setPos(self.car, positionLocalUpdate) self.car.setH(self.car, self.radToDeg(headingLocalUpdate)) self.wheelFrontLeft.setH(self.steerAngle) self.wheelFrontRight.setH(self.steerAngle) return task.cont def drawAxis(self, scale): axisSegs = LineSegs("axis") axisSegs.setThickness(5) axisSegs.setColor(1.0, 0.0, 0.0) axisSegs.moveTo(Point3(0.0, 0.0, 0.0)) axisSegs.drawTo(Point3(1.0, 0.0, 0.0) * scale) axisSegs.setColor(0.0, 1.0, 0.0) axisSegs.moveTo(Point3(0.0, 0.0, 0.0)) axisSegs.drawTo(Point3(0.0, 1.0, 0.0) * scale) axisSegs.setColor(0.0, 0.0, 1.0) axisSegs.moveTo(Point3(0.0, 0.0, 0.0)) axisSegs.drawTo(Point3(0.0, 0.0, 1.0) * scale) axisNode = axisSegs.create() axisNodePath = self.car.attachNewNode(axisNode) axisNodePath.setZ(axisNodePath, 1.0) return axisNodePath def drawWheelBase(self): wheelSegs = LineSegs("wheelBase") wheelSegs.setThickness(5) wheelSegs.moveTo(self.wheelFront + Vec3(0.0, 5.0, 1.44)) wheelSegs.drawTo(self.wheelFront + Vec3(0.0, -5.0, 1.44)) wheelSegs.moveTo(self.wheelBack + Vec3(0.0, 5.0, 1.44)) wheelSegs.drawTo(self.wheelBack + Vec3(0.0, -5.0, 1.44)) wheelNode = wheelSegs.create() wheelNodePath = self.car.attachNewNode(wheelNode) return wheelNodePath
class RoamingRalphDemo(ShowBase): def __init__(self): # Set up the window, camera, etc. ShowBase.__init__(self) self.orbCollisionHandler = CollisionHandlerQueue() self.cTrav = CollisionTraverser() self.cTrav.setRespectPrevTransform(True) #hbPath = NodePath() utilsKristina2.setUpKeys(self) utilsKristina2.loadModels(self) utilsKristina2.setUpLighting(self) utilsKristina2.setUpFloatingSpheres(self) utilsKristina2.setUpRalphsShot(self) utilsKristina2.setUpCamera(self) utilsKristina2.setUpCollisionSpheres(self) self.healthTxt = utilsKristina2.addInstructions(.06,"Health: 100") self.orbTxt = utilsKristina2.addInstructions(.18,"Orbs: 0") self.vec = LVector3(0,1,0)#vector for pawns shot # Create a frame #frame = DirectFrame(text = "main", scale = 0.001) # Add button #bar = DirectWaitBar(text = "", value = 50, pos = (0,.4,.4)) #bar.reparent(render) # Game state variables self.isMoving = False self.jumping = False self.vz = 0 self.numOrbs = 0 self.healthCount = 100 #self.shotList = [] #self.sphere = CollisionBox((self.ralph.getX() + -10,self.ralph.getY(),self.ralph.getZ()),10,10,10) self.sphere = CollisionSphere(0,-5,4,3) self.sphere3 = CollisionSphere(0,5,5,3) self.sphere4 = CollisionSphere(-4,0,5,2) self.sphere5 = CollisionSphere(4,0,5,2) self.sphere2 = CollisionSphere(0,0,3,2) self.cnodePath = self.ralph.attachNewNode((CollisionNode("ralphColNode"))) self.cnodePath2 = self.ralph.attachNewNode((CollisionNode("ralphWallCheck"))) self.cnodePath3 = self.ralph.attachNewNode((CollisionNode("ralphWallCheck2"))) self.cnodePath4 = self.ralph.attachNewNode((CollisionNode("ralphWallCheck3"))) self.cnodePath5 = self.ralph.attachNewNode((CollisionNode("ralphWallCheck4"))) self.cnodePath.node().addSolid(self.sphere2) self.cnodePath2.node().addSolid(self.sphere) self.cnodePath3.node().addSolid(self.sphere3) self.cnodePath4.node().addSolid(self.sphere4) self.cnodePath5.node().addSolid(self.sphere5) #self.cnodePath.node().addSolid(self.sphere2) self.cnodePath.show() #self.cnodePath2.show() #self.cnodePath3.show() #self.cnodePath4.show() #self.cnodePath5.show() self.cTrav.addCollider(self.cnodePath2, self.orbCollisionHandler) self.cTrav.addCollider(self.cnodePath3, self.orbCollisionHandler) self.cTrav.addCollider(self.cnodePath4, self.orbCollisionHandler) self.cTrav.addCollider(self.cnodePath5, self.orbCollisionHandler) self.pusher = CollisionHandlerPusher() self.pusher.addCollider(self.cnodePath, self.ralph) #self.cTrav.addCollider(self.cnodePath, self.ralphCollisionHandler) self.cTrav.addCollider(self.cnodePath, self.pusher) self.chrisLastShotTime = globalClock.getFrameTime() self.chrisTimer = globalClock.getDt() #def __init__(self, pos,showbase, colPathName, dir, length): self.chrisList = [utilsKristina2.chris((15,0,0.5),self,"chrisColPath0","X",6), utilsKristina2.chris((18,29,0.5),self,"chrisColPath1","X",6), utilsKristina2.chris((-6,67,0.5),self,"chrisColPath2","X",6), utilsKristina2.chris((-41,72,0.5),self,"chrisColPath7","X",6)] #,utilsKristina2.chris((-42,106,0.5),self,"chrisColPath3","X",6)]#, utilsKristina2.chris((-62,108,0.5),self,"chrisColPath4","X",6), #utilsKristina2.chris((-74,70,0.5),self,"chrisColPath5","y",6)] #def _init_(self,showbase,pos,color,speed,radius): self.orbList = [utilsKristina2.orb(self,(0,0,2),(0,0,1,1),20,4)] self.donutList = [utilsKristina2.donut(self,(0,0,2),40,3)] self.cameraCollided = False self.ralphSpeed = 60 self.ralphHit = False self.ralphRedTime = 0 self.ralphLife=True taskMgr.add(self.move, "moveTask") taskMgr.add(self.moveChris,"moveChrisTask") # Records the state of the arrow keys def setKey(self, key, value): self.keyMap[key] = value def startEnemyThread(self): showbase = self class enemyThread(threading.Thread): def run(self): dt = globalClock.getDt() for chris in showbase.chrisList: chris.moveChris(dt,showbase,showbase.chrisList) def moveChris(self,task): dt = globalClock.getDt() for chris in self.chrisList: chris.moveChris(dt,self,self.chrisList) return task.cont # Accepts arrow keys to move either the player or the menu cursor, # Also deals with grid checking and collision detection def move(self, task): # Get the time that elapsed since last frame. We multiply this with # the desired speed in order to find out with which distance to move # in order to achieve that desired speed. dt = globalClock.getDt() #utilsKristina2.moveChris(self,dt) #self.chris2.moveChris(dt,self) #self.startEnemyThread() if globalClock.getFrameTime()- self.ralphRedTime > .3 and self.ralphHit == True: self.ralph.clearColor() self.ralphHit = False # If the camera-left key is pressed, move camera left. # If the camera-right key is pressed, move camera right. if self.keyMap["cam-left"]: self.camera.setZ(self.camera, -20 * dt) if self.keyMap["cam-right"]: self.camera.setZ(self.camera, +20 * dt) # save ralph's initial position so that we can restore it, # in case he falls off the map or runs into something. startpos = self.ralph.getPos() # If a move-key is pressed, move ralph in the specified direction. if self.keyMap["left"]: self.ralph.setH(self.ralph.getH() + 75 * dt) #self.camera.setX(self.camera, +15.5 * dt) if self.keyMap["right"]: self.ralph.setH(self.ralph.getH() - 75 * dt) #self.camera.setX(self.camera, -15.5 * dt) if self.keyMap["forward"]: self.ralph.setFluidY(self.ralph, -1*self.ralphSpeed * dt) #self.camera.setY(self.camera, -35 * dt) if self.keyMap["back"]: self.ralph.setFluidY(self.ralph, self.ralphSpeed * dt) #self.camera.setY(self.camera, 35 * dt) if self.keyMap["space"]: if self.jumping is False: #self.ralph.setZ(self.ralph.getZ() + 100 * dt) self.jumping = True self.vz = 8 if self.keyMap["c"] or self.keyMap["enter"]: if self.keyMap["c"]: self.keyMap["c"]=False if self.keyMap["enter"]: self.keyMap["enter"] = False self.shotList[self.shotCount].lpivot.setPos(self.ralph.getPos()) self.shotList[self.shotCount].lpivot.setZ(self.ralph.getZ() + .5) self.shotList[self.shotCount].lpivot.setX(self.ralph.getX() - .25) print self.ralph.getPos() #self.shotList.append(rShot) #self.lightpivot3.setPos(self.ralph.getPos()) #self.lightpivot3.setZ(self.ralph.getZ() + .5) #self.lightpivot3.setX(self.ralph.getX() - .25) #self.myShot.setHpr(self.ralph.getHpr()) #parent to ralph #node = NodePath("tmp") #node.setHpr(self.ralph.getHpr()) #vec = render.getRelativeVector(node,(0,-1,0)) #self.myShotVec = vec node = NodePath("tmp") node.setHpr(self.ralph.getHpr()) vec = render.getRelativeVector(node,(0,-1,0)) self.shotList[self.shotCount].vec = vec self.shotCount = (self.shotCount + 1) % 10 for rs in self.shotList: rs.lpivot.setPos(rs.lpivot.getPos() + rs.vec * dt * 25 ) #if shot is too far stop updating if self.jumping is True: self.vz = self.vz - 16* dt self.ralph.setZ(self.ralph.getZ() + self.vz * dt ) if self.ralph.getZ() < 0: self.ralph.setZ(0) self.jumping = False else: if self.ralph.getZ() < 0.25: self.ralph.setZ(0.25) elif self.ralph.getZ() > 0.25: self.ralph.setZ(self.ralph.getZ() -7 * dt) # If ralph is moving, loop the run animation. # If he is standing still, stop the animation. if self.keyMap["forward"] or self.keyMap["left"] or self.keyMap["right"] or self.keyMap["space"] or self.keyMap["forward"] or self.keyMap["back"]: if self.isMoving is False: self.ralph.loop("run") self.isMoving = True else: if self.isMoving: self.ralph.stop() self.ralph.pose("walk", 5) self.isMoving = False # update pawns shot or set up new shot after it reaches a certain distance node = NodePath("tmp") node.setHpr(self.pawn.getHpr()) vec = render.getRelativeVector(node,(random.random() * -0.8,random.random() + 1,0)) self.shot.setPos(self.shot.getPos() + self.vec * dt * 10 ) if self.shot.getY() < -15 or self.shot.getY() > 30 or self.shot.getX() < 5 or self.shot.getX() > 15: self.shot.setPos(self.pawn.getPos() + (0,0,0)) self.vec = render.getRelativeVector(node,(random.random() * -0.8,random.random() + 1,0)) self.vec = render.getRelativeVector(node,(random.random() * random.randrange(-1,2),random.random() + 1,0)) # If the camera is too far from ralph, move it closer. # If the camera is too close to ralph, move it farther. #self.camera.lookAt(self.floater) camvec = self.ralph.getPos() - self.camera.getPos() #camvec = Vec3(0,camvec.getY(),0) camdist = camvec.length() x = self.camera.getZ() camvec.normalize() #if camdist > 6.0: # self.camera.setPos(self.camera.getPos() + camvec * (camdist - 6)) #if camdist < 6.0: # self.camera.setPos(self.camera.getPos() - camvec * (6 - camdist)) # Normally, we would have to call traverse() to check for collisions. # However, the class ShowBase that we inherit from has a task to do # this for us, if we assign a CollisionTraverser to self.cTrav. #self.cTrav.traverse(render) # Adjust camera so it stays at same height if self.cameraCollided == False: if self.camera.getZ() < self.ralph.getZ() + 1 or self.camera.getZ() > self.ralph.getZ() + 1: self.camera.setZ(self.ralph.getZ() + 1) # The camera should look in ralph's direction, # but it should also try to stay horizontal, so look at # a floater which hovers above ralph's head. self.camera.lookAt(self.floater) entries = list(self.orbCollisionHandler.getEntries()) if(len(entries) > 0): #self.lightpivot.reparentTo(NodePath()) orbCollected = False self.cameraCollided = False self.ralphSpeed = 65 for entry in self.orbCollisionHandler.getEntries(): #print(entry) fromColNp = entry.getFromNodePath() toColNp = entry.getIntoNodePath() if fromColNp.getName() == "orbColPath" and toColNp.getName() == "ralphColNode": if orbCollected == False: fromColNp.getParent().reparentTo(NodePath()) self.orbTxt.destroy() self.numOrbs += 1 str1 = "Orbs: " + str(self.numOrbs) self.orbTxt = utilsKristina2.addInstructions(.18, str1) orbCollected = True elif toColNp.getName() == "orbColPath" and fromColNp.getName() == "ralphColNode": if orbCollected == False: toColNp.getParent().reparentTo(NodePath()) self.orbTxt.destroy() self.numOrbs += 1 str1 = "Orbs: " + str(self.numOrbs) self.orbTxt = utilsKristina2.addInstructions(.18, str1) orbCollected = True elif toColNp.getName() == "ralphOrbColPath" and (fromColNp.getName()[:-1] == "chrisColPath" or fromColNp.getName()[:-2] == "chrisColPath"): toColNp.getParent().setZ(20) for chriss in self.chrisList: if chriss.chrisColName == fromColNp.getName(): chris = chriss break chris.chrisHealth = chris.chrisHealth - 1 chris.chris.setColor(1,0,0,1) chris.chrisHit = True chris.chrisRedTime = globalClock.getFrameTime() #print chris.chrisRedTime if chris.chrisHealth < 0: fromColNp.getParent().removeNode() chris.chrisAlive = False chris.chrisShot.setZ(26) elif (toColNp.getName()[:-1] == "chrisColPath" or toColNp.getName()[:-2] == "chrisColPath") and fromColNp.getName() == "ralphOrbColPath": fromColNp.getParent().setZ(20) for chriss in self.chrisList: if chriss.chrisColName == toColNp.getName(): chris = chriss break chris.chrisHealth = chris.chrisHealth - 1 chris.chris.setColor(1,0,0,1) chris.chrisHit = True chris.chrisRedTime = globalClock.getFrameTime() #print chris.chrisRedTime if chris.chrisHealth < 0: toColNp.getParent().removeNode() chris.chrisAlive = False chris.chrisShot.setZ(26) elif toColNp.getName() == "enemyOrbColPath" and fromColNp.getName() == "ralphColNode": toColNp.getParent().setZ(26) self.healthTxt.destroy() self.healthCount -= 3 str1 = "Health: " + str(self.healthCount) self.healthTxt = utilsKristina2.addInstructions(.06, str1) self.ralphHit = True self.ralph.setColor((1,0,0,1)) self.ralphRedTime = globalClock.getFrameTime() if self.healthCount <=0: sys.exit() elif toColNp.getName() == "ralphColNode" and fromColNp.getName() == "enemyOrbColPath": fromColNp.getParent().setZ(26) self.healthTxt.destroy() self.healthCount -= 3 str1 = "Health: " + str(self.healthCount) self.healthTxt = utilsKristina2.addInstructions(.06, str1) self.ralphHit = True self.ralph.setColor((1,0,0,1)) self.ralphRedTime = globalClock.getFrameTime() if self.healthCount <=0: sys.exit() elif fromColNp.getName() == "ralphOrbColPath" and toColNp.getName() == "allinclusive": fromColNp.getParent().setZ(50) elif toColNp.getName() == "ralphOrbColPath" and fromColNp.getName() == "allinclusive": toColNp.getParent().setZ(50) elif fromColNp.getName() == "enemyOrbWallCheck" and toColNp.getName() == "allinclusive": fromColNp.getParent().setZ(50) #print toColNp.getName() elif toColNp.getName() == "enemyOrbWallCheck" and fromColNp.getName() == "allinclusive": toColNp.getParent().setZ(50) #print fromColNp.getName() elif fromColNp.getName() == "ralphWallCheck" and toColNp.getName() == "allinclusive": #node = NodePath("tmp") #node.setHpr(self.ralph.getHpr()) #vec = render.getRelativeVector(node,(0,-1,0)) #self.ralph.setPos(self.ralph.getPos()-vec) #fromColNp.getParent().setZ(26) self.ralphSpeed = 25 elif toColNp.getName() == "ralphWallCheck" and fromColNp.getName() == "allinclusive": #node = NodePath("tmp") #node.setHpr(self.ralph.getHpr()) #vec = render.getRelativeVector(node,(0,-1,0)) #self.ralph.setPos(self.ralph.getPos()-vec) #print "wtf" #toColNp.getParent().setZ(26) self.ralphSpeed = 25 elif fromColNp.getName() == "ralphWallCheck2" and toColNp.getName() == "allinclusive": #node = NodePath("tmp") #node.setHpr(self.ralph.getHpr()) #vec = render.getRelativeVector(node,(0,1,0)) #self.ralph.setPos(self.ralph.getPos()-vec) #fromColNp.getParent().setZ(26) self.ralphSpeed = 25 elif toColNp.getName() == "ralphWallCheck2" and fromColNp.getName() == "allinclusive": #node = NodePath("tmp") #node.setHpr(self.ralph.getHpr()) #vec = render.getRelativeVector(node,(0,1,0)) #self.ralph.setPos(self.ralph.getPos()-vec) #self.camera.setPos(self.ralph.getPos()) #self.cameraCollided = True self.ralphSpeed = 25 elif fromColNp.getName() == "ralphWallCheck3" and toColNp.getName() == "allinclusive": #node = NodePath("tmp") #node.setHpr(self.ralph.getHpr()) #vec = render.getRelativeVector(node,(-1,0,0)) #self.ralph.setPos(self.ralph.getPos()-vec) #fromColNp.getParent().setZ(26) self.ralphSpeed = 25 print "3" elif toColNp.getName() == "ralphWallCheck3" and fromColNp.getName() == "allinclusive": #node = NodePath("tmp") #node.setHpr(self.ralph.getHpr()) #vec = render.getRelativeVector(node,(-1,0,0)) #self.ralph.setPos(self.ralph.getPos()-vec) #self.camera.setPos(self.ralph.getPos()) #self.cameraCollided = True self.ralphSpeed = 25 print "3" elif fromColNp.getName() == "ralphWallCheck4" and toColNp.getName() == "allinclusive": #node = NodePath("tmp") #node.setHpr(self.ralph.getHpr()) #vec = render.getRelativeVector(node,(1,0,0)) #self.ralph.setPos(self.ralph.getPos()-vec) #fromColNp.getParent().setZ(26) self.ralphSpeed = 25 print "4" elif toColNp.getName() == "ralphWallCheck4" and fromColNp.getName() == "allinclusive": #node = NodePath("tmp") #node.setHpr(self.ralph.getHpr()) #vec = render.getRelativeVector(node,(1,0,0)) #self.ralph.setPos(self.ralph.getPos()-vec) #self.camera.setPos(self.ralph.getPos()) #self.cameraCollided = True self.ralphSpeed = 25 print "4" return task.cont
class RoamingRalphDemo(ShowBase): def __init__(self): # Set up the window, camera, etc. ShowBase.__init__(self) #self.setupCD() # Set the background color to black # self.win.setClearColor((0.6, 0.6, 1.0, 1.0)) # self.fog = Fog('myFog') # self.fog.setColor(0, 0, 0) # self.fog.setExpDensity(.05) # render.setFog(self.fog) # This is used to store which keys are currently pressed. self.keyMap = { "left": 0, "right": 0, "forward": 0, "cam-left": 0, "cam-right": 0, "c":0, "back":0, "space":0} # Post the instructions #self.title = addTitle( # "Panda3D Tutorial: Roaming Ralph (Walking on Uneven Terrain)") self.inst1 = addInstructions(0.06, "[ESC]: Quit") self.inst2 = addInstructions(0.12, "[Left Arrow]: Rotate Ralph Left") self.inst3 = addInstructions(0.18, "[Right Arrow]: Rotate Ralph Right") self.inst4 = addInstructions(0.24, "[Up Arrow]: Run Ralph Forward") #self.inst6 = addInstructions(0.30, "[A]: Rotate Camera Left") #self.inst7 = addInstructions(0.36, "[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/world") #self.environ.reparentTo(render) self.room = loader.loadModel("models/room2.egg") self.room.reparentTo(render) #self.room.setScale(.1) self.room.setPos(0,0,-5) self.room.setShaderAuto() #self.room.writeBamFile("myRoom1.bam") #self.room.setColor(1,.3,.3,1) self.room2 = loader.loadModel("models/abstractroom2") self.room2.reparentTo(render) self.room2.setScale(.1) self.room2.setPos(-12,0,0) # Create the main character, Ralph #ralphStartPos = LVecBase3F(0,0,0) #self.room.find("**/start_point").getPos() self.ralph = Actor("models/ralph", {"run": "models/ralph-run", "walk": "models/ralph-walk"}) self.ralph.reparentTo(render) self.ralph.setScale(.2) self.ralph.setPos(0,0,0) #cs = CollisionSphere(0, 0, 0, 1) #cnodePath = self.ralph.attachNewNode(CollisionNode('cnode')) #cnodePath.node().addSolid(cs) #cnodePath.node().setPos(0,0,0) #cnodePath.show() self.gianteye = loader.loadModel("models/gianteye") self.gianteye.reparentTo(render) self.gianteye.setScale(.1) self.gianteye.setPos(10,10,0) #self.bluefinal = loader.loadModel("models/chrysalis") #self.bluefinal.reparentTo(render) #self.bluefinal.setScale(.1) #self.bluefinal.setPos(7,7,0) #self.blue = loader.loadModel("models/blue1") #self.blue.reparentTo(render) #self.blue.setScale(.1) #self.blue.setPos(10,5,0) self.chik = loader.loadModel("models/chik") self.chik.reparentTo(render) self.chik.setScale(.1) self.chik.setPos(3,13,0) self.pawn = loader.loadModel("pawn") self.pawn.reparentTo(render) self.pawn.setPos(0,0,0) self.shot = loader.loadModel("models/icosphere.egg") self.shot.reparentTo(render) self.shot.setScale(.5) self.shot.setPos(0,0,1) self.shot.setColor(1,.3,.3,1) self.myShot = loader.loadModel("models/icosphere.egg") #self.myShot.reparentTo(render) self.myShot.setScale(.1) self.myShot.setPos(0,0,1) self.myShotVec = LVector3(0,0,0) self.lightpivot3 = render.attachNewNode("lightpivot3") self.lightpivot3.setPos(0, 0, 0) self.lightpivot3.hprInterval(10, LPoint3(0, 0, 0)).loop() plight3 = PointLight('plight2') plight3.setColor((0, .3,0, 1)) plight3.setAttenuation(LVector3(0.7, 0.05, 0)) plnp3 = self.lightpivot3.attachNewNode(plight3) plnp3.setPos(0, 0, 0) self.room2.setLight(plnp3) self.room.setLight(plnp3) sphere3 = loader.loadModel("models/icosphere") sphere3.reparentTo(plnp3) sphere3.setScale(0.1) sphere3.setColor((0,1,0,1)) # Create a floater object, which floats 2 units above ralph. We # use this as a target for the camera to look at. self.floater = NodePath(PandaNode("floater")) self.floater.reparentTo(self.ralph) self.floater.setZ(8.0) # Accept the control keys for movement and rotation self.accept("escape", sys.exit) self.accept("arrow_left", self.setKey, ["left", True]) self.accept("arrow_right", self.setKey, ["right", True]) self.accept("arrow_up", self.setKey, ["forward", True]) self.accept("arrow_down", self.setKey, ["back", True]) self.accept("a", self.setKey, ["cam-left", True]) self.accept("s", self.setKey, ["cam-right", True]) self.accept("arrow_left-up", self.setKey, ["left", False]) self.accept("arrow_right-up", self.setKey, ["right", False]) self.accept("arrow_up-up", self.setKey, ["forward", False]) self.accept("arrow_down-up", self.setKey, ["back", False]) self.accept("a-up", self.setKey, ["cam-left", False]) self.accept("s-up", self.setKey, ["cam-right", False]) self.accept("space", self.setKey, ["space", True]) self.accept("space-up", self.setKey, ["space", False]) self.accept("c",self.setKey,["c",True]) self.accept("c-up",self.setKey,["c",False]) taskMgr.add(self.move, "moveTask") # Game state variables self.isMoving = False self.jumping = False self.vz = 0 # Set up the camera self.disableMouse() self.camera.setPos(self.ralph.getX(), self.ralph.getY() + 7, 3) self.camLens.setFov(60) # 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. def setupCollision(self): cs = CollisionSphere(0,0,2,1) cnodePath = self.ralph.attachNewNode(CollisionNode('cnode')) cnodePath.node().addSolid(cs) cnodePath.show() #for o in self.OBS: #ct = CollisionTube(0,0,0, 0,0,1, 0.5) #cn = o.attachNewNode(CollisionNode('ocnode')) #cn.node().addSolid(ct) #cn.show() eyecs = CollisionSphere(0,0,4,5) cnodePath = self.gianteye.attachNewNode(CollisionNode('cnode')) cnodePath.node().addSolid(eyecs) cnodePath.show() eyecs = CollisionSphere(0,0,4,2) cnodePath = self.chik.attachNewNode(CollisionNode('cnode')) cnodePath.node().addSolid(eyecs) cnodePath.show() pusher = CollisionHandlerPusher() pusher.addCollider(cnodePath, self.player) self.cTrav = CollisionTraverser() self.cTrav.add_collider(cnodePath,pusher) self.cTrav.showCollisions(render) self.walls = self.room2.find("**/wall_collide") #self.walls.node().setIntoCollideMask(BitMask32.bit(0)) self.cTrav = CollisionTraverser() self.ralphGroundRay = CollisionRay() self.ralphGroundRay.setOrigin(0, 0, 9) self.ralphGroundRay.setDirection(0, 0, -1) self.ralphGroundCol = CollisionNode('ralphRay') self.ralphGroundCol.addSolid(self.ralphGroundRay) self.ralphGroundCol.setFromCollideMask(CollideMask.bit(0)) self.ralphGroundCol.setIntoCollideMask(CollideMask.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, 9) self.camGroundRay.setDirection(0, 0, -1) self.camGroundCol = CollisionNode('camRay') self.camGroundCol.addSolid(self.camGroundRay) self.camGroundCol.setFromCollideMask(CollideMask.bit(0)) self.camGroundCol.setIntoCollideMask(CollideMask.allOff()) self.camGroundColNp = self.camera.attachNewNode(self.camGroundCol) self.camGroundHandler = CollisionHandlerQueue() self.cTrav.addCollider(self.camGroundColNp, self.camGroundHandler) self.sphere = CollisionSphere(0,0,4,2) self.sphere2 = CollisionSphere(0,0,2,2) self.cnodePath = self.ralph.attachNewNode((CollisionNode('cnode'))) self.cnodePath.node().addSolid(self.sphere) self.cnodePath.node().addSolid(self.sphere2) self.cnodePath.show() self.pusher = CollisionHandlerPusher() self.pusher.addCollider(self.cnodePath, self.ralph) self.cTrav.add_collider(self.cnodePath, self.pusher) self.eyecs = CollisionSphere(0,0,22,25) self.cnodePath1 = self.gianteye.attachNewNode(CollisionNode('cnode')) self.cnodePath1.node().addSolid(self.eyecs) self.cnodePath1.show() self.pusher1 = CollisionHandlerPusher() self.pusher1.addCollider(self.cnodePath1, self.gianteye) self.cTrav.add_collider(self.cnodePath1, self.pusher1) self.cTrav.showCollisions(render) self.eyeGroundRay = CollisionRay() self.eyeGroundRay.setOrigin(0, 0, 9) self.eyeGroundRay.setDirection(0, 0, -1) self.eyeGroundCol = CollisionNode('eyeRay') self.eyeGroundCol.addSolid(self.eyeGroundRay) self.eyeGroundCol.setFromCollideMask(CollideMask.bit(0)) self.eyeGroundCol.setIntoCollideMask(CollideMask.allOff()) self.eyeGroundColNp = self.gianteye.attachNewNode(self.eyeGroundCol) self.eyeGroundHandler = CollisionHandlerQueue() self.cTrav.addCollider(self.eyeGroundColNp, self.eyeGroundHandler) self.chikcs = CollisionSphere(0,0,11,20) self.cnodePath2 = self.chik.attachNewNode(CollisionNode('cnode')) self.cnodePath2.node().addSolid(self.chikcs) self.cnodePath2.show() self.pusher2 = CollisionHandlerPusher() self.pusher2.addCollider(self.cnodePath, self.chik) self.cTrav.add_collider(self.cnodePath, self.pusher2) self.cTrav.showCollisions(render) self.chikGroundRay = CollisionRay() self.chikGroundRay.setOrigin(0, 0, 9) self.chikGroundRay.setDirection(0, 0, -1) self.chikGroundCol = CollisionNode('chikRay') self.chikGroundCol.addSolid(self.chikGroundRay) self.chikGroundCol.setFromCollideMask(CollideMask.bit(0)) self.chikGroundCol.setIntoCollideMask(CollideMask.allOff()) self.chikGroundColNp = self.chik.attachNewNode(self.chikGroundCol) self.chikGroundHandler = CollisionHandlerQueue() self.cTrav.addCollider(self.chikGroundColNp, self.chikGroundHandler) # Uncomment this line to see the collision rays self.ralphGroundColNp.show() self.camGroundColNp.show() #self.ralphroom1ColNp.show() # Uncomment this line to show a visual representation of the # collisions occuring #self.cTrav.showCollisions(render) # Create some lighting ambientLight = AmbientLight("ambientLight") ambientLight.setColor((.3, .3, .3, .4)) ambientLight2 = AmbientLight("ambientLight2") ambientLight2.setColor((1, 1, 1, 10)) directionalLight = DirectionalLight("directionalLight") directionalLight.setDirection((0, 0, -2)) directionalLight.setColor((1, 1, 1, 1)) directionalLight.setSpecularColor((1, 1, 1, 1)) render.setLight(render.attachNewNode(ambientLight)) #self.environ.setLight(self.environ.attachNewNode(ambientLight2)) render.setLight(render.attachNewNode(directionalLight)) # Add a light to the scene. self.lightpivot = render.attachNewNode("lightpivot") self.lightpivot.setPos(0, 0, 1.6) self.lightpivot.hprInterval(20, LPoint3(360, 0, 0)).loop() plight = PointLight('plight') plight.setColor((.7, .3, 0, 1)) plight.setAttenuation(LVector3(0.7, 0.05, 0)) plnp = self.lightpivot.attachNewNode(plight) plnp.setPos(5, 0, 0) self.room.setLight(plnp) sphere = loader.loadModel("models/icosphere") sphere.reparentTo(plnp) sphere.setScale(0.1) sphere.setColor((1,1,0,1)) self.lightpivot2 = render.attachNewNode("lightpivot") self.lightpivot2.setPos(-16, 0, 1.6) self.lightpivot2.hprInterval(20, LPoint3(360, 0, 0)).loop() plight2 = PointLight('plight2') plight2.setColor((0, .4,.8, 1)) plight2.setAttenuation(LVector3(0.7, 0.05, 0)) plnp2 = self.lightpivot2.attachNewNode(plight2) plnp2.setPos(5, 0, 0) self.room2.setLight(plnp2) sphere2 = loader.loadModel("models/icosphere") sphere2.reparentTo(plnp2) sphere2.setScale(0.2) sphere2.setColor((0,0,1,1)) self.vec = LVector3(0,1,0) # Records the state of the arrow keys def setKey(self, key, value): self.keyMap[key] = value # Accepts arrow keys to move either the player or the menu cursor, # Also deals with grid checking and collision detection def move(self, task): # Get the time that elapsed since last frame. We multiply this with # the desired speed in order to find out with which distance to move # in order to achieve that desired speed. dt = globalClock.getDt() # If the camera-left key is pressed, move camera left. # If the camera-right key is pressed, move camera right. if self.keyMap["cam-left"]: self.camera.setZ(self.camera, -20 * dt) if self.keyMap["cam-right"]: self.camera.setZ(self.camera, +20 * dt) # save ralph's initial position so that we can restore it, # in case he falls off the map or runs into something. startpos = self.ralph.getPos() # If a move-key is pressed, move ralph in the specified direction. if self.keyMap["left"]: self.ralph.setH(self.ralph.getH() + 150 * dt) #self.floater.setH(self.floater.getH() + 300 * dt) self.camera.setX(self.camera, +15.5 * dt) if self.keyMap["right"]: self.ralph.setH(self.ralph.getH() - 150 * dt) self.camera.setX(self.camera, -15.5 * dt) if self.keyMap["forward"]: self.ralph.setY(self.ralph, -35 * dt) if self.keyMap["back"]: self.ralph.setY(self.ralph, +35 * dt) if self.keyMap["c"]: if self.jumping is False: #self.ralph.setH(self.ralph.getH() + 300 * dt) #self.ralph.setZ(self.ralph.getZ() + 100 * dt) self.jumping = True self.vz = 7 if self.keyMap["space"]: self.lightpivot3.setPos(self.ralph.getPos()) self.lightpivot3.setZ(self.ralph.getZ() + .5) self.lightpivot3.setX(self.ralph.getX() - .25) #self.myShot.setHpr(self.ralph.getHpr()) #parent node = NodePath("tmp") node.setHpr(self.ralph.getHpr()) vec = render.getRelativeVector(node,(0,-1,0)) self.myShotVec = vec self.lightpivot3.setPos(self.lightpivot3.getPos() + self.myShotVec * dt * 15 ) if self.jumping is True: self.vz = self.vz - 16* dt self.ralph.setZ(self.ralph.getZ() + self.vz * dt ) entries = list(self.ralphGroundHandler.getEntries()) entries.sort(key=lambda x: x.getSurfacePoint(render).getZ()) if len(entries) > 0 : if self.ralph.getZ() < 0:#entries[0].getSurfacePoint(render).getZ(): #self.ralph.setZ(entries[0].getSurfacePoint(render).getZ()) self.ralph.setZ(0) self.jumping = False # If ralph is moving, loop the run animation. # If he is standing still, stop the animation. if self.keyMap["forward"] or self.keyMap["left"] or self.keyMap["right"] or self.keyMap["c"] or self.keyMap["forward"] or self.keyMap["back"]: if self.isMoving is False: self.ralph.loop("run") self.isMoving = True else: if self.isMoving: self.ralph.stop() self.ralph.pose("walk", 5) self.isMoving = False node = NodePath("tmp") node.setHpr(self.ralph.getHpr()) vec = render.getRelativeVector(node,(1,0,0)) #self.ralph.setPos(self.ralph.getPos() + vec * dt * 20) node = NodePath("tmp") #self.pawn.getH() node.setHpr(self.pawn.getHpr()) vec = render.getRelativeVector(node,(random.random() * -0.8,random.random() + 1,0)) self.shot.setPos(self.shot.getPos() + self.vec * dt * 10 ) if self.shot.getY() < -15 or self.shot.getY() > 15 or self.shot.getX() < -15 or self.shot.getX() > 15: self.shot.setPos(self.pawn.getPos() + (0,0,0)) self.vec = render.getRelativeVector(node,(random.random() * -0.8,random.random() + 1,0)) self.vec = render.getRelativeVector(node,(random.random() * -0.8,random.random() + 1,0)) # If the camera is too far from ralph, move it closer. # If the camera is too close to ralph, move it farther. camvec = self.ralph.getPos() - self.camera.getPos() #camvec.setZ(self.camera.getZ()) camdist = camvec.length() x = self.camera.getZ() camvec.normalize() if camdist > 6.0: self.camera.setPos(self.camera.getPos() + camvec * (camdist - 6)) camdist = 10.0 #self.camera.setZ(self.camera, x) if camdist < 6.0: self.camera.setPos(self.camera.getPos() - camvec * (6 - camdist)) camdist = 5.0 #self.camera.setZ(self.camera, x) # Normally, we would have to call traverse() to check for collisions. # However, the class ShowBase that we inherit from has a task to do # this for us, if we assign a CollisionTraverser to self.cTrav. #self.cTrav.traverse(render) # Adjust ralph's Z coordinate. If ralph's ray hit terrain, # update his Z. If it hit anything else, or didn't hit anything, put # him back where he was last frame. entries = list(self.ralphGroundHandler.getEntries()) entries.sort(key=lambda x: x.getSurfacePoint(render).getZ()) if self.jumping == False: if len(entries) > 0:# and entries[0].getIntoNode().getName() == "terrain": #self.ralph.setZ(entries[0].getSurfacePoint(render).getZ()) pass else: self.ralph.setPos(startpos) # Keep the camera at one foot above the terrain, # or two feet above ralph, whichever is greater. entries = list(self.camGroundHandler.getEntries()) entries.sort(key=lambda x: x.getSurfacePoint(render).getZ()) #if len(entries) > 0 and entries[0].getIntoNode().getName() == "ground": #self.camera.setZ(entries[0].getSurfacePoint(render).getZ() + 1.5) if self.camera.getZ() < self.ralph.getZ() + 1 or self.camera.getZ() > self.ralph.getZ() + 1: self.camera.setZ(self.ralph.getZ() + 1) #self.camera.setZ(self.ralph.getZ() + 1.5) #self.camera.setP(self.camera, 130) # The camera should look in ralph's direction, # but it should also try to stay horizontal, so look at # a floater which hovers above ralph's head. self.camera.lookAt(self.floater) return task.cont
class Player: STATE_IDLE = "Idle" STATE_WALK = "Walk" STATE_RUN = "Run" STATE_JUMP = "Jump" STATE_RUNJUMP = "RunJump" STATE_FALL = "Fall" STATE_DUCK = "Duck" STATE_UN_DUCK = "UnDuck" STATE_FLOAT = "Float" STATE_SWIM = "Swim" SUB_STATE_GRAB = "Grab" JUMP_ACCEL = 3.5 FALL_ACCEL = -9.81 TERRAIN_NONE = 0 TERRAIN_GROUND = 1 TERRAIN_WATER = 2 TERRAIN_AIR = 3 DUCK_FRAME_COUNT = 0 DUCK_FRAME_MID = 0 SUNK_CUTOFF = -0.9 def __init__(self, base): self.base = base self.keyState = { "WalkFw": False, "WalkBw": False, "Run": False, "RotateL": False, "RotateR": False, "Jump": False, "Duck": False } self.isKeyDown = self.base.mouseWatcherNode.isButtonDown self.state = Player.STATE_IDLE self.sub_state = None self.walkDir = 0 self.rotationDir = 0 self.zVelocity = 0 self.zOffset = None self.jumpHeight = None self.terrainZone = Player.TERRAIN_NONE self.terrainSurfZ = None self.waterDepth = 0 self.collidedObjects = list() # actor anims = { "idle": "models/player-idle", "walk": "models/player-walk", "run": "models/player-run", "jump": "models/player-jump", "duck": "models/player-duck", "float": "models/player-float", "swim": "models/player-swim", "grab": "models/player-grab" } self.actor = Actor("models/player", anims) self.actor.reparentTo(self.base.render) self.actor.setH(200) log.debug("actor tight bounds is %s" % str(self.actor.getTightBounds())) # animation info Player.DUCK_FRAME_COUNT = self.actor.getNumFrames("duck") Player.DUCK_FRAME_MID = int(Player.DUCK_FRAME_COUNT / 2) # camara point self.camNode = NodePath("camNode") self.camNode.reparentTo(self.actor) self.camNode.setPos(0, 0, 1) # collision # ray collRay = CollisionRay(0, 0, 1.5, 0, 0, -1) collRayN = CollisionNode("playerCollRay") collRayN.addSolid(collRay) collRayN.setFromCollideMask(1) collRayN.setIntoCollideMask(CollideMask.allOff()) collRayNP = self.actor.attachNewNode(collRayN) self.collQRay = CollisionHandlerQueue() self.base.cTrav.addCollider(collRayNP, self.collQRay) # sphere mask 2 collSphere2 = CollisionSphere(0, 0, 0.5, 0.25) collSphere2N = CollisionNode("playerCollSphere2") collSphere2N.addSolid(collSphere2) collSphere2N.setFromCollideMask(2) collSphere2N.setIntoCollideMask(CollideMask.allOff()) self.collSphere2NP = self.actor.attachNewNode(collSphere2N) self.collPSphere = CollisionHandlerPusher() self.collPSphere.addCollider(self.collSphere2NP, self.actor) self.base.cTrav.addCollider(self.collSphere2NP, self.collPSphere) # key events self.base.accept("i", self.dump_info) # task self.base.taskMgr.add(self.update, "playerUpdateTask") def defineKeys(self): for k in self.keyState.keys(): self.keyState[k] = False if self.isKeyDown(KeyboardButton.up()): self.keyState["WalkFw"] = True if self.isKeyDown(KeyboardButton.down()): self.keyState["WalkBw"] = True if self.isKeyDown(KeyboardButton.left()): self.keyState["RotateL"] = True if self.isKeyDown(KeyboardButton.right()): self.keyState["RotateR"] = True if self.isKeyDown(KeyboardButton.shift()): self.keyState["Run"] = True if self.isKeyDown(KeyboardButton.space()): self.keyState["Jump"] = True if self.isKeyDown(KeyboardButton.asciiKey("d")): self.keyState["Duck"] = True def defineState(self): # newState = self.state # keys states ks = self.keyState # state force if self.zOffset > 0.2 and self.state != Player.STATE_FALL: newState = Player.STATE_FALL # from Idle -> Walk, Jump if self.state == Player.STATE_IDLE: # Walk if ks["WalkFw"] or ks["WalkBw"] or ks["RotateL"] or ks["RotateR"]: newState = Player.STATE_WALK elif ks["Jump"]: newState = Player.STATE_JUMP elif ks["Duck"]: newState = Player.STATE_DUCK # from Walk, Run -> Walk, Run, Idle; from Run -> Jump elif self.state == Player.STATE_WALK or self.state == Player.STATE_RUN: if ks["Run"] and self.state != Player.STATE_RUN and self.terrainZone != Player.TERRAIN_WATER: newState = Player.STATE_RUN elif not ks["Run"] and self.state == Player.STATE_RUN: newState = Player.STATE_WALK if ks["WalkFw"]: self.walkDir = -1 elif ks["WalkBw"]: self.walkDir = 1 elif not ks["WalkFw"] and not ks["WalkBw"]: self.walkDir = 0 if ks["RotateL"]: self.rotationDir = 1 elif ks["RotateR"]: self.rotationDir = -1 elif not ks["RotateL"] and not ks["RotateR"]: self.rotationDir = 0 if ks["Jump"]: newState = Player.STATE_RUNJUMP if self.walkDir == 0 and self.rotationDir == 0: newState = Player.STATE_IDLE # from Jump -> Fall elif self.state == Player.STATE_JUMP or self.state == Player.STATE_RUNJUMP: if self.zVelocity > 0: newState = Player.STATE_FALL # from Fall -> Idle elif self.state == Player.STATE_FALL: if self.zOffset <= 0: newState = Player.STATE_IDLE self.jumpHeight = None self.zVelocity = 0 self.walkDir = 0 # from Duck -> UnDuck elif self.state == Player.STATE_DUCK: if not ks["Duck"]: newState = Player.STATE_UN_DUCK # from UnDuck -> Idle elif self.state == Player.STATE_UN_DUCK: if not self.actor.getCurrentAnim() == "duck": newState = Player.STATE_IDLE return newState def processState(self, dt): # terrain sdjustment if self.zOffset <= 0.2 and not self.state == Player.STATE_FALL: self.actor.setZ(self.terrainSurfZ) # idle if self.state == Player.STATE_IDLE: self.collSphere2NP.setZ(0) # walk if self.walkDir != 0: if self.zVelocity == 0: speed = 3.6 if self.state == Player.STATE_RUN else 2.4 else: speed = 3.2 if self.terrainZone == Player.TERRAIN_WATER: speed *= 0.5 self.actor.setY(self.actor, speed * self.walkDir * dt) if self.rotationDir != 0: self.actor.setH(self.actor.getH() + 3.5 * self.rotationDir) # jump if self.state == Player.STATE_JUMP or self.state == Player.STATE_RUNJUMP: self.zVelocity = Player.JUMP_ACCEL log.debug("jump start at v=%f" % self.zVelocity) if self.state == Player.STATE_RUNJUMP: self.walkDir = -1 # fall if self.state == Player.STATE_FALL: dZ = self.zVelocity * dt dV = Player.FALL_ACCEL * dt curZ = self.actor.getZ() newZ = curZ + dZ if self.jumpHeight == None and newZ < curZ: self.jumpHeight = self.zOffset log.debug("jump height=%f" % self.jumpHeight) log.debug( "falling... dt=%(dt)f getZ=%(getZ)f v=%(v)f dZ=%(dZ)f newZ=%(newZ)f dV=%(dV)f zOffset=%(zOff)f" % { "dt": dt, "getZ": self.actor.getZ(), "v": self.zVelocity, "dZ": dZ, "newZ": newZ, "dV": dV, "zOff": self.zOffset }) if newZ < self.terrainSurfZ: newZ = self.terrainSurfZ self.actor.setZ(newZ) self.zVelocity += dV # duck if self.state == Player.STATE_DUCK: if self.actor.getCurrentAnim() == "duck": collSphrZ = (self.actor.getCurrentFrame("duck") / Player.DUCK_FRAME_MID) * 0.25 self.collSphere2NP.setZ(-collSphrZ) def processTerrainRelation( self): # -> [terrainZone, terrainSurfZ, zOffset, waterDepth] collEntries = self.collQRay.getEntries() newZone = None # if len(collEntries) == 0: #log.error("out of terrain, pos=%s"%str(self.actor.getPos())) newZone = Player.TERRAIN_NONE if newZone != self.terrainZone: self.onTerrainZoneChanged(Player.TERRAIN_NONE) self.terrainZone = newZone return Player.TERRAIN_NONE, 0, 0, 0 # newZone = Player.TERRAIN_NONE gndZ, wtrZ = -1000, -1000 waterDepth = 0 for entry in collEntries: eName = entry.getIntoNodePath().getName() eZ = entry.getSurfacePoint(self.base.render).getZ() if eName.startswith("Water"): wtrZ = eZ if wtrZ > gndZ: newZone = Player.TERRAIN_WATER else: if eZ > gndZ: gndZ = eZ if gndZ > wtrZ: newZone = Player.TERRAIN_GROUND if newZone == Player.TERRAIN_WATER: waterDepth = gndZ - wtrZ #log.debug("water depth is %f"%waterDepth) zOffset = self.actor.getZ() - gndZ return newZone, gndZ, zOffset, waterDepth def onTerrainZoneChanged(self, zone): log.debug("terrain zone chaged to: %i" % zone) def onStateChanged(self, newState): curState = self.state log.debug("state change %s -> %s" % (str(curState), str(newState))) #self.actor.stop() if newState == Player.STATE_IDLE: self.actor.loop("idle") elif newState == Player.STATE_WALK: self.actor.setPlayRate(4.0, "walk") self.actor.loop("walk") elif newState == Player.STATE_RUN: self.actor.loop("run") elif newState == Player.STATE_JUMP or newState == Player.STATE_RUNJUMP: self.actor.setPlayRate(1.4, "jump") self.actor.play("jump", fromFrame=20, toFrame=59) elif newState == Player.STATE_DUCK: self.actor.play("duck", fromFrame=0, toFrame=Player.DUCK_FRAME_MID) elif newState == Player.STATE_UN_DUCK: initFrame = Player.DUCK_FRAME_MID if self.actor.getCurrentAnim() == "duck": initFrame = Player.DUCK_FRAME_COUNT - self.actor.getCurrentFrame( "duck") self.actor.stop() self.actor.play("duck", fromFrame=initFrame, toFrame=Player.DUCK_FRAME_COUNT) def updateCollidedObjectsList(self): pass def update(self, task): # clock dt = self.base.taskMgr.globalClock.getDt() # keys self.defineKeys() # terrain relation newZone, self.terrainSurfZ, self.zOffset, self.waterDepth = self.processTerrainRelation( ) if newZone != self.terrainZone: self.terrainZone = newZone self.onTerrainZoneChanged(self.terrainZone) # obstacles relation self.updateCollidedObjectsList() # state newState = self.defineState() if self.state != newState: self.onStateChanged(newState) self.state = newState self.processState(dt) # move return task.cont def dump_info(self): info = "position: %s\n" % str(self.actor.getPos()) info += "hpr: %s\n" % str(self.actor.getHpr()) info += "state: %s; sub_state: %s\n" % (str( self.state), str(self.sub_state)) info += "terrainZone: %s\n" % str(self.terrainZone) info += "terrainSurfZ: %s\n" % str(self.terrainSurfZ) info += "zOffset: %s\n" % str(self.zOffset) log.info("*INFO:\n%s" % info)
class Demo(ShowBase): # stereo = cv2.StereoBM_create(numDisparities=16, blockSize=15) def __init__(self, img_size=512, screen_off=True, target_area_radius=5, initial_area_radius=10, keyboard_input=False, random_reset_around_target=False, test=False): logging.info('random_reset_around_target :%s', random_reset_around_target) self.random_reset_around_target = random_reset_around_target self.keyboard_input = keyboard_input # Configure the parallax mapping settings (these are just the defaults) self.img_size = img_size self.initial_area_radius = initial_area_radius self.target_area_radius = target_area_radius loadPrcFileData("", "side-by-side-stereo 1") if test: loadPrcFileData("", "load-display p3tinydisplay") loadPrcFileData("", "transform-cache false") loadPrcFileData("", "audio-library-name null") # Prevent ALSA errors loadPrcFileData("", "win-size %d %d" % (2 * img_size, img_size)) loadPrcFileData("", "parallax-mapping-samples 3\n" "parallax-mapping-scale 0.1") if screen_off: # Spawn an offscreen buffer loadPrcFileData("", "window-type offscreen") # Initialize the ShowBase class from which we inherit, which will # create a window and set up everything we need for rendering into it. ShowBase.__init__(self) self.keyMap = { "left": 0, "right": 0, "forward": 0, "cam-left": 0, "cam-right": 0} # Load the 'abstract room' model. This is a model of an # empty room containing a pillar, a pyramid, and a bunch # of exaggeratedly bumpy textures. self.room = loader.loadModel("models/abstractroom") self.room.reparentTo(render) # Create the main character, Ralph ghost = BulletGhostNode('Ghost') ghostNP = render.attachNewNode(ghost) # self.agent = Actor("models/agent", # {"run": "models/agent-run", # "walk": "models/agent-walk"}) self.agent = ghostNP self.agent.reparentTo(render) # self.agent.setScale(.2) target = BulletGhostNode('target') self.navigation_target = render.attachNewNode(target) self.navigation_target.reparentTo(render) # knghit=Knight((0,0,0),(0.3,.3,.3,1)) self.pieces = [Piece(self.room) for _ in range(200)] ################################################## cnodePath = self.room.attachNewNode(CollisionNode('room')) plane = CollisionPlane(Plane(Vec3(1, 0, 0), Point3(-60, 0, 0))) # left cnodePath.node().addSolid(plane) plane = CollisionPlane( Plane(Vec3(-1, 0, 0), Point3(60, 0, 0))) # right cnodePath.node().addSolid(plane) plane = CollisionPlane(Plane(Vec3(0, 1, 0), Point3(0, -60, 0))) # back cnodePath.node().addSolid(plane) plane = CollisionPlane( Plane(Vec3(0, -1, 0), Point3(0, 60, 0))) # front cnodePath.node().addSolid(plane) sphere = CollisionSphere(-25, -25, 0, 12.5) # tube = CollisionTube(-25, -25,0 , -25, -25, 1, 12.5) cnodePath.node().addSolid(sphere) box = CollisionBox(Point3(5, 5, 0), Point3(45, 45, 10)) cnodePath.node().addSolid(box) # cnodePath.show() # Make the mouse invisible, turn off normal mouse controls self.disableMouse() # props = WindowProperties() # props.setCursorHidden(True) # self.win.requestProperties(props) # self.camLens.setFov(60) self.camLens.setFov(80) # Set the current viewing target self.focus = LVector3(55, -55, 20) self.heading = 180 self.pitch = 0 self.mousex = 0 self.mousey = 0 self.last = 0 self.mousebtn = [0, 0, 0] # Start the camera control task: # taskMgr.add(self.controlCamera, "camera-task") # self.accept("escape", sys.exit, [0]) # self.accept("enter", self.toggleShader) # self.accept("j", self.rotateLight, [-1]) # self.accept("k", self.rotateLight, [1]) # self.accept("arrow_left", self.rotateCam, [-1]) # self.accept("arrow_right", self.rotateCam, [1]) # Accept the control keys for movement and rotation self.accept("escape", sys.exit) self.accept("arrow_left", self.setKey, ["left", True]) self.accept("arrow_right", self.setKey, ["right", True]) self.accept("arrow_up", self.setKey, ["forward", True]) self.accept("a", self.setKey, ["cam-left", True]) self.accept("s", self.setKey, ["cam-right", True]) self.accept("arrow_left-up", self.setKey, ["left", False]) self.accept("arrow_right-up", self.setKey, ["right", False]) self.accept("arrow_up-up", self.setKey, ["forward", False]) self.accept("a-up", self.setKey, ["cam-left", False]) self.accept("s-up", self.setKey, ["cam-right", False]) # Add a light to the scene. self.lightpivot = render.attachNewNode("lightpivot") self.lightpivot.setPos(0, 0, 25) self.lightpivot.hprInterval(10, LPoint3(360, 0, 0)).loop() plight = PointLight('plight') plight.setColor((5, 5, 5, 1)) plight.setAttenuation(LVector3(0.7, 0.05, 0)) plnp = self.lightpivot.attachNewNode(plight) plnp.setPos(45, 0, 0) self.room.setLight(plnp) # Add an ambient light alight = AmbientLight('alight') alight.setColor((0.2, 0.2, 0.2, 1)) alnp = render.attachNewNode(alight) self.room.setLight(alnp) # Create a sphere to denote the light sphere = loader.loadModel("models/icosphere") sphere.reparentTo(plnp) # self.cameraModel = self.agent # self.win2 = self.openWindow() # self.win2.removeAllDisplayRegions() # self.dr2 = self.win2.makeDisplayRegion() # camNode = Camera('cam') # camNP = NodePath(camNode) # camNP.reparentTo(self.cameraModel) # camNP.setZ(150) # camNP.lookAt(self.cameraModel) # self.dr2.setCamera(camNP) # self.cam2 = camNP # Camera('cam')p # 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 agent'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() cs = CollisionSphere(0, 0, 0, 0.2) cnodePath = self.agent.attachNewNode(CollisionNode('agent')) cnodePath.node().addSolid(cs) # cnodePath.show() self.ralphGroundHandler = CollisionHandlerQueue() self.cTrav.addCollider(cnodePath, self.ralphGroundHandler) cnodePath = self.navigation_target.attachNewNode( CollisionNode('target')) cnodePath.node().addSolid(CollisionSphere(0, 0, 0, 2)) self.cTrav.addCollider(cnodePath, self.ralphGroundHandler) # Tell Panda that it should generate shaders performing per-pixel # lighting for the room. self.room.setShaderAuto() self.shaderenable = 1 # tex = self.win.getScreenshot() # tex.setFormat(Texture.FDepthComponent) tex = Texture() self.depthmap = tex tex.setFormat(Texture.FDepthComponent) altBuffer = self.win.makeTextureBuffer( "hello", img_size, img_size, tex, True) self.altBuffer = altBuffer altCam = self.makeCamera(altBuffer) altCam.reparentTo(self.agent) # altRender) altCam.setZ(0.4) l = altCam.node().getLens() l.setFov(80) l.setNear(.1) camera.reparentTo(self.agent) camera.setZ(0.4) l = self.camLens # l.setFov(80) l.setNear(.1) # end init def setKey(self, key, value): self.keyMap[key] = value def step(self, action=(0, 0, 0)): h, p, forward = action self.agent.setH(self.agent, h) self.agent.setP(self.agent, p) self.agent.setY(self.agent, forward) relative_target_position = self.__get_relative_target_position() if not None == self.relative_target_position: speed = abs(relative_target_position) - \ abs(self.relative_target_position) assert abs(speed) < 0.02 * 25 * 0.2 * 1.001 * 4 self.relative_target_position = relative_target_position return len(self.get_collision_list()) > 0 dt = 0.02 # 控制跳帧,这个问题在转移到现实的时候需要考虑,摄影机如果能提供固定的拍摄频率就最好了, if len(self.pieces) > 0: for _ in range(5): rng.choice(self.pieces).step(dt) def get_collision_list(self): if self.keyboard_input == False: f = taskMgr.getAllTasks()[-1].getFunction() f(None) f = taskMgr.getAllTasks()[2].getFunction() f(None) else: taskMgr.step() return (self.ralphGroundHandler.getEntries()) relative_target_position = None def __get_relative_target_position(self): from_hpr = self.agent.getHpr() h = from_hpr[0] * math.pi / 180 from_pos = self.agent.getPos() from_pos = from_pos[0] + from_pos[1] * 1j target_pos = self.navigation_target.getPos() target_pos = target_pos[0] + target_pos[1] * 1j relative_pos = target_pos - from_pos rpos = relative_pos * np.exp(-h * 1j) return rpos def get_agent_position(self): return self.agent.getPos() def get_obstacle_positions(self): return [piece.knight.getPos() for piece in self.pieces] def get_obstacle_radius(self): return [piece.radius for piece in self.pieces] def get_target_position(self): return self.navigation_target.getPos() def resetGame(self): self.renderFrame() img = self.getScreen() for p in self.pieces: p.putaway() self.agent.setPos((20, 20, 1000)) size = 60 while True: x, y = rng.uniform(-size, size, 2) # rng.rand() * self.navigation_target.setPos((x, y, 0)) l = self.get_collision_list() if len(l) < 1: break # print(i) target_pos = self.navigation_target.getPos() target_pos = target_pos[0] + target_pos[1] * 1j while True: if not self.random_reset_around_target: # : x, y = rng.uniform(-size, size, 2) # rng.rand else: while True: pos = (10 + size * math.pi * rng.rand()) * \ np.exp(rng.rand() * math.pi * 1j) pos += target_pos if -size < pos.real < size and -size < pos.imag < size: x, y = pos.real, pos.imag break self.agent.setPos((x, y, 0)) l = self.get_collision_list() if len(l) < 1: break self.agent.setH(rng.uniform(0, 360)) self.relative_target_position = self.__get_relative_target_position() for p in self.pieces: p.reset(avoids=[(self.agent.getPos(), self.initial_area_radius), (self.navigation_target.getPos(), self.target_area_radius)]) # print(i) def renderFrame(self): base.graphicsEngine.renderFrame() def getScreen(self): # base.graphicsEngine.renderFrame() # self.screenshot(namePrefix=self._screenshot) tex = self.win.getScreenshot() r = tex.getRamImage() l = np.asarray(r) x = tex.getXSize() y = tex.getYSize() # print((x,y,len(l))) assert x * y * 4 == len(l) if x * y * 3 == len(l): l = l.reshape((y, x, 3)) elif x * y * 4 == len(l): l = l.reshape((y, x, 4)) elif x * y == len(l): l = l.reshape((y, x)) else: a = 1 / 0 l = np.flipud(l) return l def getDepthMap(self, p): # print(p.dtype) # p=p[:,:,:2] # img =p# cv2.imread(p, 0) img = np.mean(p, axis=2, dtype='uint8') # print(img.shape) # img=p[:,:,3] # print(img.shape) # os.remove(p) img1 = img[:, :self.img_size] img2 = img[:, self.img_size:] disparity = self.stereo.compute(img1, img2) if rng.rand() < 0.1: p = '/dev/shm/z_depthsample%d.jpg' p2 = '/dev/shm/z_depthsample%d_.jpg' for i in range(50): if not os.path.exists(p % i): scipy.misc.imsave(p % i, disparity) scipy.misc.imsave(p2 % i, np.asarray( [img1, img2, disparity])) break return disparity, img1, img2 def getRawImage(self, p): img = p # scipy.misc.imread(p) # os.remove(p) img1 = img[:, :self.img_size] img = np.mean(img1, axis=2) return img def getRawImageStereo(self, img): img = np.mean(img, axis=2) img1 = img[:, :self.img_size] img2 = img[:, self.img_size:] return np.asarray([img1, img2]) def getDepthMapT(self): tex = self.depthmap # tex = self.altBuffer.getTexture() r = tex.getRamImage() s = r.getData() i = np.fromstring(s, dtype='float32') i = i.reshape((self.img_size, self.img_size)) i = np.flipud(i) return i def get1Ddepth(self, dmap): i = int(self.img_size * 0.525) # horizon return dmap[i]
class CollisionChecker(object): """ A fast collision checker that allows maximum 32 collision pairs author: weiwei date: 20201214osaka """ def __init__(self, name="auto"): self.ctrav = CollisionTraverser() self.chan = CollisionHandlerQueue() self.np = NodePath(name) self.bitmask_list = [BitMask32(2**n) for n in range(31)] self._bitmask_ext = BitMask32( 2**31) # 31 is prepared for cd with external non-active objects self.all_cdelements = [ ] # a list of cdlnks or cdobjs for quick accessing the cd elements (cdlnks/cdobjs) def add_cdlnks(self, jlcobj, lnk_idlist): """ The collision node of the given links will be attached to self.np, but their collision bitmask will be cleared When the a robot_s is treated as an obstacle by another robot_s, the IntoCollideMask of its all_cdelements will be set to BitMask32(2**31), so that the other robot_s can compare its active_cdelements with the all_cdelements. :param jlcobj: :param lnk_idlist: :return: author: weiwei date: 20201216toyonaka """ for id in lnk_idlist: if jlcobj.lnks[id]['cdprimit_childid'] == -1: # first time add cdnp = jlcobj.lnks[id]['collision_model'].copy_cdnp_to( self.np, clearmask=True) self.ctrav.addCollider(cdnp, self.chan) self.all_cdelements.append(jlcobj.lnks[id]) jlcobj.lnks[id]['cdprimit_childid'] = len( self.all_cdelements) - 1 else: raise ValueError("The link is already added!") def set_active_cdlnks(self, activelist): """ The specified collision links will be used for collision detection with external obstacles :param activelist: essentially a from list like [jlchain.lnk0, jlchain.lnk1...] the correspondent tolist will be set online in cd functions TODO use all elements in self.all_cdnlks if None :return: author: weiwei date: 20201216toyonaka """ for cdlnk in activelist: if cdlnk['cdprimit_childid'] == -1: raise ValueError( "The link needs to be added to collider using the add_cdlnks function first!" ) cdnp = self.np.getChild(cdlnk['cdprimit_childid']) cdnp.node().setFromCollideMask(self._bitmask_ext) def set_cdpair(self, fromlist, intolist): """ The given collision pair will be used for self collision detection :param fromlist: [[bool, cdprimit_cache], ...] :param intolist: [[bool, cdprimit_cache], ...] :return: author: weiwei date: 20201215 """ if len(self.bitmask_list) == 0: raise ValueError("Too many collision pairs! Maximum: 29") allocated_bitmask = self.bitmask_list.pop() for cdlnk in fromlist: if cdlnk['cdprimit_childid'] == -1: raise ValueError( "The link needs to be added to collider using the addjlcobj function first!" ) cdnp = self.np.getChild(cdlnk['cdprimit_childid']) current_from_cdmask = cdnp.node().getFromCollideMask() new_from_cdmask = current_from_cdmask | allocated_bitmask cdnp.node().setFromCollideMask(new_from_cdmask) for cdlnk in intolist: if cdlnk['cdprimit_childid'] == -1: raise ValueError( "The link needs to be added to collider using the addjlcobj function first!" ) cdnp = self.np.getChild(cdlnk['cdprimit_childid']) current_into_cdmask = cdnp.node().getIntoCollideMask() new_into_cdmask = current_into_cdmask | allocated_bitmask cdnp.node().setIntoCollideMask(new_into_cdmask) def add_cdobj(self, objcm, rel_pos, rel_rotmat, into_list): """ :return: cdobj_info, a dictionary that mimics a joint link; Besides that, there is an additional 'into_list' key to hold into_list to easily toggle off the bitmasks. """ cdobj_info = {} cdobj_info['collision_model'] = objcm # for reversed lookup cdobj_info['gl_pos'] = objcm.get_pos() cdobj_info['gl_rotmat'] = objcm.get_rotmat() cdobj_info['rel_pos'] = rel_pos cdobj_info['rel_rotmat'] = rel_rotmat cdobj_info['into_list'] = into_list cdnp = objcm.copy_cdnp_to(self.np, clearmask=True) cdnp.node().setFromCollideMask(self._bitmask_ext) # set active self.ctrav.addCollider(cdnp, self.chan) self.all_cdelements.append(cdobj_info) cdobj_info['cdprimit_childid'] = len(self.all_cdelements) - 1 self.set_cdpair([cdobj_info], into_list) return cdobj_info def delete_cdobj(self, cdobj_info): """ :param cdobj_info: an lnk-like object generated by self.add_objinhnd :param objcm: :return: """ self.all_cdelements.remove(cdobj_info) cdnp_to_delete = self.np.getChild(cdobj_info['cdprimit_childid']) self.ctrav.removeCollider(cdnp_to_delete) this_cdmask = cdnp_to_delete.node().getFromCollideMask() for cdlnk in cdobj_info['into_list']: cdnp = self.np.getChild(cdlnk['cdprimit_childid']) current_into_cdmask = cdnp.node().getIntoCollideMask() new_into_cdmask = current_into_cdmask & ~this_cdmask cdnp.node().setIntoCollideMask(new_into_cdmask) cdnp_to_delete.detachNode() self.bitmask_list.append(this_cdmask) def is_collided(self, obstacle_list=[], otherrobot_list=[], toggle_contact_points=False): """ :param obstacle_list: staticgeometricmodel :param otherrobot_list: :return: """ for cdelement in self.all_cdelements: pos = cdelement['gl_pos'] rotmat = cdelement['gl_rotmat'] cdnp = self.np.getChild(cdelement['cdprimit_childid']) cdnp.setPosQuat(da.npv3_to_pdv3(pos), da.npmat3_to_pdquat(rotmat)) # print(da.npv3mat3_to_pdmat4(pos, rotmat)) # print("From", cdnp.node().getFromCollideMask()) # print("Into", cdnp.node().getIntoCollideMask()) # print("xxxx colliders xxxx") # for collider in self.ctrav.getColliders(): # print(collider.getMat()) # print("From", collider.node().getFromCollideMask()) # print("Into", collider.node().getIntoCollideMask()) # attach obstacles obstacle_parent_list = [] for obstacle in obstacle_list: obstacle_parent_list.append(obstacle.objpdnp.getParent()) obstacle.objpdnp.reparentTo(self.np) # attach other robots for robot in otherrobot_list: for cdnp in robot.cc.np.getChildren(): current_into_cdmask = cdnp.node().getIntoCollideMask() new_into_cdmask = current_into_cdmask | self._bitmask_ext cdnp.node().setIntoCollideMask(new_into_cdmask) robot.cc.np.reparentTo(self.np) # collision check self.ctrav.traverse(self.np) # clear obstacles for i, obstacle in enumerate(obstacle_list): obstacle.objpdnp.reparentTo(obstacle_parent_list[i]) # clear other robots for robot in otherrobot_list: for cdnp in robot.cc.np.getChildren(): current_into_cdmask = cdnp.node().getIntoCollideMask() new_into_cdmask = current_into_cdmask & ~self._bitmask_ext cdnp.node().setIntoCollideMask(new_into_cdmask) robot.cc.np.detachNode() if self.chan.getNumEntries() > 0: collision_result = True else: collision_result = False if toggle_contact_points: contact_points = [ da.pdv3_to_npv3(cd_entry.getSurfacePoint(base.render)) for cd_entry in self.chan.getEntries() ] return collision_result, contact_points else: return collision_result def show_cdprimit(self): """ Copy the current nodepath to base.render to show collision states TODO: maintain a list to allow unshow :return: author: weiwei date: 20220404 """ # print("call show_cdprimit") snp_cpy = self.np.copyTo(base.render) for cdelement in self.all_cdelements: pos = cdelement['gl_pos'] rotmat = cdelement['gl_rotmat'] cdnp = snp_cpy.getChild(cdelement['cdprimit_childid']) cdnp.setPosQuat(da.npv3_to_pdv3(pos), da.npmat3_to_pdquat(rotmat)) cdnp.show() def disable(self): """ clear pairs and nodepath :return: """ for cdelement in self.all_cdelements: cdelement['cdprimit_childid'] = -1 self.all_cdelements = [] for child in self.np.getChildren(): child.removeNode() self.bitmask_list = list(range(31))
class Game(ShowBase): def __init__(self): # Set up the window, camera, etc. ShowBase.__init__(self) # Set the background color to black self.win.setClearColor(BACKGROUND_COLOR) # 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/world") self.environ.reparentTo(render) # Create the main character playerStartPos = self.environ.find("**/start_point").getPos() self.player = Actor("models/player", {"run": "models/player-run", "walk": "models/player-walk"}) self.player.reparentTo(render) self.player.setScale(.2) self.player.setPos(playerStartPos + (0, 0, 0.5)) # Create a floater object, which floats 2 units above player. We # use this as a target for the camera to look at. self.floater = NodePath(PandaNode("floater")) self.floater.reparentTo(self.player) self.floater.setZ(CAMERA_TARGET_HEIGHT_DELTA) self.first_person = False def key_dn(name): return lambda: self.setKey(name, True) def key_up(name): return lambda: self.setKey(name, False) def quit(): self.destroy() def toggle_first(): self.first_person = not self.first_person # Accept the control keys for movement and rotation key_map = [ # key command action help # --- ------- ------ ---- ("escape", "esc", lambda: quit(), '[ESC]: Quit'), ("arrow_left", 'left', key_dn("left"), "[Left Arrow]: Rotate Left"), ("arrow_left-up", 'left', key_up("left"), None), ("arrow_right", 'right', key_dn("right"), "[Right Arrow]: Rotate Right"), ("arrow_right-up", 'right', key_up("right"), None), ("arrow_up", 'forward', key_dn("forward"), "[Up Arrow]: Run Forward"), ("arrow_up-up", 'forward', key_up("forward"), None), ("arrow_down", 'backward', key_dn("backward"), "[Down Arrow]: Run Backward"), ("arrow_down-up", 'backward', key_up("backward"), None), ("a", 'cam-left', key_dn("cam-left"), "[A]: Rotate Camera Left"), ("a-up", 'cam-left', key_up("cam-left"), None), ("s", 'cam-right', key_dn("cam-right"), "[S]: Rotate Camera Right"), ("s-up", 'cam-right', key_up("cam-right"), None), ('f', 'first-pers', lambda: toggle_first(), '[F]: Toggle first-person'), ] self.keyMap = {} inst = Instructions() for key, command, action, description in key_map: if command: self.setKey(command, False) self.accept(key, action) if description: inst.add(description) taskMgr.add(self.move, "moveTask") # Game state variables self.isMoving = False # Set up the camera self.disableMouse() self.camera.setPos(self.player.getX(), self.player.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 player'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.playerGroundRay = CollisionRay() self.playerGroundRay.setOrigin(0, 0, 9) self.playerGroundRay.setDirection(0, 0, -1) self.playerGroundCol = CollisionNode('playerRay') self.playerGroundCol.addSolid(self.playerGroundRay) self.playerGroundCol.setFromCollideMask(CollideMask.bit(0)) self.playerGroundCol.setIntoCollideMask(CollideMask.allOff()) self.playerGroundColNp = self.player.attachNewNode(self.playerGroundCol) self.playerGroundHandler = CollisionHandlerQueue() self.cTrav.addCollider(self.playerGroundColNp, self.playerGroundHandler) self.camGroundRay = CollisionRay() self.camGroundRay.setOrigin(0, 0, 9) self.camGroundRay.setDirection(0, 0, -1) self.camGroundCol = CollisionNode('camRay') self.camGroundCol.addSolid(self.camGroundRay) self.camGroundCol.setFromCollideMask(CollideMask.bit(0)) self.camGroundCol.setIntoCollideMask(CollideMask.allOff()) self.camGroundColNp = self.camera.attachNewNode(self.camGroundCol) self.camGroundHandler = CollisionHandlerQueue() self.cTrav.addCollider(self.camGroundColNp, self.camGroundHandler) # Uncomment this line to see the collision rays self.playerGroundColNp.show() self.camGroundColNp.show() # Uncomment this line to show a visual representation of the # collisions occuring self.cTrav.showCollisions(render) # Create some lighting ambientLight = AmbientLight("ambientLight") ambientLight.setColor((.3, .3, .3, 1)) directionalLight = DirectionalLight("directionalLight") directionalLight.setDirection((-5, -5, -5)) directionalLight.setColor((1, 1, 1, 1)) directionalLight.setSpecularColor((1, 1, 1, 1)) render.setLight(render.attachNewNode(ambientLight)) render.setLight(render.attachNewNode(directionalLight)) # Records the state of the arrow keys def setKey(self, key, value): self.keyMap[key] = value # Accepts arrow keys to move either the player or the menu cursor, # Also deals with grid checking and collision detection def move(self, task): # Get the time that elapsed since last frame. We multiply this with # the desired speed in order to find out with which distance to move # in order to achieve that desired speed. dt = globalClock.getDt() # If the camera-left key is pressed, move camera left. # If the camera-right key is pressed, move camera right. if self.keyMap["cam-left"]: self.camera.setX(self.camera, -20 * dt) if self.keyMap["cam-right"]: self.camera.setX(self.camera, +20 * dt) # save player's initial position so that we can restore it, # in case he falls off the map or runs into something. startpos = self.player.getPos() # If a move-key is pressed, move player in the specified direction. if self.keyMap["left"]: self.player.setH(self.player.getH() + 300 * dt) if self.keyMap["right"]: self.player.setH(self.player.getH() - 300 * dt) if self.keyMap["forward"]: self.player.setY(self.player, -25 * dt) if self.keyMap["backward"]: self.player.setY(self.player, 25 * dt) # If player is moving, loop the run animation. # If he is standing still, stop the animation. if self.keyMap["forward"] or self.keyMap["backward"] or self.keyMap["left"] or self.keyMap["right"]: if self.isMoving is False: self.player.loop("run") self.isMoving = True else: if self.isMoving: self.player.stop() self.player.pose("walk", 5) self.isMoving = False # If the camera is too far from player, move it closer. # If the camera is too close to player, move it farther. if self.first_person: self.camera.setPos(self.player.getPos()) else: camvec = self.player.getPos() - self.camera.getPos() camvec.setZ(0) camdist = camvec.length() camvec.normalize() if camdist > CAMERA_DISTANCE_MAX: self.camera.setPos(self.camera.getPos() + camvec * (camdist - int(CAMERA_DISTANCE_MAX))) camdist = CAMERA_DISTANCE_MAX if camdist < CAMERA_DISTANCE_MIN: self.camera.setPos(self.camera.getPos() - camvec * (int(CAMERA_DISTANCE_MIN) - camdist)) camdist = CAMERA_DISTANCE_MIN # Normally, we would have to call traverse() to check for collisions. # However, the class ShowBase that we inherit from has a task to do # this for us, if we assign a CollisionTraverser to self.cTrav. self.cTrav.traverse(render) # Adjust player's Z coordinate. If player's ray hit terrain, # update his Z. If it hit anything else, or didn't hit anything, put # him back where he was last frame. entries = list(self.playerGroundHandler.getEntries()) entries.sort(key=lambda x: x.getSurfacePoint(render).getZ()) if len(entries) > 0 and entries[0].getIntoNode().getName() == "terrain": self.player.setZ(entries[0].getSurfacePoint(render).getZ()) else: self.player.setPos(startpos) # Keep the camera at one foot above the terrain, # or two feet above player, whichever is greater. entries = list(self.camGroundHandler.getEntries()) entries.sort(key=lambda x: x.getSurfacePoint(render).getZ()) if len(entries) > 0 and entries[0].getIntoNode().getName() == "terrain": self.camera.setZ(entries[0].getSurfacePoint(render).getZ() + CAMERA_POSITION_HEIGHT_DELTA_MIN) if self.camera.getZ() < self.player.getZ() + CAMERA_POSITION_HEIGHT_DELTA_MAX: self.camera.setZ(self.player.getZ() + CAMERA_POSITION_HEIGHT_DELTA_MAX) # The camera should look in player's direction, # but it should also try to stay horizontal, so look at # a floater which hovers above player's head. self.camera.lookAt(self.floater) return task.cont
class CogdoFlyingCameraManager: def __init__(self, cam, parent, player, level): self._toon = player.toon self._camera = cam self._parent = parent self._player = player self._level = level self._enabled = False def enable(self): if self._enabled: return self._toon.detachCamera() self._prevToonY = 0.0 levelBounds = self._level.getBounds() l = Globals.Camera.LevelBoundsFactor self._bounds = ((levelBounds[0][0] * l[0], levelBounds[0][1] * l[0]), (levelBounds[1][0] * l[1], levelBounds[1][1] * l[1]), (levelBounds[2][0] * l[2], levelBounds[2][1] * l[2])) self._lookAtZ = self._toon.getHeight( ) + Globals.Camera.LookAtToonHeightOffset self._camParent = NodePath('CamParent') self._camParent.reparentTo(self._parent) self._camParent.setPos(self._toon, 0, 0, 0) self._camParent.setHpr(180, Globals.Camera.Angle, 0) self._camera.reparentTo(self._camParent) self._camera.setPos(0, Globals.Camera.Distance, 0) self._camera.lookAt(self._toon, 0, 0, self._lookAtZ) self._cameraLookAtNP = NodePath('CameraLookAt') self._cameraLookAtNP.reparentTo(self._camera.getParent()) self._cameraLookAtNP.setPosHpr(self._camera.getPos(), self._camera.getHpr()) self._levelBounds = self._level.getBounds() self._enabled = True self._frozen = False self._initCollisions() def _initCollisions(self): self._camCollRay = CollisionRay() camCollNode = CollisionNode('CameraToonRay') camCollNode.addSolid(self._camCollRay) camCollNode.setFromCollideMask(OTPGlobals.WallBitmask | OTPGlobals.CameraBitmask | ToontownGlobals.FloorEventBitmask | ToontownGlobals.CeilingBitmask) camCollNode.setIntoCollideMask(0) self._camCollNP = self._camera.attachNewNode(camCollNode) self._camCollNP.show() self._collOffset = Vec3(0, 0, 0.5) self._collHandler = CollisionHandlerQueue() self._collTrav = CollisionTraverser() self._collTrav.addCollider(self._camCollNP, self._collHandler) self._betweenCamAndToon = {} self._transNP = NodePath('trans') self._transNP.reparentTo(render) self._transNP.setTransparency(True) self._transNP.setAlphaScale(Globals.Camera.AlphaBetweenToon) self._transNP.setBin('fixed', 10000) def _destroyCollisions(self): self._collTrav.removeCollider(self._camCollNP) self._camCollNP.removeNode() del self._camCollNP del self._camCollRay del self._collHandler del self._collOffset del self._betweenCamAndToon self._transNP.removeNode() del self._transNP def freeze(self): self._frozen = True def unfreeze(self): self._frozen = False def disable(self): if not self._enabled: return self._destroyCollisions() self._camera.wrtReparentTo(render) self._cameraLookAtNP.removeNode() del self._cameraLookAtNP self._camParent.removeNode() del self._camParent del self._prevToonY del self._lookAtZ del self._bounds del self._frozen self._enabled = False def update(self, dt=0.0): self._updateCam(dt) self._updateCollisions() def _updateCam(self, dt): toonPos = self._toon.getPos() camPos = self._camParent.getPos() x = camPos[0] z = camPos[2] toonWorldX = self._toon.getX(render) maxX = Globals.Camera.MaxSpinX toonWorldX = clamp(toonWorldX, -1.0 * maxX, maxX) spinAngle = Globals.Camera.MaxSpinAngle * toonWorldX * toonWorldX / ( maxX * maxX) newH = 180.0 + spinAngle self._camParent.setH(newH) spinAngle = spinAngle * (pi / 180.0) distBehindToon = Globals.Camera.SpinRadius * cos(spinAngle) distToRightOfToon = Globals.Camera.SpinRadius * sin(spinAngle) d = self._camParent.getX() - clamp(toonPos[0], *self._bounds[0]) if abs(d) > Globals.Camera.LeewayX: if d > Globals.Camera.LeewayX: x = toonPos[0] + Globals.Camera.LeewayX else: x = toonPos[0] - Globals.Camera.LeewayX x = self._toon.getX(render) + distToRightOfToon boundToonZ = min(toonPos[2], self._bounds[2][1]) d = z - boundToonZ if d > Globals.Camera.MinLeewayZ: if self._player.velocity[2] >= 0 and toonPos[ 1] != self._prevToonY or self._player.velocity[2] > 0: z = boundToonZ + d * INVERSE_E**(dt * Globals.Camera.CatchUpRateZ) elif d > Globals.Camera.MaxLeewayZ: z = boundToonZ + Globals.Camera.MaxLeewayZ elif d < -Globals.Camera.MinLeewayZ: z = boundToonZ - Globals.Camera.MinLeewayZ if self._frozen: y = camPos[1] else: y = self._toon.getY(render) - distBehindToon self._camParent.setPos(x, smooth(camPos[1], y), smooth(camPos[2], z)) if toonPos[2] < self._bounds[2][1]: h = self._cameraLookAtNP.getH() if d >= Globals.Camera.MinLeewayZ: self._cameraLookAtNP.lookAt(self._toon, 0, 0, self._lookAtZ) elif d <= -Globals.Camera.MinLeewayZ: self._cameraLookAtNP.lookAt(self._camParent, 0, 0, self._lookAtZ) self._cameraLookAtNP.setHpr(h, self._cameraLookAtNP.getP(), 0) self._camera.setHpr( smooth(self._camera.getHpr(), self._cameraLookAtNP.getHpr())) self._prevToonY = toonPos[1] def _updateCollisions(self): pos = self._toon.getPos(self._camera) + self._collOffset self._camCollRay.setOrigin(pos) direction = -Vec3(pos) direction.normalize() self._camCollRay.setDirection(direction) self._collTrav.traverse(render) nodesInBetween = {} if self._collHandler.getNumEntries() > 0: self._collHandler.sortEntries() for entry in self._collHandler.getEntries(): name = entry.getIntoNode().getName() if name.find('col_') >= 0: np = entry.getIntoNodePath().getParent() if np not in nodesInBetween: nodesInBetween[np] = np.getParent() for np in nodesInBetween.keys(): if np in self._betweenCamAndToon: del self._betweenCamAndToon[np] else: np.setTransparency(True) np.wrtReparentTo(self._transNP) if np.getName().find('lightFixture') >= 0: if not np.find('**/*floor_mesh').isEmpty(): np.find('**/*floor_mesh').hide() elif np.getName().find('platform') >= 0: if not np.find('**/*Floor').isEmpty(): np.find('**/*Floor').hide() for np, parent in self._betweenCamAndToon.items(): np.wrtReparentTo(parent) np.setTransparency(False) if np.getName().find('lightFixture') >= 0: if not np.find('**/*floor_mesh').isEmpty(): np.find('**/*floor_mesh').show() elif np.getName().find('platform') >= 0: if not np.find('**/*Floor').isEmpty(): np.find('**/*Floor').show() self._betweenCamAndToon = nodesInBetween
class RoamingDroneDemo(ShowBase): def __init__(self): ShowBase.__init__(self) # initalize the window base.disableMouse() self.win.setClearColor((0, 0, 0, 1)) props = WindowProperties() props.setCursorHidden(True) props.setSize(1700,1000) base.win.requestProperties(props) # store keys self.keyMap = { "left": 0, "right": 0, "forward": 0, "drift-left": 0, "drift-right": 0, "up": 0, "down": 0, "restart": 0, "firstPerson": 0} #instructions self.instruction2 = addInstructions(0.12, "[Left Arrow]: Rotate Left") self.instruction3 = addInstructions(0.18, "[Right Arrow]: Rotate Right") self.instruction4 = addInstructions(0.24, "[Up Arrow]: Fly Forward") self.instruction5 = addInstructions(0.30, "[A, D]: Move Camera") self.instruction6 = addInstructions(0.36, "[W, S]: Fly Lift/ Descent") self.instruction7 = addInstructions(0.42, "[F]: Toggle First Person/ Third Person") self.instruction8 = addInstructions(0.48, "[R]: Restart") # Set up the playground # models/toon/phase_15/hood/toontown_central.bam # models/world # CS_Map/myCSMAP.egg self.environ = loader.loadModel("models/world") self.environ.reparentTo(render) # Create drone and initalize drone position self.Drone = Actor("models/mydrone.egg") self.Drone.reparentTo(render) # resize and reposition the drone self.Drone.setScale(.1) self.Drone.setPos(5,5,5) # initial position is saved for restarting the game self.DroneStartPos = self.Drone.getPos() # User Controls self.accept('escape', __import__('sys').exit, [0]) self.accept("arrow_left", self.setKey, ["left", True]) self.accept("arrow_right", self.setKey, ["right", True]) self.accept("arrow_up", self.setKey, ["forward", True]) self.accept("a", self.setKey, ["drift-left", True]) self.accept("d", self.setKey, ["drift-right", True]) self.accept("arrow_left-up", self.setKey, ["left", False]) self.accept("arrow_right-up", self.setKey, ["right", False]) self.accept("arrow_up-up", self.setKey, ["forward", False]) self.accept("a-up", self.setKey, ["drift-left", False]) self.accept("d-up", self.setKey, ["drift-right", False]) self.accept("w", self.setKey, ["up", True]) self.accept("w-up", self.setKey, ["up", False]) self.accept("s", self.setKey, ["down", True]) self.accept("s-up", self.setKey, ["down", False]) self.accept("r", self.setKey, ["restart", True]) self.accept("r-up", self.setKey, ["restart", False]) self.accept("f", self.setKey, ["firstPerson", True]) self.accept("f-up", self.setKey, ["firstPerson", False]) taskMgr.add(self.move, "moveTask") # Disable Mouse self.disableMouse() # Camera settings self.cameraDistance = 5 self.cameraPitch = -10 # create the collision box for the drone # this collision box will be used for collision detection self.droneBox = CollisionBox((0,0,2.5), 3, 3, 0.7) self.cnodePath = self.Drone.attachNewNode(CollisionNode('cnode')) self.cnodePath.node().addSolid(self.droneBox) # collision detection set up self.cTrav = CollisionTraverser() self.queue = CollisionHandlerQueue() self.cTrav.addCollider(self.cnodePath, self.queue) self.cTrav.traverse(render) ################################## #self.cTrav.showCollisions(render) #self.DroneGroundColNp.show() #self.camGroundColNp.show() ################################## # Lighting ambientLight = AmbientLight("ambientLight") ambientLight.setColor((1, 1, 1, 1)) directionalLight = DirectionalLight("directionalLight") directionalLight.setDirection((-5, -5, -5)) directionalLight.setColor((1, 1, 1, 1)) directionalLight.setSpecularColor((1, 1, 1, 1)) render.setLight(render.attachNewNode(ambientLight)) render.setLight(render.attachNewNode(directionalLight)) # Crashed Text self.crashed = OnscreenText() self.firstPerson = False # HPR setting self.angle = 0 self.angleChange = 0.8 self.maxAngle = 20 # Records the state of the arrow keys def setKey(self, key, value): self.keyMap[key] = value # Deal with user interface and collision detection def move(self, task): self.crashed.destroy() self.crashed = OnscreenText(text="Crashed!!!" if len(self.queue.getEntries()) != 0 else "", pos = (-0.5, 0.02), scale = 0.07, mayChange = True, fg = (255,255,255,1)) # Get the time that elapsed since last frame. dt = globalClock.getDt() # Drone is movable only when it's not crashed if len(self.queue.getEntries()) == 0: # tilting while drift left and right if self.keyMap["drift-left"]: self.Drone.setY(self.Drone, 40 * dt) # tilt left when drift left if self. angle > -self.maxAngle: self.angle -= self.angleChange self.Drone.setP(self.angle) elif self.keyMap["drift-right"]: self.Drone.setY(self.Drone, -40 * dt) # tilt right when drift right if self. angle < self.maxAngle: self.angle += self.angleChange self.Drone.setP(self.angle) # gradually stablize itself while drift-keys are not pressed else: if self.angle >=self.angleChange: self.angle -= self.angleChange elif self.angle <=-self.angleChange: self.angle +=self.angleChange self.Drone.setP(self.angle) # turn left if self.keyMap["left"]: self.Drone.setH(self.Drone.getH() + 150 * dt) # turn right if self.keyMap["right"]: self.Drone.setH(self.Drone.getH() - 150 * dt) # go forward if self.keyMap["forward"]: self.Drone.setX(self.Drone, 200 * dt) # lift up if self.keyMap["up"]: self.Drone.setZ(self.Drone, 40 * dt) # go down if self.keyMap["down"]: self.Drone.setZ(self.Drone, -80 * dt) # restart game / reset position if self.keyMap["restart"]: self.Drone.setPos(self.DroneStartPos + (0, 0, 5)) self.collisionCount = False self.crashed.destroy() if self.keyMap["firstPerson"]: self.firstPerson = not self.firstPerson ###################### #self.cnodePath.show() ###################### # set the position and HPR of the camera according to the position of the drone if self.firstPerson: base.camera.setH(self.Drone.getH()-90) base.camera.setP(self.Drone.getR()) base.camera.setR(self.Drone.getP()) base.camera.setPos(self.Drone.getPos()) else: base.camera.setHpr(self.Drone.getHpr()+(180,0,0)) h,p,r = self.Drone.getHpr() base.camera.setPos(self.Drone.getPos() + (math.cos(math.pi * h / 180) * -self.cameraDistance, \ math.sin(math.pi * h / 180) * -self.cameraDistance, 0.5)) viewTarget = Point3(self.Drone.getPos() + (0,0,0)) base.camera.lookAt(viewTarget) #print(self.camera.getPos()) return task.cont
class MyApp(ShowBase): def insertTile(self, node, submap_center, xel: Xel.Xel, tile_z, terrain): dx, dy = submap_center (q, r) = xel.exa.x, xel.exa.e v_center = VBase2(s3 * q, v3s * 2 * (q / 2 + r)) tile_color = "rock" #TODO #if abs(q) == Map.radius or abs(r) == Map.radius or abs(xel.exa.a) == Map.radius: # tile_color = "yellow" #else: #temporary if terrain == "water": tile_z = 0 tile_color = terrain return self.model.loadExaTile(node, v_center[0] + dx, v_center[1] + dy, tile_z, tile_color) def drawTriangle(self, node, submap_center, submap_xel, triangle_index, nodes_ZTH, open_simplex): global god center_map = submap_xel for d in range(1, Map.radius + 1): # distance from center moving along triangle side t center_map = center_map.link[dirs[triangle_index]] tmp_map = center_map for n in range( 0, d ): # each cell from triangle_index triangle edge (included) to next triangle edge (excluded) if d == Map.radius and n == 0: cell_z = nodes_ZTH[0][triangle_index] cell_t = nodes_ZTH[1][triangle_index] cell_h = nodes_ZTH[2][triangle_index] else: interna = ((nodes_ZTH[0][6], nodes_ZTH[0][triangle_index], nodes_ZTH[0][(triangle_index + 1) % 6]), (nodes_ZTH[1][6], nodes_ZTH[1][triangle_index], nodes_ZTH[1][(triangle_index + 1) % 6]), (nodes_ZTH[2][6], nodes_ZTH[2][triangle_index], nodes_ZTH[2][(triangle_index + 1) % 6])) res = ExaRandom.interpolate( self, interna, Map.radius, (tmp_map.exa.e, tmp_map.exa.x, tmp_map.exa.a)) (cell_z, cell_t, cell_h) = res cell_z += open_simplex.noise2d(tmp_map.exa.x, tmp_map.exa.e) / 5 esterna = god.creation( submap_center, (tmp_map.exa.e, tmp_map.exa.x, tmp_map.exa.a), cell_t, cell_h) #animale, vegetale, terreno #load models dx, dy = submap_center (q, r) = tmp_map.exa.x, tmp_map.exa.e v_center = VBase2(s3 * q, v3s * 2 * (q / 2 + r)) if esterna[0] != None: self.model.loadAnimal(node, v_center[0] + dx, v_center[1] + dy, cell_z, esterna[0].nome) if esterna[1] != None: self.model.loadPlant(node, v_center[0] - side + dx, v_center[1] + dy, cell_z, esterna[1].nome) if esterna[2] != None: terrain_name = esterna[2].nome else: terrain_name = "rock" self.insertTile(node, submap_center, tmp_map, cell_z, terrain_name) tmp_map = tmp_map.link[dirs[(triangle_index + 2) % 6]] def drawSubmap(self, submap): l_map = Map.l_map # TODO: chose if letting this way or pass l_map as parameter open_s = OpenSimplex(submap.noise_seed) #Center global god esterna = god.creation(submap.centerXY, (0, 0, 0), submap.array_T[6], submap.array_H[6]) #animale, vegetale, terreno if esterna[2] != None: terrain_name = esterna[2].nome else: terrain_name = "rock" self.insertTile(submap.node, submap.centerXY, l_map, submap.array_Z[6], terrain_name) ### Graphic map construction, triangle-by-triangle way center_map = l_map for t in range(6): # scan each triangle interna = (submap.array_Z, submap.array_T, submap.array_H) self.drawTriangle(submap.node, submap.centerXY, center_map, t, interna, open_s) ### return submap def drawMap(self, submap): global current_submap global stored_submaps_list global rendered_submaps new_seven_centers = [] for d, v in coords.items(): temp_c = ((v[0] + submap.centerXY[0]), (v[1] + submap.centerXY[1])) #print("-----------------------",v, submap.centerXY, temp_c) #BUGGONE new_seven_centers.append(temp_c) new_seven_centers.append(submap.centerXY) if len(rendered_submaps) == 0: # Starting case submap.node = SubmapNode("0") submap.node.reparentTo(self.render) center = self.drawSubmap(submap) rendered_submaps.append(center) tmp_rendered_submaps = [None, None, None, None, None, None, center] else: tmp_rendered_submaps = [None, None, None, None, None, None, None] useful_values = [] for i in range(7): draw = True #check if a sub_map in new_seven_centers has to be drawn. #print all rendenew_seven_centersred maps """print("Rendered:") for s in rendered_submaps: print(s, end='') print("")""" c = new_seven_centers[i] for s in rendered_submaps: #diff = (abs(c[0] - s.centerXY[0]), abs(c[1] - s.centerXY[1])) print("_--------------", c, s) print( "DIFF= ", math.isclose(c[0], s.centerXY[0], abs_tol=0.1) and math.isclose(c[1], s.centerXY[1], abs_tol=0.1)) if math.isclose(c[0], s.centerXY[0], abs_tol=0.1) and math.isclose( c[1], s.centerXY[1], abs_tol=0.1): draw = False #if a map in new_seven_centers is already in rendered_submaps set draw to false tmp_rendered_submaps[i] = s # prova useful_values.append(s) break if draw == True: #if draw is still True, then draw the map in new_seven_centers if c in stored_submaps_list.keys(): s_map = stored_submaps_list[c] else: s_map = ExaRandom().create_submap(c) stored_submaps_list[c] = s_map s_map.node = SubmapNode("1") s_map.node.reparentTo(self.render) tmp_rendered_submaps[i] = self.drawSubmap(s_map) #rendered_submaps.append(self.drawSubmap(self.render, s_map)) print(c, "drawn\n") else: print(c, "NOT drawn\n") # identify no-longer loaded submaps and clear their nodes for s in rendered_submaps: if s not in tmp_rendered_submaps: self.clear_node(s.node) s.node.removeNode() # needs to be tested (TODO) s.node = None # if previous line doesn't remove it automatically rendered_submaps = tmp_rendered_submaps current_submap = submap print("Rendered:") for s in rendered_submaps: print(s, end='') print("") def clear_node(self, nodepath): if not nodepath.isEmpty(): for model in nodepath.getChildren(): model.removeNode() def clear_all_nodes(self): self.clear_node(rendered_submaps[random.randint( 0, 5)].node) # temporary madness #for n in all_nodes: # self.clear_node(n) # n.remove_node() def print_all_nodes(self): string = "" for n in all_nodes: string += str(n) + "; " print(string) def __init__(self): ShowBase.__init__(self) # Load Lights self.light = LoadLight.Light self.light.setupLight(self) # Load Environment self.model = LoadModel.Model() self.model.initialize(self) Map.init() map_center = (0, 0) subprova = Submap.Submap(map_center) global stored_submaps_list global current_submap stored_submaps_list[map_center] = subprova current_submap = subprova print("stored in init:", stored_submaps_list) self.drawMap(subprova) """ self.model.loadAnimal(5,6,0, "bear") self.model.loadAnimal(12,-6,0, "cow") self.model.loadAnimal(7,-9,0, "panther") self.model.loadAnimal(-17,5,0, "rabbit") self.model.loadAnimal(-7,5,0, "wolf") self.model.loadPlant(8,-2,0, "fir") self.model.loadPlant(-3,-2,0, "grass") self.model.loadPlant(-4,2,0, "oak") self.model.loadPlant(-1,17,0, "berry_bush") """ # Text on screen self.text_char_pos = None self.text_submap = None self.text_lock = None # Create the main character self.char = self.model.loadCharacter(0, 0, 0) # 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 char'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.charGroundRay = CollisionRay() self.charGroundRay.setOrigin(0, 0, 9) self.charGroundRay.setDirection(0, 0, -1) self.charGroundCol = CollisionNode('charRay') self.charGroundCol.addSolid(self.charGroundRay) self.charGroundCol.setFromCollideMask(CollideMask.bit(0)) self.charGroundCol.setIntoCollideMask(CollideMask.allOff()) self.charGroundColNp = self.char.attachNewNode(self.charGroundCol) self.charGroundHandler = CollisionHandlerQueue() self.cTrav.addCollider(self.charGroundColNp, self.charGroundHandler) # Uncomment this line to see the collision rays #self.charGroundColNp.show() # Uncomment this line to show a visual representation of the # collisions occuring #self.cTrav.showCollisions(render) # This is used to store which keys are currently pressed. self.keyMap = { "restart": 0, "left": 0, "right": 0, "forward": 0, "backward": 0, "cam-q": 0, "cam-w": 0, "cam-e": 0, "cam-d": 0, "cam-s": 0, "cam-a": 0 } # Post the instructions self.title = addTitle("Isometric HexaMap test") self.inst1 = addInstructions(0.06, "[ESC]: Quit") self.inst2 = addInstructions(0.12, "[Arrow Keys]: Move char") self.inst3 = addInstructions( 0.18, "[Q,W,E,D,S,A]: Change camera's angle of view") #self.inst8 = addInstructions(0.48, "[R]: Restart") # Accept the control keys for movement and rotation self.accept("escape", sys.exit) self.accept("n", self.print_all_nodes) self.accept("k", self.clear_all_nodes) self.accept("arrow_left", self.setKey, ["left", True]) self.accept("arrow_right", self.setKey, ["right", True]) self.accept("arrow_up", self.setKey, ["forward", True]) self.accept("arrow_down", self.setKey, ["backward", True]) self.accept("q", self.setKey, ["cam-q", True]) self.accept("w", self.setKey, ["cam-w", True]) self.accept("e", self.setKey, ["cam-e", True]) self.accept("d", self.setKey, ["cam-d", True]) self.accept("s", self.setKey, ["cam-s", True]) self.accept("a", self.setKey, ["cam-a", True]) self.accept("arrow_left-up", self.setKey, ["left", False]) self.accept("arrow_right-up", self.setKey, ["right", False]) self.accept("arrow_up-up", self.setKey, ["forward", False]) self.accept("arrow_down-up", self.setKey, ["backward", False]) self.accept("q-up", self.setKey, ["cam-q", False]) self.accept("w-up", self.setKey, ["cam-w", False]) self.accept("e-up", self.setKey, ["cam-e", False]) self.accept("d-up", self.setKey, ["cam-d", False]) self.accept("s-up", self.setKey, ["cam-s", False]) self.accept("a-up", self.setKey, ["cam-a", False]) # self.accept("restart", self.char.setPos(0,0,0)) self.taskMgr.add(self.move, "moveTask") # Game state variables self.isMoving = False # Set up the camera with isometric perspective self.disableMouse() lens = OrthographicLens() lens.setFilmSize(20, 11.25) lens.setNear(-20) self.cam.node().setLens(lens) self.camera.setPos(self.char.getPos() + (math.sin(cam_angle[cam_view]) * cam_dist, -math.cos(cam_angle[cam_view]) * cam_dist, cam_dz)) self.camera.lookAt(self.char) # Records the state of the arrow keys def setKey(self, key, value): self.keyMap[key] = value def move(self, task): # Get the time that elapsed since last frame. We multiply this with # the desired speed in order to find out with which distance to move # in order to achieve that desired speed. dt = globalClock.getDt() # If the camera-left key is pressed, move camera left. # If the camera-right key is pressed, move camera right. global cam_view temp_cam = cam_view if self.keyMap["cam-q"]: cam_view = 'q' if self.keyMap["cam-w"]: cam_view = 'w' if self.keyMap["cam-e"]: cam_view = 'e' if self.keyMap["cam-d"]: cam_view = 'd' if self.keyMap["cam-s"]: cam_view = 's' if self.keyMap["cam-a"]: cam_view = 'a' global cam_rotating final_angle = int(round((cam_angle[cam_view] * RAD_DEG))) % 360 current_angle = int(round(self.camera.getH())) % 360 # Camera rotation task is triggered when a different angle of view is selected. # The direction of rotation is chosen by comparing the two angles # and changing the sign of the camera rotation angle: cam_delta_angle if final_angle != current_angle: if not self.taskMgr.hasTaskNamed("SpinCameraTask"): global cam_delta_angle diff = final_angle - current_angle cam_delta_angle = math.copysign(cam_delta_angle, diff) if diff > 180: cam_delta_angle = math.copysign(cam_delta_angle, -1) elif diff < -180: cam_delta_angle = math.copysign(cam_delta_angle, 1) self.taskMgr.doMethodLater(cam_task_time, self.spinCameraTask, "SpinCameraTask") cam_rotating = True if not cam_rotating: # save char's initial position so that we can restore it, # in case he falls off the map or runs into something. startpos = self.char.getPos() # If a move-key is pressed, turn and move char in the relative direction. # The pressure of multiple keys at the same time is handled by the v_rot vector # Movements in 2D plane depend on camera's position & orientation v_rot = VBase3(0, 0, 0) if self.keyMap["forward"]: v_rot += (0, 1, 0) if self.keyMap["backward"]: v_rot += (0, -1, 0) if self.keyMap["left"]: v_rot += (-v3, 0, 0) if self.keyMap["right"]: v_rot += (v3, 0, 0) if v_rot.normalize(): self.char.lookAt(self.char.getPos() + v_rot) self.char.setH(self.char.getH() + self.camera.getH()) self.char.setY(self.char, step * dt) # Normally, we would have to call traverse() to check for collisions. # However, the class ShowBase that we inherit from has a task to do # this for us, if we assign a CollisionTraverser to self.cTrav. self.cTrav.traverse(render) # Adjust char's Z coordinate. If char's ray hit terrain, # update his Z. If it hit anything else, or didn't hit anything, put # him back where he was last frame. entries = list(self.charGroundHandler.getEntries()) entries.sort(key=lambda x: x.getSurfacePoint(render).getZ()) if (len(entries) > 0 and entries[0].getIntoNode().getName() == "ExaTile" and entries[0].getSurfacePoint(render).getZ() - self.char.getZ() <= CHAR_MAX_ZGAP): self.char.setZ(entries[0].getSurfacePoint(render).getZ()) else: self.char.setPos(startpos) # Put on screen the lock's value #if self.text_lock != None: # self.text_lock.destroy() #self.text_lock = addInfo(0.15, "Lock: "+str(Map.new_dir_lock)) #QUIIIIIII+ global pix_pos_tmp pix_pos = self.char.getPos() exa_pos = None #if (math.isclose(abs(pix_pos[0])%(apo+0.05), apo, rel_tol=0.05) or math.isclose(abs(pix_pos[1])%(apo+0.05), apo,rel_tol=0.05)): exa_pos = -1 / 3 * pix_pos[0] + math.sqrt( 3) / 3 * pix_pos[1], 2 / 3 * pix_pos[0] pix_pos = VBase3(round(exa_pos[0]), round(exa_pos[1]), 0) pix_pos += VBase3(0, 0, round(-pix_pos[0] - pix_pos[1])) pix_pos_diff = VBase3(abs(pix_pos[0] - exa_pos[0]), abs(pix_pos[1] - exa_pos[1]), abs(pix_pos[2] - (-exa_pos[0] - exa_pos[1]))) if pix_pos_diff[0] > pix_pos_diff[1] and pix_pos_diff[ 0] > pix_pos_diff[2]: pix_pos[0] = -pix_pos[1] - pix_pos[2] elif pix_pos_diff[1] > pix_pos_diff[2]: pix_pos[1] = -pix_pos[0] - pix_pos[2] else: pix_pos[2] = -pix_pos[0] - pix_pos[1] if pix_pos_tmp != pix_pos: direc = pix_pos - pix_pos_tmp directions = { VBase3(1, -1, 0): 'q', VBase3(1, 0, -1): 'w', VBase3(0, 1, -1): 'e', VBase3(-1, 1, 0): 'd', VBase3(-1, 0, 1): 's', VBase3(0, -1, 1): 'a' } Map.menu(directions.get(direc)) print(Map.position) #print(self.char.getPos()) if self.text_char_pos != None: self.text_char_pos.destroy() self.text_char_pos = addInfo( 0, "Char position: (" + str(round(self.char.getX(), 2)) + " , " + str(round(self.char.getY(), 2)) + ")") #Map.new_dir_lock=False # New submap check if Map.new_dir != None: global current_submap global new_submap #Map.new_dir_lock=True d = Map.new_dir Map.new_dir = None new_center = (current_submap.centerXY[0] + coords[d][0], current_submap.centerXY[1] + coords[d][1]) for c, s in stored_submaps_list.items(): if math.isclose(c[0], new_center[0], abs_tol=0.1) and math.isclose( c[1], new_center[1], abs_tol=0.1): new_submap = s break self.drawMap( new_submap ) # TODO: maybe we can give direction as parameter global rendered_submaps print("Rendered.length = " + str(len(rendered_submaps))) pix_pos_tmp = pix_pos #Map.position= l_map.findXel() #print(Exa.Exa(pix_pos[0], pix_pos[1], -pix_pos[2])) # Camera position handling: it depends on char's position self.camera.setX(self.char.getX() + math.sin(current_angle * DEG_RAD) * cam_dist) self.camera.setY(self.char.getY() - math.cos(current_angle * DEG_RAD) * cam_dist) self.camera.setZ(self.char.getZ() + cam_dz) self.camera.lookAt(self.char) else: # cam is rotating if final_angle == current_angle: print("CAM IN PLACE!") self.taskMgr.remove("SpinCameraTask") cam_rotating = False #print("curr: ", self.camera.getH()) #print("dest: ", cam_angle[cam_view]*RAD_DEG) return task.cont # Task to animate the camera when user changes the angle of view. # Make the camera rotate counterclockwise around the character in x,y plane def spinCameraTask(self, task): angle_deg = int(round(self.camera.getH())) + cam_delta_angle angle_rad = angle_deg * DEG_RAD delta_v = VBase3(self.char.getX() + cam_dist * math.sin(angle_rad), self.char.getY() - cam_dist * math.cos(angle_rad), self.camera.getZ()) self.camera.setPos(delta_v) self.camera.lookAt(self.char) return task.done def restartGame(self): pass
class Picker(Viewer): """ View and click objects in a scene.""" def __init__(self): # Parent init. super(Picker, self).__init__() self.disableMouse() # Picker stuff. self.contact_margin = Vec3(0.01, 0.01, 0.01) self.parser = None self.marked = None self.attached_pairs = set() self.contacts = None self.contact_points = None self.contact_bottoms = None self.compound_components = [] self.compound_objects = [] self.joints = JointManager() self.wire_attrib = RenderModeAttrib.make( RenderModeAttrib.MWireframe, 4.) self.attachment_colors = (Vec4(0.1, 0.1, 1., 1.), Vec4(0.1, 0.8, 0.1, 1.), Vec4(1., 0.1, 1., 1.), Vec4(1., 0.5, 0.1, 1.), Vec4(1., 1., 1., 1.)) self.max_attach = 999 self.permanent_events += ["mouse1"] # Make cursor dot. self.cursor = self._build_cursor("cross") s = 0.08 self.cursor.setScale(s, s, s) self.cursor.setColor(1, 1, 1, 1) self.cursor.reparentTo(self.aspect2d) self.taskMgr.add(self.draw_cursor2d, "draw_cursor2d") self.permanent_tasks.append("draw_cursor2d") def init_ssos(self, *args, **kwargs): super(Picker, self).init_ssos(*args, **kwargs) def init_physics(self, *args, **kwargs): super(Picker, self).init_physics(*args, **kwargs) self.joints.bbase = self.bbase def init_picker(self): # Collision traverser self.traverser = CollisionTraverser("traverser") # Collision handler self.handler = CollisionHandlerQueue() # Initialize and set up picker ray node and NodePath self.picker = CollisionNode("mouse_ray") self.pickerNP = self.camera.attachNewNode(self.picker) self.picker.setFromCollideMask(GeomNode.getDefaultCollideMask()) self.picker_ray = CollisionRay() self.picker.addSolid(self.picker_ray) self.traverser.addCollider(self.pickerNP, self.handler) mark_color = (1, 1, 1, 0.3) self.base_mark = self.create_mark(color=mark_color) connector_color = (1, 1, 1, 1) self.base_connector = self.create_connector(color=connector_color) def _build_cursor(self, shape="sphere"): if shape == "sphere": cursor = self._load("sphere.bam") elif shape == "cross": cursor = LineNodePath() lines = [[Point3(-0.5, 0, 0), Point3(0.5, 0, 0)], [Point3(0, 0, -0.5), Point3(0, 0, 0.5)]] cursor.drawLines(lines) cursor.setThickness(1) cursor.create() # cursor = NodePath("cross") # S = {"cylinderX.bam": ((0., 0., 0.), (1., 0.1, 0.1)), # "cylinderY.bam": ((0., 0., 0.), (0.1, 1., 0.1)), # "cylinderZ.bam": ((0., 0., 0.), (0.1, 0.1, 1.))} # for k, v in S.iteritems(): # m = self._load(k) # m.setName(k) # m.setPos(*v[0]) # m.setScale(*v[1]) # m.reparentTo(cursor) #BP() return cursor def create_mark(self, color): """ Makes a graphical mark object.""" # Make a graphical box. props = dict(name="mark", color=color, model="box-round.egg") obj = GSO(props=props) return obj def create_connector(self, color): """ Makes a graphical connector object.""" # Make a graphical box. props = dict(name="connector", color=color, model="connector.egg") obj = GSO(props=props) return obj def start_picker(self, pickables): # Set pickable objs. for i, obj in enumerate(pickables): obj.setTag("pickable", str(i)) # Add mouse events. self.accept("mouse1", self.clicked, extraArgs=[1]) # Start contact detector. detector = ContactDetector(self.bbase.world, self.scene, margin=self.contact_margin) self.contacts = detector.contacts self.contact_bodies = detector.bodies self.contact_points = detector.points parser = Parser(self.contacts, self.contact_bodies) self.contact_bottoms = parser.bottom_bodies self.connectors = {} def stop_picker(self): self.removeTask("mouse1") def goto_sso(self, *args, **kwargs): self.clear_attachments() self.stop_picker() super(Picker, self).goto_sso(*args, **kwargs) self.remove_physics() # Start picker. pickables = self.sso.descendants(type_=PSO) self.start_picker(pickables) self.attach_physics() def get_picked_obj(self): mpos = self.mouseWatcherNode.getMouse() self.picker_ray.setFromLens(self.cam.node(), mpos.getX(), mpos.getY()) self.traverser.traverse(self.render) if self.handler.getNumEntries() > 0: # This is so we get the closest object self.handler.sortEntries() entries = self.handler.getEntries() for entry in entries: picked_obj = entry.getIntoNodePath().findNetTag("pickable") if not picked_obj.isEmpty(): break if picked_obj.isEmpty(): picked_obj = None else: picked_obj = None return picked_obj def clicked(self, button): """ Mouse click handler.""" if self.mouseWatcherNode.hasMouse(): # Get picked object picked_obj = self.get_picked_obj() if picked_obj is not None: if self.marked is None: # New mark activated. self.marked = picked_obj self.show_marked(picked_obj, True) event = "mark" elif picked_obj == self.marked: # Existing mark deactivated. self.show_marked(picked_obj, False) self.marked = None event = "unmark" else: # New attachment or detachment. pair = tuple(sorted((self.marked, picked_obj))) ij = tuple(sorted((self.contact_bodies.index(pair[0]), self.contact_bodies.index(pair[1])))) if ij in self.contacts: f_add = (ij, pair) not in self.attached_pairs if (not f_add or len(self.attached_pairs) < self.max_attach): self.store_attachment(ij, pair, f_add) self.show_marked(self.marked, False) self.marked = None event = "attach" if f_add else "detach" else: print("Max attachments already reached.") event = "max-attach" else: event = "non-contact" else: event = "non-pick" return picked_obj, event def store_attachment(self, ij, pair, f_add): """ Stores the attached objects, and draws them.""" if f_add: self.attached_pairs.add((ij, pair)) self.show_attachment(ij, True) self.attach_pair(pair, True) else: try: self.attached_pairs.remove((ij, pair)) except KeyError: pass else: self.attach_pair(pair, False) self.show_attachment(ij, False) def clear_attachments(self): """ Clear all attachments.""" if self.marked: self.show_marked(self.marked, False) self.marked = None self.mark = None for ij, pair in self.attached_pairs: self.attach_pair(pair, False) self.show_attachment(ij, False) self.attached_pairs = set() # self.reset_compounds() self.contacts = None self.contact_bodies = None self.contact_points = None self.contact_bottoms = None def _make_mark(self, node, extent, name): """ Makes a mark GSO.""" mark = self.base_mark.copy() mat = node.getMat(self.scene) mark.apply_prop(dict(name=name), other=self.scene) mark.setMat(self.scene, mat) mark.setScale(self.scene, mark.getScale(self.scene) + extent) mark.wrtReparentTo(node) return mark def show_marked(self, node, f_on): """ Turns on/off marked graphic.""" if f_on: extent = Vec3(0.15, 0.15, 0.15) name = "mark" self.mark = self._make_mark(node, extent, name) self.mark.init_tree(tags=("model",)) # Exclude object from casting shadows self.mark.hide(self.shadow_mask) self.mark.setTransparency(TransparencyAttrib.MAlpha) self.mark.setDepthWrite(False) self.mark.setBin("fixed", 0, priority=5) else: self.mark.removeNode() def _make_connector(self, parent, points, extent, name): """ Makes connector object.""" connector = self.base_connector.copy() scale = Vec3(*(np.ptp(points, axis=0))) scale_extended = scale + extent pos = Point3(*(np.min(points, axis=0) + scale / 2.)) connector.apply_prop(dict(name=name, scale=scale_extended, pos=pos), other=self.scene) connector.wrtReparentTo(parent) return connector def show_attachment(self, ij, f_on): """ Turns on/off attachment graphic.""" if f_on: parent = self.contact_bottoms[ij] points = self.contact_points[ij] extent = Vec3(0.15, 0.15, 0.15) name = "connector_%d-%d" % ij self.connectors[ij] = self._make_connector(parent, points, extent, name) self.connectors[ij].init_tree(tags=("model",)) else: self.connectors.pop(ij).removeNode() # def attach_pair(self, pair, f_on): # """ Adds/removes physical attachment between a pair of nodes.""" # key = tuple(sorted(p.node() for p in pair)) # # key = frozenset(pair) # if f_on: # # Create the joint and add it. # self.joints[key] = self.joints.make_fixed(*pair) # else: # # Remove it. # del self.joints[key] def attach_physics(self): # Attach `self.scene` to the physics world. try: exclude = zip(*self.compound_components)[0] except IndexError: exclude = [] bnodes = [bnode for bnode in self.scene.descendants(type_=PSO) if bnode not in exclude] for bnode in bnodes: bnode.init_resources(tags=("shape",)) bnode.setCollideMask(BitMask32.allOn()) bnode.node().setDeactivationEnabled(False) self.bbase.attach(bnodes) def reset_compounds(self): for n, p in self.compound_components: n.wrtReparentTo(p) self.compound_components = [] for cnode in self.compound_objects: cnode.destroy_resources() cnode.removeNode() self.compound_objects = [] def make_attachment_graph(self): if not self.contact_bodies: return None n = len(self.contact_bodies) mtx = np.zeros((n, n), dtype="i") for (i, j), _ in self.attached_pairs: # i = self.contact_bodies.index(pair[0]) # j = self.contact_bodies.index(pair[1]) mtx[i, j] = 1 # mtx[j, i] = 1 graph = nx.from_numpy_matrix(mtx) return graph def attach_pair(self, pair, f_on): """ Adds/removes physical attachment between a pair of nodes.""" # Get the connected subgroups. graph = self.make_attachment_graph() sgs = [sg for sg in nx.connected_components(graph) if len(sg) > 1] self.reset_compounds() # Iterate over subgroups, creating compound shapes. for sg in sgs: nodes = [self.contact_bodies[i] for i in sg] parents = [c.getParent() for c in nodes] self.compound_components.extend(zip(nodes, parents)) cname = "+".join([str(i) for i in sorted(sg)]) cnode = CPSO(cname) cnode.reparentTo(self.scene) cnode.add(nodes) cnode.init_tree(tags=("shape",)) cnode.destroy_component_shapes() self.compound_objects.append(cnode)
class RaceDrone(ShowBase): def __init__(self): ShowBase.__init__(self) # initalize the window base.disableMouse() self.win.setClearColor((0, 0, 0, 1)) props = WindowProperties() props.setCursorHidden(True) props.setSize(1700,1000) base.win.requestProperties(props) # store keys self.keyMap = { "left": 0, "right": 0, "forward": 0, "backward": 0, "drift-left": 0, "drift-right": 0, "up": 0, "down": 0, "restart": 0, "firstPerson": 0, "gravity": 0} #instructions self.ins2 = addInstructions(0.12, "[Left/Right Arrow]: Rotate Left/Right") self.ins3 = addInstructions(0.18, "[Up/Down Arrow]: Fly Forward/Backward") self.ins4 = addInstructions(0.24, "[A, D]: Move Camera") self.ins5 = addInstructions(0.30, "[W, S]: Lift / Descent") self.ins6 = addInstructions(0.36, "[F]: Toggle First Person/ Third Person") self.ins7 = addInstructions(0.42, "[G]: Toggle Gravity") self.ins8 = addInstructions(0.48, "[R]: Restart") # Set up the playground # other maps: # models/toon/phase_15/hood/toontown_central.bam # models/world # CS_Map/myCSMAP.egg # Race/RaceTrack/FullTrack.blend self.environ = loader.loadModel("models/world") self.environ.reparentTo(render) self.mapScale = 1 """ self.environ = loader.loadModel("The City/The City.obj") self.environ.reparentTo(render) myTexture = loader.loadTexture("The City/Maps/cty1.jpg") self.environ.setTexture(myTexture) self.environ.setHpr(0,90,0) self.environ.setScale(.1) """ # Create drone and initalize drone position self.Drone = Actor("models/mydrone.egg") self.Drone.reparentTo(render) # resize and reposition the drone self.Drone.setScale(.1) self.Drone.setPos(5,5,8) self.Drone.setH(180) # initial position is saved for restarting the game self.DroneStartPos = self.Drone.getPos() # User Controls self.accept('escape', __import__('sys').exit, [0]) self.accept("arrow_left", self.setKey, ["left", True]) self.accept("arrow_right", self.setKey, ["right", True]) self.accept("arrow_up", self.setKey, ["forward", True]) self.accept("arrow_down", self.setKey, ["backward", True]) self.accept("a", self.setKey, ["drift-left", True]) self.accept("d", self.setKey, ["drift-right", True]) self.accept("arrow_left-up", self.setKey, ["left", False]) self.accept("arrow_right-up", self.setKey, ["right", False]) self.accept("arrow_up-up", self.setKey, ["forward", False]) self.accept("arrow_down-up", self.setKey, ["backward", False]) self.accept("a-up", self.setKey, ["drift-left", False]) self.accept("d-up", self.setKey, ["drift-right", False]) self.accept("w", self.setKey, ["up", True]) self.accept("w-up", self.setKey, ["up", False]) self.accept("s", self.setKey, ["down", True]) self.accept("s-up", self.setKey, ["down", False]) self.accept("r", self.setKey, ["restart", True]) self.accept("r-up", self.setKey, ["restart", False]) self.accept("f", self.setKey, ["firstPerson", True]) self.accept("f-up", self.setKey, ["firstPerson", False]) self.accept("g", self.setKey, ["gravity", True]) self.accept("g-up", self.setKey, ["gravity", False]) taskMgr.add(self.move, "moveTask") # Disable Mouse self.disableMouse() # Camera settings self.cameraDistance = 5 self.cameraPitch = -10 # create the collision box for the drone # this collision box will be used for collision detection self.droneBox = CollisionBox((0,0,2.5), 3, 3, 0.7) self.cnodePath = self.Drone.attachNewNode(CollisionNode('cnode')) self.cnodePath.node().addSolid(self.droneBox) # collision detection set up self.cTrav = CollisionTraverser() self.queue = CollisionHandlerQueue() self.cTrav.addCollider(self.cnodePath, self.queue) self.cTrav.traverse(render) # Lighting portion are modified from an example provided by Panda3d – Roaming Ralph ambientLight = AmbientLight("ambientLight") ambientLight.setColor((1, 1, 1, 1)) directionalLight = DirectionalLight("directionalLight") directionalLight.setDirection((-5, -5, -5)) directionalLight.setColor((1, 1, 1, 1)) directionalLight.setSpecularColor((1, 1, 1, 1)) render.setLight(render.attachNewNode(ambientLight)) render.setLight(render.attachNewNode(directionalLight)) # Crashed Text self.crashed = OnscreenText() self.firstPerson = False # HPR setting self.angle = 0 self.angleChange = 0.5 self.maxAngle = 15 # Speed Control self.FBSpeed = self.mapScale * 6 self.LRSpeed = self.mapScale * 4 self.turnSpeed = 80 self.liftSpeed = self.mapScale * 80 self.downSpeed = self.mapScale * 80 # AI set up self.AI = True if self.AI: self.droneAI = Actor("models/mydrone.egg") self.droneAI.reparentTo(render) self.droneAI.setScale(.1) self.droneAI.setPos(5,5,5) self.AI_actions = open("AI/RoamingRalph/AI_easy.txt", "r").readlines() ####################### # additional features # ####################### # acceleration self.FBacceleration = 0 self.LRacceleration = 0 self.accelMax = 40 self.accelIncrement = 2 # gravity self.gravity = True self.gravity_value = 15 * self.mapScale # Record user inputs def setKey(self, key, value): self.keyMap[key] = value # Main Function (Deal with user interface and collision detection) def move(self, task): #debug #print(self.queue.getEntries()) #print(self.drone.getPos()) #print(self.FBacceleration, self.LRacceleration) # crash message self.crashed.destroy() self.crashed = OnscreenText(text="Crashed!!!" if len(self.queue.getEntries()) != 0 else "", pos = (-0.5, 0.02), scale = 0.07, mayChange = True, fg = (255,255,255,1)) # Get the time that elapsed since last frame. dt = globalClock.getDt() # control the movement of AI if self.AI: if self.AI_actions != []: curAction = self.AI_actions[0].split(" ") self.droneAI.setX(float(curAction[0])) self.droneAI.setY(float(curAction[1])) self.droneAI.setZ(float(curAction[2])) self.droneAI.setH(float(curAction[3])) self.droneAI.setP(float(curAction[4])) self.droneAI.setR(float(curAction[5])) self.AI_actions.pop(0) # Drone is movable only when it's not crashed if len(self.queue.getEntries()) == 0: # initial height curHeight = self.Drone.getZ() # move by acceleration if self.FBacceleration != 0: self.Drone.setX(self.Drone, self.FBSpeed * self.FBacceleration * dt) self.FBacceleration += 1 if self.FBacceleration < 0 else -1 if self.LRacceleration != 0: self.Drone.setY(self.Drone, self.LRSpeed * self.LRacceleration * dt) self.LRacceleration += 1 if self.LRacceleration < 0 else -1 self.Drone.setZ(curHeight) # tilting while drift left and right if self.keyMap["drift-left"]: #self.Drone.setY(self.Drone, self.LRSpeed * dt) # tilt left when drift left if self. angle > -self.maxAngle: self.angle -= self.angleChange self.Drone.setP(self.angle) if self.LRacceleration < self.accelMax: self.LRacceleration += self.accelIncrement elif self.keyMap["drift-right"]: #self.Drone.setY(self.Drone, -self.LRSpeed * dt) # tilt right when drift right if self. angle < self.maxAngle: self.angle += self.angleChange self.Drone.setP(self.angle) if self.LRacceleration > -self.accelMax: self.LRacceleration -= self.accelIncrement # gradually stablize itself while drift-keys are not pressed else: if self.angle >=self.angleChange: self.angle -= self.angleChange elif self.angle <=-self.angleChange: self.angle +=self.angleChange self.Drone.setP(self.angle) # turn left if self.keyMap["left"]: self.Drone.setH(self.Drone.getH() + self.turnSpeed * dt) # turn right if self.keyMap["right"]: self.Drone.setH(self.Drone.getH() - self.turnSpeed * dt) # go forward if self.keyMap["forward"]: #self.Drone.setX(self.Drone, self.FBSpeed * dt) if self.FBacceleration < self.accelMax: self.FBacceleration += self.accelIncrement elif self.keyMap["backward"]: #self.Drone.setX(self.Drone, -self.FBSpeed * dt) if self.FBacceleration > -self.accelMax: self.FBacceleration -= self.accelIncrement # lift up if self.keyMap["up"]: self.Drone.setZ(self.Drone, self.liftSpeed * dt) # go down if self.keyMap["down"]: self.Drone.setZ(self.Drone, -self.downSpeed * dt) # gravity if self.gravity: self.Drone.setZ(self.Drone, -self.gravity_value * dt) # restart game / reset position if self.keyMap["restart"]: self.Drone.setPos(self.DroneStartPos + (0, 0, 5)) self.collisionCount = False self.crashed.destroy() self.Drone.setH(180) # First Person View / Third Person View Toggle if self.keyMap["firstPerson"]: self.firstPerson = not self.firstPerson # Gravity Toggle if self.keyMap["gravity"]: self.gravity = not self.gravity # uncomment the following code to see the collision box ######################## #self.cnodePath.show() # ######################## # set the position and HPR of the camera according to the position of the drone # First Person View if self.firstPerson: base.camera.setH(self.Drone.getH()-90) base.camera.setP(self.Drone.getR()) base.camera.setR(self.Drone.getP()) base.camera.setPos(self.Drone.getPos()) # Third Person View else: base.camera.setHpr(self.Drone.getHpr()+(180,0,0)) h,p,r = self.Drone.getHpr() base.camera.setPos(self.Drone.getPos() + (math.cos(math.pi * h / 180) * -self.cameraDistance, \ math.sin(math.pi * h / 180) * -self.cameraDistance, 0.5)) viewTarget = Point3(self.Drone.getPos() + (0,0,0)) # the look-at point can be changed base.camera.lookAt(viewTarget) return task.cont
class Mouse(): def __init__(self): self.enabled = False self.locked = False self.position = Vec3(0, 0, 0) self.delta = Vec3(0, 0, 0) self.prev_x = 0 self.prev_y = 0 self.velocity = Vec3(0, 0, 0) self.prev_click_time = time.time() self.double_click_distance = .5 self.hovered_entity = None self.left = False self.right = False self.middle = False self.delta_drag = Vec3(0, 0, 0) self.i = 0 self.update_rate = 10 self._mouse_watcher = None self._picker = CollisionTraverser() # Make a traverser self._pq = CollisionHandlerQueue() # Make a handler self._pickerNode = CollisionNode('mouseRay') self._pickerNP = camera.attach_new_node(self._pickerNode) self._pickerRay = CollisionRay() # Make our ray self._pickerNode.addSolid(self._pickerRay) self._picker.addCollider(self._pickerNP, self._pq) self.raycast = True self.collision = None self.enabled = True @property def x(self): if not self._mouse_watcher.has_mouse(): return 0 return self._mouse_watcher.getMouseX( ) / 2 * window.aspect_ratio # same space as ui stuff @property def y(self): if not self._mouse_watcher.has_mouse(): return 0 return self._mouse_watcher.getMouseY() / 2 def __setattr__(self, name, value): if name == 'visible': window.set_cursor_hidden(not value) application.base.win.requestProperties(window) if name == 'locked': try: object.__setattr__(self, name, value) window.set_cursor_hidden(value) application.base.win.requestProperties(window) except: pass try: super().__setattr__(name, value) # return except: pass def input(self, key): if not self.enabled: return if key.endswith('mouse down'): self.start_x = self.x self.start_y = self.y elif key.endswith('mouse up'): self.delta_drag = Vec3(self.x - self.start_x, self.y - self.start_y, 0) if key == 'left mouse down': self.left = True if self.hovered_entity: if hasattr(self.hovered_entity, 'on_click'): self.hovered_entity.on_click() for s in self.hovered_entity.scripts: if hasattr(s, 'on_click'): s.on_click() # double click if time.time( ) - self.prev_click_time <= self.double_click_distance: base.input('double click') if self.hovered_entity: if hasattr(self.hovered_entity, 'on_double_click'): self.hovered_entity.on_double_click() for s in self.hovered_entity.scripts: if hasattr(s, 'on_double_click'): s.on_double_click() self.prev_click_time = time.time() if key == 'left mouse up': self.left = False if key == 'right mouse down': self.right = True if key == 'right mouse up': self.right = False if key == 'middle mouse down': self.middle = True if key == 'middle mouse up': self.middle = False def update(self): if not self.enabled or not self._mouse_watcher.has_mouse(): self.velocity = Vec3(0, 0, 0) return self.position = Vec3(self.x, self.y, 0) self.moving = self.x + self.y != self.prev_x + self.prev_y if self.moving: if self.locked: self.velocity = self.position application.base.win.move_pointer(0, int(window.size[0] / 2), int(window.size[1] / 2)) else: self.velocity = Vec3(self.x - self.prev_x, (self.y - self.prev_y) / window.aspect_ratio, 0) else: self.velocity = Vec3(0, 0, 0) if self.left or self.right or self.middle: self.delta = Vec3(self.x - self.start_x, self.y - self.start_y, 0) self.prev_x = self.x self.prev_y = self.y self.i += 1 if self.i < self.update_rate: return # collide with ui self._pickerNP.reparent_to(scene.ui_camera) self._pickerRay.set_from_lens(camera._ui_lens_node, self.x * 2 / window.aspect_ratio, self.y * 2) self._picker.traverse(camera.ui) if self._pq.get_num_entries() > 0: # print('collided with ui', self._pq.getNumEntries()) self.find_collision() return # collide with world self._pickerNP.reparent_to(camera) self._pickerRay.set_from_lens(scene.camera.lens_node, self.x * 2 / window.aspect_ratio, self.y * 2) self._picker.traverse(base.render) if self._pq.get_num_entries() > 0: # print('collided with world', self._pq.getNumEntries()) self.find_collision() return # else: # print('mouse miss', base.render) # unhover all if it didn't hit anything for entity in scene.entities: if hasattr(entity, 'hovered') and entity.hovered: entity.hovered = False self.hovered_entity = None if hasattr(entity, 'on_mouse_exit'): entity.on_mouse_exit() for s in entity.scripts: if hasattr(s, 'on_mouse_exit'): s.on_mouse_exit() @property def normal(self): if not self.collision: return None if not self.collision.has_surface_normal(): print('no surface normal') return None n = self.collision.get_surface_normal( self.collision.get_into_node_path().parent) return (n[0], n[2], n[1]) @property def world_normal(self): if not self.collision: return None if not self.collision.has_surface_normal(): print('no surface normal') return None n = self.collision.get_surface_normal(render) return (n[0], n[2], n[1]) @property def point(self): if self.hovered_entity: p = self.collision.getSurfacePoint(self.hovered_entity) return Point3(p[0], p[2], p[1]) else: return None @property def world_point(self): if self.hovered_entity: p = self.collision.getSurfacePoint(render) return Point3(p[0], p[2], p[1]) else: return None def find_collision(self): if not self.raycast: return self._pq.sortEntries() if len(self._pq.get_entries()) == 0: self.collision = None return self.collisions = list() for entry in self._pq.getEntries(): # print(entry.getIntoNodePath().parent) for entity in scene.entities: if entry.getIntoNodePath().parent == entity: if entity.collision: self.collisions.append( Hit( hit=entry.collided(), entity=entity, distance=0, point=entry.getSurfacePoint(entity), world_point=entry.getSurfacePoint(scene), normal=entry.getSurfaceNormal(entity), world_normal=entry.getSurfaceNormal(scene), )) break self.collision = self._pq.getEntry(0) nP = self.collision.getIntoNodePath().parent for entity in scene.entities: if not hasattr(entity, 'collision' ) or not entity.collision or not entity.collider: continue # if hit entity is not hovered, call on_mouse_enter() if entity == nP: if not entity.hovered: entity.hovered = True self.hovered_entity = entity # print(entity.name) if hasattr(entity, 'on_mouse_enter'): entity.on_mouse_enter() for s in entity.scripts: if hasattr(s, 'on_mouse_enter'): s.on_mouse_enter() # unhover the rest else: if entity.hovered: entity.hovered = False if hasattr(entity, 'on_mouse_exit'): entity.on_mouse_exit() for s in entity.scripts: if hasattr(s, 'on_mouse_exit'): s.on_mouse_exit()
class FlyThroughAir(ShowBase): # method completely taken from RoamingRalph demo: def addInstructions(self, pos, msg): return OnscreenText(text=msg, style=1, fg=(1, 1, 1, 1), scale=.05, shadow=(0, 0, 0, 1), parent=base.a2dTopLeft, pos=(0.08, -pos - 0.04), align=TextNode.ALeft) def setUpFlyingInstructions(self): self.inst[0] = self.addInstructions(.06, "Arrow Keys to move around") self.inst[1] = self.addInstructions(.12, "w and s to control pitch") self.inst[2] = self.addInstructions(.18, "a and d to control yaw") self.inst[3] = self.addInstructions(.24, "h to switch to driving mode") self.inst[4] = self.addInstructions(.3, "mouse click to add object") def destroyInstructions(self): # got way to destroy text from: # https://www.panda3d.org/manual/index.php/OnscreenText for element in self.inst: element.destroy() def setUpTreeButton(self): pass def setUpDrivingInstructions(self): self.inst[0] = self.addInstructions(.06, "Right arrow and left arrow to turn") self.inst[1] = self.addInstructions(.12, "Forward and Backward arrow to go forward and backward") self.inst[2] = self.addInstructions(.18, "h to switch to add object mode") def __init__(self): # Set up the window, camera, etc. ShowBase.__init__(self) # Set the background color to black self.win.setClearColor((0, 1, 1, 1)) # This is used to store which keys are currently pressed. self.keyMap = { "left": 0, "right": 0, "forward": 0, "cam-left": 0, "cam-right": 0, "backward": 0, "cam-up": 0, "cam-down": 0, "add-car": 0, "switch-mode":0, "mouse-click":0} # this is the egg that came with the module # environ = loader.loadModel("models/world") # this is the one I created using mountainMaker.py self.environ = loader.loadModel("TestMountain1") self.environ.reparentTo(render) self.car = loader.loadModel("TestCar") #self.car = loader.loadModel("testModel/ball") self.car.reparentTo(render) #instructions self.inst = [""]*5 self.setUpFlyingInstructions() # for adjusting so that the position is the center of the car self.adjustedXForCenter = 10/2 self.adjustedYForCenter = 20/2 # important for setting the size relative to everything else # found it here : https://www.panda3d.org/manual/index.php/Common_State_Changes # set the mode that the player is currently in self.mode = 0 self.modeFly = 0 self.modeRace = 1 # to ensure that when pressing h it only switches once each press self.hasSwitched = False self.carPositionX = 10 self.carPositionY = 10 self.carPositionZ = 100 # note for rotating camera: from this website: # https://www.panda3d.org/manual/index.php/Common_State_Changes # setHpr(Yaw, Pitch, Roll) # setting up initial conditions for which way camera is rotated self.carYaw = 0 self.carPitch = 0 self.setUpCarCollider() self.setUpMouseCollider() # make the rocks and other stuff that will show up self.objects = [] # Accept the control keys for movement and rotation #setting up keys for movement self.accept("escape", sys.exit) self.accept("arrow_left", self.setKey, ["left", True]) self.accept("arrow_right", self.setKey, ["right", True]) self.accept("arrow_up", self.setKey, ["forward", True]) self.accept("arrow_down", self.setKey, ["backward", True]) self.accept("arrow_left-up", self.setKey, ["left", False]) self.accept("arrow_right-up", self.setKey, ["right", False]) self.accept("arrow_up-up", self.setKey, ["forward", False]) self.accept("arrow_down-up", self.setKey, ["backward", False]) # adding car self.accept("mouse1", self.setKey, ["mouse-click", True]) self.accept("mouse1-up", self.setKey, ["mouse-click", False]) # setting up orientation of the camera self.accept("a", self.setKey, ["cam-left", True]) self.accept("s", self.setKey, ["cam-down", True]) self.accept("a-up", self.setKey, ["cam-left", False]) self.accept("s-up", self.setKey, ["cam-down", False]) self.accept("d", self.setKey, ["cam-right", True]) self.accept("d-up", self.setKey, ["cam-right", False]) self.accept("w", self.setKey, ["cam-up", True]) self.accept("w-up", self.setKey, ["cam-up", False]) # to switch between tasks self.accept("h", self.setKey, ["switch-mode", True]) self.accept("h-up", self.setKey, ["switch-mode", False]) taskMgr.add(self.move, "moveTask") # Game state variables self.isMoving = False self.cameraPositionX = 0 self.cameraPositionY = 0 self.cameraPositionZ = 0 # note for rotating camera: from this website: # https://www.panda3d.org/manual/index.php/Common_State_Changes # setHpr(Yaw, Pitch, Roll) # setting up initial conditions for which way camera is rotated self.cameraYaw = 0 self.cameraPitch = 0 # Set up the camera self.disableMouse() # should probably clean up these magic numbers self.camera.setPos(20, 20, 20) # Create some lighting # this is a part that is completely unchanged from demo ambientLight = AmbientLight("ambientLight") ambientLight.setColor((.3, .3, .3, 1)) directionalLight = DirectionalLight("directionalLight") directionalLight.setDirection((-5, -5, -5)) directionalLight.setColor((1, 1, 1, 1)) directionalLight.setSpecularColor((1, 1, 1, 1)) render.setLight(render.attachNewNode(ambientLight)) render.setLight(render.attachNewNode(directionalLight)) def setUpCarCollider(self): self.carCollideTrav = CollisionTraverser() base.cTrav = self.carCollideTrav self.handler = CollisionHandlerQueue() self.carRay = CollisionRay(self.carPositionX, self.carPositionY, self.carPositionZ, 0, 0, -1) self.carCollision = CollisionNode("groundCollision") self.carCollision.addSolid(self.carRay) # add to .egg <Scalar> collide-mask { 0x0 } #self.carCollision.setFromCollideMask(CollideMask.bit(0)) #self.carCollision.setIntoCollideMask(CollideMask.allOff()) self.carCollisionNode = self.car.attachNewNode(self.carCollision) self.carCollideTrav.addCollider(self.carCollisionNode, self.handler) self.carCollisionNode.show() def setUpMouseCollider(self): # clicking on objects stuff came from here: # https://www.panda3d.org/manual/index.php/Collision_Traversers # https://www.panda3d.org/manual/index.php/Collision_Handlers # will not use the traverser set up by car because slow # instead we will render each time clicked self.mouseCollideTrav = CollisionTraverser("mouseTraverse") self.mousehandler = CollisionHandlerQueue() # edit this so that from Object is the camera # self.mouseCollideTrav.addCollider(fromObject, queue) # self.mouseCollideTrav.traverse(render) # this next part came from: # https://www.panda3d.org/manual/index.php/Clicking_on_3D_Objects pickerNode = CollisionNode("mouseRay") pickerNp = camera.attachNewNode(pickerNode) pickerNode.setFromCollideMask(GeomNode.getDefaultCollideMask()) self.pickerRay = CollisionRay() pickerNode.addSolid(self.pickerRay) self.mouseCollideTrav.addCollider(pickerNp, self.mousehandler) # Records the state of the arrow keys def setKey(self, key, value): self.keyMap[key] = value def move(self, task): if(self.mode==self.modeFly): # Get the time that elapsed since last frame. We multiply this with # the desired speed in order to find out with which distance to move # in order to achieve that desired speed. dt = globalClock.getDt() if self.keyMap["switch-mode"] and not self.hasSwitched: self.mode = (self.mode+1)%2 self.destroyInstructions() self.setUpDrivingInstructions() self.hasSwitched = True elif not self.keyMap["switch-mode"]: self.hasSwitched = False # the angle is in degrees with 360 equal to full rotation angleAdjustment = 100 if self.keyMap["cam-left"]: self.cameraYaw += angleAdjustment*dt if self.keyMap["cam-right"]: self.cameraYaw -= angleAdjustment*dt if self.keyMap["cam-up"]: self.cameraPitch += angleAdjustment*dt if self.keyMap["cam-down"]: self.cameraPitch -= angleAdjustment*dt positionAdjustment = 500 # should switch rad and Deg in variable name radToDeg = math.pi/180 # the x and y component of left and right moves, do not need to # compensate in z axis because not doing any roll, so there should be # no zComponent xComponent = math.cos(self.cameraYaw*radToDeg) yComponent = math.sin(self.cameraYaw*radToDeg) if self.keyMap["left"]: self.cameraPositionX -= positionAdjustment * dt *xComponent self.cameraPositionY -= positionAdjustment * dt *yComponent if self.keyMap["right"]: self.cameraPositionX += positionAdjustment * dt*xComponent self.cameraPositionY += positionAdjustment * dt*yComponent # for going forward, the orientation is rotated 90 degrees so need to # change components xComponent = math.cos(self.cameraYaw*radToDeg+math.pi/2)*math.cos( self.cameraPitch*radToDeg) yComponent = math.sin(self.cameraYaw*radToDeg+math.pi/2)*math.cos( self.cameraPitch*radToDeg) zComponent = math.sin(self.cameraPitch*radToDeg) if self.keyMap["forward"]: self.cameraPositionX += positionAdjustment * dt*xComponent self.cameraPositionY += positionAdjustment * dt*yComponent self.cameraPositionZ += positionAdjustment * dt*zComponent if self.keyMap["backward"]: self.cameraPositionX -= positionAdjustment * dt*xComponent self.cameraPositionY -= positionAdjustment * dt*yComponent self.cameraPositionZ -= positionAdjustment * dt*zComponent self.camera.setX(self.cameraPositionX) self.camera.setY(self.cameraPositionY) self.camera.setZ(self.cameraPositionZ) self.camera.setHpr(self.cameraYaw, self.cameraPitch, 0) # when implementing the use of the mouse look here: # https://www.panda3d.org/manual/index.php/Clicking_on_3D_Objects # clicking on 3D objects comes from here: # https://www.panda3d.org/manual/index.php/Clicking_on_3D_Objects # checks if it needs to add any objects: if(self.keyMap["mouse-click"]): # found way to speed this up by only doing collision check # when mouse clicked by not using cTrav like in this method # the way I did it I found here: # https://www.panda3d.org/manual/index.php/Clicking_on_3D_Objects self.mouseCollideTrav.traverse(render) if(base.mouseWatcherNode.hasMouse()): mousePos = base.mouseWatcherNode.getMouse() self.pickerRay.setFromLens(base.camNode, mousePos.getX(), mousePos.getY()) if(self.mousehandler.getNumEntries()>0): entries = list(self.mousehandler.getEntries()) # pathagorean formula for sorting entries.sort(key=lambda x: ((x.getSurfacePoint(render).getX( )-self.cameraPositionX)**2 + (x.getSurfacePoint(render).getY()- self.cameraPositionY)**2)**.5) newX = entries[0].getSurfacePoint(render).getX() newY = entries[0].getSurfacePoint(render).getY() newZ = entries[0].getSurfacePoint(render).getZ() self.objects.append(loader.loadModel("TestCar.egg")) self.objects[len(self.objects)-1].reparentTo(render) self.objects[len(self.objects)-1].setPos(newX, newY, newZ) return task.cont elif(self.mode == self.modeRace): # Get the time that elapsed since last frame. We multiply this with # the desired speed in order to find out with which distance to move # in order to achieve that desired speed. dt = globalClock.getDt() degreeAdjustment = 60 positionAdjustment = 100 # should switch rad and Deg in variable name radToDeg = math.pi/180 # the x and y component of left and right moves, do not need to # compensate in z axis because not doing any roll, so there should be # no zComponent xComponent = math.sin(self.carYaw*radToDeg) yComponent = math.cos(self.carYaw*radToDeg) if self.keyMap["switch-mode"] and not self.hasSwitched: self.mode = (self.mode+1)%2 self.destroyInstructions() self.setUpFlyingInstructions() self.hasSwitched = True elif not self.keyMap["switch-mode"]: self.hasSwitched = False if self.keyMap["left"]: self.carYaw += degreeAdjustment * dt if self.keyMap["right"]: self.carYaw -= degreeAdjustment * dt if self.keyMap["forward"]: self.carPositionX -= positionAdjustment * dt*xComponent self.carPositionY += positionAdjustment * dt*yComponent if self.keyMap["backward"]: self.carPositionX += positionAdjustment * dt*xComponent self.carPositionY -= positionAdjustment * dt*yComponent # need to consider both the x and y component of offset for both # because x slowly changes to y as it turns actualXPos = (self.carPositionX+self.adjustedXForCenter* math.cos(radToDeg*self.carYaw)+self.adjustedYForCenter *math.sin(radToDeg*self.carYaw)) actualYPos = (self.carPositionY+self.adjustedYForCenter* math.cos(radToDeg*self.carYaw)+self.adjustedXForCenter *math.sin(radToDeg*self.carYaw)) self.car.setX(actualXPos) self.car.setY(actualYPos) self.car.setZ(self.carPositionZ) self.car.setHpr(self.carYaw, self.carPitch, 0) # when implementing the use of the mouse look here: # https://www.panda3d.org/manual/index.php/Clicking_on_3D_Objects # almost directly taken from ralph example entries = list(self.handler.getEntries()) entries.sort(key=lambda x: x.getSurfacePoint(render).getZ()) # worry about which thing it collides with later if (len(entries) > 0): # and entries[0].getIntoNode().getName() == "mountainCollide": self.carPositionZ = (entries[0].getSurfacePoint(render).getZ()) else: # because at 100 everything should be below car and do not want # to continually go up or else it may go up forever. self.car.setZ(100) print("less") # Modify view of camera so that it is behind car distanceBehind = 40 distanceAbove = 20 self.camera.setHpr(self.carYaw, -.5, 0) camX = actualXPos + distanceBehind * math.sin(radToDeg*self.carYaw) camY = actualYPos - distanceBehind * math.cos(radToDeg*self.carYaw) camZ = self.carPositionZ + distanceAbove self.camera.setPos(camX, camY, camZ) return task.cont
class World(ShowBase): def __init__(self): ShowBase.__init__(self) #PStatClient.connect() #self.lookPoint = NodePath(PandaNode("floater")) self.lookPoint = self.loader.loadModel("models/cone") self.lookPoint.reparentTo(render) self.menu = StartMenu(self) #self.bar = Bar() #self.messenger.toggleVerbose() #sys.exit() #pdb.set_trace() # Window change event handler #self.windowEventSetup() def setup(self): print("Init Levels...") self.initLevels() print("Init World...") self.initWorld("yard") print("Init Items...") self.initItems() print("Init Actors...") self.initActors() print("Init GUI ...") self.initGui() print("Init Lights...") self.initLights() print("Init Collisions...") self.initCollision() print("Init Tasks...") self.initTasks() print("Launching World") # Accept the control keys #self.accept("h", self.crono.start) def initItems(self): with open('items/items.json') as items_file: self.items = json.load(items_file) def initActors(self): self.player = Player(self, 20, 10, 10, 10, 10, 10) #app, hp, mana, strength, dexterity, vigor, magic): self.enemies = [] self.npcs = [] #Creating AI World self.AIworld = AIWorld(render) """self.foe1 = Enemy(self, 100, 50, 5, 2, "bug") #(self, app, hp, mana, speed, attackSpeed, name): self.nasgul = Enemy(self, 100, 50, 5, 2, "nasgul") self.npc1 = Npc(self, 100, 50, 5, 2, "guy2") self.npc2 = Npc(self, 100, 50, 5, 2, "ralph") self.enemies.append(self.foe1) self.enemies.append(self.nasgul) self.npcs.append(self.npc1) self.npcs.append(self.npc2)""" def initGui(self): # Load the models. self.inventory = Inventory(self) self.status = Status(self) self.skills = Skills(self) #self.statusBar = self.loader.loadModel("models/statusbar") ##self.statusBar.setDepthTest(True) #self.statusBar.setDepthWrite(True) # Reparent the model to render2d. #self.statusBar.reparentTo(self.render2d) #self.statusBar.setScale(0.15, 0.15, 0.15) #self.statusBar.setPos(-0.95, 0, 0.65) #self.crono = Crono(self) ##self.cursorpos = CursorPos(self) #self.playerpos = PlayerPos(self) #self.crono.draw(0.7, -0.85) #self.cursorpos.draw(0.0, -0.85) #self.playerpos.draw(-0.7, -0.85) self.accept("i", self.inventory.toggle) self.accept("c", self.status.toggle) self.accept("k", self.skills.toggle) def initTasks(self): #self.taskMgr.add(self.crono.task, "cronoTask") #self.taskMgr.add(self.cursorpos.task, "cursorposTask") ###self.taskMgr.add(self.playerpos.task, "playerposTask") self.taskMgr.add(self.checkCollision, "collisionTask") #self.taskMgr.add(self.player.update, "updateTask") self.taskMgr.add(self.player.move, "moveTask") self.taskMgr.add(self.player.updateCamera, "playerCameraTask",priority=1) """ #self.taskMgr.add(self.foe1.update, "bugTask",priority=1) #self.taskMgr.add(self.nasgul.update, "nasgulTask",priority=1) #self.taskMgr.add(self.npc1.update, "npc1Task",priority=1) #self.taskMgr.add(self.npc2.update, "npc2Task",priority=1) """ self.taskMgr.add(self.update, 'update') def initLights(self): # Create some lighting #self.environ.ls() #print(self.environ.findAllMatches("**/Spot")) ambientLight = AmbientLight("ambientLight") ambientLight.setColor(Vec4(0.8, 0.8, 0.8, 0.65)) """ directionalLight = DirectionalLight("directionalLight") directionalLight.setDirection(Vec3(-10, -10, 5)) directionalLight.showFrustum() directionalLight.setColor(Vec4(1, 1, 1, 1)) directionalLight.setSpecularColor(Vec4(1, 1, 1, 1)) dirnp = render.attachNewNode(directionalLight) dirnp.setPos(10, 0, 6) """ plight1 = PointLight('plight1') plight1.setColor(VBase4(1, 1, 1, 1)) plight1.showFrustum() #plight1.setShadowCaster(True) plnp1 = render.attachNewNode(plight1) plnp1.setPos(26.71, -33.2, 26) plight2 = PointLight('plight2') plight2.setColor(VBase4(1, 1, 1, 1)) plight2.showFrustum() plnp2 = render.attachNewNode(plight2) plnp2.setPos(-25, 25, 25) slight = Spotlight('slight') slight.setColor(VBase4(1, 1, 1, 1)) lens = PerspectiveLens() lens.setFilmSize(1, 1) # Or whatever is appropriate for your scene slight.setLens(lens) slight.setShadowCaster(True, 512, 512) slight.showFrustum() slnp = render.attachNewNode(slight) slnp.setPos(0, 0, 100) slnp.lookAt(Vec3(0,0,0)) render.setLight(slnp) render.setLight(plnp1) render.setLight(plnp2) #render.setLight(render.attachNewNode(ambientLight)) #render.setLight(dirnp) render.setShaderAuto() #render.setLight(render.attachNewNode(directionalLight)) """ self.light = render.attachNewNode(Spotlight("Spot")) self.light.node().setScene(render) self.light.node().setShadowCaster(True) self.light.node().showFrustum() self.light.node().getLens().setFov(40) self.light.node().getLens().setNearFar(10,100) render.setLight(self.light) """ def initLevels(self): with open('levels/levels.json') as levels_file: self.levels = json.load(levels_file) def initWorld(self, level): self.environ = self.loader.loadModel(self.levels["levels"][level]["model"]) #self.environ.setScale(20, 20, 20) #self.environ.setHpr(0, 0, 0) self.environ.setPos(0, 0, 0) self.playerStartPos = self.environ.find("**/startPos").getPos() # Reparent the model to render self.environ.reparentTo(render) #self.environ.ls() self.accept("q", self.changeMap) def destroyWorld(self): self.environ.detachNode() self.environ.removeNode() def changeMap(self):#, levelName): self.destroyWorld() self.initWorld("yard") def update(self, task): dt = globalClock.getDt() self.AIworld.update() return task.cont def setKey(self, key, value): self.keyMap[key] = value def windowEventSetup( self ): # accept the window event's self.accept( 'window-event', self.windowEventHandler) # create a window event yourself #messenger.send( 'window-event', [self.win] ) def windowEventHandler( self, window=None ): wp = window.getProperties() print("Window changed") print("X = %s" % wp.getXSize()) print("Y = %s" % wp.getYSize()) self.setResolution( wp.getXSize(), wp.getYSize() ) def setResolution( self, w, h ): wp = WindowProperties() wp.setSize( w, h ) if os.name == 'posix': self.openMainWindow() self.graphicsEngine.openWindows() self.win.requestProperties( wp ) # Define a procedure to move the camera. def spinCameraTask(self, task): angleDegrees = task.time * 6.0 angleRadians = angleDegrees * (pi / 180.0) self.camera.setPos(40 * sin(angleRadians), -10.0 * cos(angleRadians), 3) self.camera.setHpr(angleDegrees, 0, 0) return Task.cont def initCollision(self): self.enemyGroundRay = [] self.enemyGroundCol = [] self.enemyGroundColNp = [] self.enemyGroundHandler = [] self.npcGroundRay = [] self.npcGroundCol = [] self.npcGroundColNp = [] self.npcGroundHandler = [] self.cTrav = CollisionTraverser() self.playerGroundRay = CollisionRay() self.playerGroundRay.setOrigin(0, 0, 9) self.playerGroundRay.setDirection(0, 0, -1) self.playerGroundCol = CollisionNode('playerRay') self.playerGroundCol.addSolid(self.playerGroundRay) self.playerGroundCol.setFromCollideMask(CollideMask.bit(0)) self.playerGroundCol.setIntoCollideMask(CollideMask.allOff()) self.playerGroundColNp = self.player.moveFloater.attachNewNode(self.playerGroundCol) self.playerGroundHandler = CollisionHandlerQueue() self.cTrav.addCollider(self.playerGroundColNp, self.playerGroundHandler) self.mouseGroundRay = CollisionRay() self.mouseGroundRay.setOrigin(0, 0, 9) self.mouseGroundRay.setDirection(0, 0, -1) self.mouseGroundCol = CollisionNode('mouseRay') self.mouseGroundCol.addSolid(self.mouseGroundRay) self.mouseGroundCol.setFromCollideMask(CollideMask.bit(0)) self.mouseGroundCol.setIntoCollideMask(CollideMask.allOff()) self.mouseGroundColNp = self.camera.attachNewNode(self.mouseGroundCol) self.mouseGroundHandler = CollisionHandlerQueue() self.cTrav.addCollider(self.mouseGroundColNp, self.mouseGroundHandler) # Uncomment this line to see the collision rays #self.playerGroundColNp.show() #self.mouseGroundColNp.show() # Uncomment this line to show a visual representation of the # collisions occuring #self.cTrav.showCollisions(render) i = 0 for enemy in self.enemies: self.enemyGroundRay.append(CollisionRay()) self.enemyGroundRay[i].setOrigin(0, 0, 9) self.enemyGroundRay[i].setDirection(0, 0, -1) self.enemyGroundCol.append(CollisionNode('%sRay' % enemy.name)) self.enemyGroundCol[i].addSolid(self.enemyGroundRay[i]) self.enemyGroundCol[i].setFromCollideMask(CollideMask.bit(0)) self.enemyGroundCol[i].setIntoCollideMask(CollideMask.allOff()) self.enemyGroundColNp.append(enemy.enemyActor.attachNewNode(self.enemyGroundCol[i])) self.enemyGroundHandler.append(CollisionHandlerQueue()) self.cTrav.addCollider(self.enemyGroundColNp[i], self.enemyGroundHandler[i]) #self.enemyGroundColNp.show() i += 1 i = 0 for npc in self.npcs: self.npcGroundRay.append(CollisionRay()) self.npcGroundRay[i].setOrigin(0, 0, 9) self.npcGroundRay[i].setDirection(0, 0, -1) self.npcGroundCol.append(CollisionNode('%sRay' % npc.name)) self.npcGroundCol[i].addSolid(self.npcGroundRay[i]) self.npcGroundCol[i].setFromCollideMask(CollideMask.bit(0)) self.npcGroundCol[i].setIntoCollideMask(CollideMask.allOff()) self.npcGroundColNp.append(npc.npcActor.attachNewNode(self.npcGroundCol[i])) self.npcGroundHandler.append(CollisionHandlerQueue()) self.cTrav.addCollider(self.npcGroundColNp[i], self.npcGroundHandler[i]) #self.npcGroundColNp.show() i += 1 def checkCollision(self, task): startpos = self.player.moveFloater.getPos() entries = list(self.playerGroundHandler.getEntries()) entries.sort(key=lambda x: x.getSurfacePoint(render).getZ()) for entry in entries: if entry > 0 and entries[0].getIntoNode().getName() == "Ground": self.player.moveFloater.setZ(entry.getSurfacePoint(render).getZ()) else: self.player.moveFloater.setPos(startpos) if self.mouseWatcherNode.hasMouse(): mpos = self.mouseWatcherNode.getMouse() self.mouseGroundRay.setFromLens(self.camNode, mpos.getX(), mpos.getY()) nearPoint = render.getRelativePoint(self.camera, self.mouseGroundRay.getOrigin()) nearVec = render.getRelativeVector(self.camera, self.mouseGroundRay.getDirection()) try: self.lookPoint.setPos(PointAtZ(self.player.moveFloater.getZ(), nearPoint, nearVec)) except: pass i = 0 for enemy in self.enemies: startpos = enemy.enemyActor.getPos() entries = list(self.enemyGroundHandler[i].getEntries()) entries.sort(key=lambda x: x.getSurfacePoint(render).getZ()) for entry in entries: if entry > 0: # and entries[0].getIntoNode().getName() == "Ground": enemy.enemyActor.setZ(entry.getSurfacePoint(render).getZ()) else: enemy.enemyActor.setPos(startpos) i += 1 i = 0 for npc in self.npcs: startpos = npc.npcActor.getPos() entries = list(self.npcGroundHandler[i].getEntries()) entries.sort(key=lambda x: x.getSurfacePoint(render).getZ()) for entry in entries: if entry > 0: # and entries[0].getIntoNode().getName() == "Ground": npc.npcActor.setZ(entry.getSurfacePoint(render).getZ()) else: npc.npcActor.setPos(startpos) i += 1 return task.cont
class MousePicker(object): def __init__(self, pickTag='MyPickingTag', nodeName='pickRay', showCollisions=False): self.pickTag = pickTag self.nodeName = nodeName self.showCollisions = showCollisions def create(self): self.mPickerTraverser = CollisionTraverser() self.mCollisionQue = CollisionHandlerQueue() self.mPickRay = CollisionRay() self.mPickRay.setOrigin(base.camera.getPos(base.render)) self.mPickRay.setDirection( base.render.getRelativeVector(base.camera, Vec3(0, 1, 0))) #create our collison Node to hold the ray self.mPickNode = CollisionNode(self.nodeName) self.mPickNode.addSolid(self.mPickRay) #Attach that node to the camera since the ray will need to be positioned #relative to it, returns a new nodepath #well use the default geometry mask #this is inefficent but its for mouse picking only self.mPickNP = base.camera.attachNewNode(self.mPickNode) #we'll use what panda calls the "from" node. This is reall a silly convention #but from nodes are nodes that are active, while into nodes are usually passive environments #this isnt a hard rule, but following it usually reduces processing #Everything to be picked will use bit 1. This way if we were doing other #collision we could seperate it, we use bitmasks to determine what we check other objects against #if they dont have a bitmask for bit 1 well skip them! self.mPickNode.setFromCollideMask(BitMask32(1)) #Register the ray as something that can cause collisions self.mPickerTraverser.addCollider(self.mPickNP, self.mCollisionQue) #Setup 2D picker self.mPickerTraverser2D = CollisionTraverser() self.mCollisionQue2D = CollisionHandlerQueue() self.mPickNode2D = CollisionNode('2D PickNode') self.mPickNode2D.setFromCollideMask(BitMask32(1)) self.mPickNode2D.setIntoCollideMask(BitMask32.allOff()) self.mPick2DNP = base.camera2d.attachNewNode(self.mPickNode2D) self.mPickRay2D = CollisionRay() self.mPickNode2D.addSolid(self.mPickRay2D) self.mPickerTraverser2D.addCollider(self.mPick2DNP, self.mCollisionQue2D) if self.showCollisions: self.mPickerTraverser.showCollisions(base.render) self.mPickerTraverser2D.showCollisions(base.aspect2d) def mousePick(self, traverse=None, tag=None): #do we have a mouse if (base.mouseWatcherNode.hasMouse() == False): return None, None traverse = traverse or base.render tag = tag or self.pickTag mpos = base.mouseWatcherNode.getMouse() #Set the position of the ray based on the mouse position self.mPickRay.setFromLens(base.camNode, mpos.getX(), mpos.getY()) self.mPickerTraverser.traverse(traverse) if (self.mCollisionQue.getNumEntries() > 0): self.mCollisionQue.sortEntries() for entry in self.mCollisionQue.getEntries(): pickedObj = entry.getIntoNodePath() pickedObj = pickedObj.findNetTag(tag) if not pickedObj.isEmpty(): pos = entry.getSurfacePoint(base.render) return pickedObj, pos return None, None def mousePick2D(self, traverse=None, tag=None, all=False): #do we have a mouse if (base.mouseWatcherNode.hasMouse() == False): return None, None traverse = traverse or base.render tag = tag or self.pickTag mpos = base.mouseWatcherNode.getMouse() self.mPickRay2D.setFromLens(base.cam2d.node(), mpos.getX(), mpos.getY()) self.mPickerTraverser2D.traverse(base.aspect2d) if self.mCollisionQue2D.getNumEntries() > 0: self.mCollisionQue2D.sortEntries() if all: return [(entry.getIntoNodePath().findNetTag(tag), entry.getSurfacePoint(base.aspect2d)) for entry in self.mCollisionQue2D.getEntries()], None else: entry = self.mCollisionQue2D.getEntry(0) pickedObj = entry.getIntoNodePath() pickedObj = pickedObj.findNetTag(tag) if not pickedObj.isEmpty(): pos = entry.getSurfacePoint(base.aspect2d) return pickedObj, pos return None, None
class PandaPath(ShowBase): def __init__(self): # Set up the window, camera, etc. ShowBase.__init__(self) # Set the background color to black self.win.setClearColor((0, 0, 0, 1)) # This is used to store which keys are currently pressed. self.key_map = {"left": 0, "right": 0, "forward": 0, "cam-left": 0, "cam-right": 0} # Post the Instruction self.title = add_title("Panda 3D Demo") # Instructions self.inst1 = add_instructions(0.06, "[ESC]: Quit") self.inst2 = add_instructions(0.12, "[Left Arrow]: Rotate Left") self.inst3 = add_instructions(0.18, "[Right Arrow]: Rotate Ralph Right") self.inst4 = add_instructions(0.24, "[Up Arrow]: Run Ralph Forward") # self.inst6 = add_instructions(0.30, "[A]: Rotate Camera Left") # self.inst7 = add_instructions(0.36, "[S]: Rotate Camera Right") # Load the Environment Model self.environ = self.loader.loadModel(ENVIRONMENT) # Reparent the model to the render controller self.environ.reparentTo(render) # Apply scale and position transform on the model # self.environ.setScale(0.25, 0.25, 0.25) # self.environ.setPos(-8, 42, 0) # Create the Main Character # Load and transform the panda actor ralph_start_pos = self.environ.find("**/start_point").getPos() self.ralph = Actor(RALPH_ACTOR, RALPH_ACTIONS) # self.ralph.setScale(0.005, 0.005, 0.005) self.ralph.reparentTo(render) # Loop it's animation self.ralph.setScale(0.2) self.ralph.setPos(ralph_start_pos + (0, 0, 0.5)) # self.ralph.loop("walk") # Create a floater object, which floats 2 units above rlaph. We use this as a target for the camera to look at self.floater = NodePath(PandaNode("floater")) self.floater.reparentTo(self.ralph) # 2 Units above Ralph self.floater.setZ(2.0) # Configure the Camera # Add the spin camera taks procedure to the taks manager # self.taskMgr.add(self.spinCameraTask, "SpinCameraTask") # Accept certain control keys self.accept("escape", sys.exit) self.accept("arrow_left", self.set_key, ["left", True]) self.accept("arrow_right", self.set_key, ["right", True]) self.accept("arrow_up", self.set_key, ["forward", True]) self.accept("a", self.set_key, ["cam-left", True]) self.accept("s", self.set_key, ["cam-right", True]) self.accept("arrow_left-up", self.set_key, ["left", False]) self.accept("arrow_right-up", self.set_key, ["right", False]) self.accept("arrow_up-up", self.set_key, ["forward", False]) self.accept("a-up", self.set_key, ["cam-left", False]) self.accept("s-up", self.set_key, ["cam-right", False]) # Game States taskMgr.add(self.move, "moveTask") self.is_moving = False self.disableMouse() self.camera.setPos(self.ralph.getX(), self.ralph.getY() + 10, 2) self.camera.lookAt(self.floater) """ Detect the height of hte terrain by creating a collision ray and casting it down towards 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 ca ndetect the height. If it hits anything else, we rule that the move is illegal. """ self.cTrav = CollisionTraverser() # Create a vector, it's location is at the top of ralph's head self.ralph_ground_ray = CollisionRay() self.ralph_ground_ray.setOrigin(0, 0, 9) # Top of ralph's head self.ralph_ground_ray.setDirection(0, 0, -1) # Points -Z axis # Create a Collision node self.ralph_ground_col = CollisionNode("ralph_ray") # Give the node a name self.ralph_ground_col.addSolid(self.ralph_ground_ray) # the vector from ralph's head to the ground is solid self.ralph_ground_col.setFromCollideMask(CollideMask.bit(0)) # ?? self.ralph_ground_col.setIntoCollideMask( CollideMask.allOff() ) # ?? This seems like it defines the behavior of ray self.ralph_ground_col_np = self.ralph.attachNewNode(self.ralph_ground_col) # Attach the ray to ralph self.ralph_ground_handler = ( CollisionHandlerQueue() ) # I think that when a collision occurs it sends through this self.cTrav.addCollider(self.ralph_ground_col_np, self.ralph_ground_handler) # Attach the collision """ Attach the a camera ray to the camera """ self.cam_ground_ray = CollisionRay() self.cam_ground_ray.setOrigin(0, 0, 9) self.cam_ground_ray.setDirection(0, 0, -1) self.cam_ground_col = CollisionNode("camera_ray") self.cam_ground_col.addSolid(self.cam_ground_ray) self.cam_ground_col.setFromCollideMask(CollideMask.bit(0)) self.cam_ground_col.setIntoCollideMask(CollideMask.allOff()) self.cam_ground_col_np = self.camera.attachNewNode(self.cam_ground_col) self.cam_ground_handler = CollisionHandlerQueue() self.cTrav.addCollider(self.cam_ground_col_np, self.cam_ground_handler) # Uncomment the following lines to view the rays self.ralph_ground_col_np.show() self.cam_ground_col_np.show() # Uncomment this line to show a visual representation of the Collision occuring self.cTrav.showCollisions(render) # Create some lighting ambient_light = AmbientLight("ambient_light") ambient_light.setColor((0.3, 0.3, 0.3, 1)) directional_light = DirectionalLight("directional_light") directional_light.setDirection((-5, -5, -5)) directional_light.setColor((1, 1, 1, 1)) directional_light.setSpecularColor((1, 1, 1, 1)) render.setLight(render.attachNewNode(ambient_light)) render.setLight(render.attachNewNode(directional_light)) # Add Cube X_OFFSET = 0 Y_OFFSET = -3 Z_OFFSET = 1 CUBE_SIZE = 2 x, y, z = ralph_start_pos x += X_OFFSET y += Y_OFFSET z += Z_OFFSET # make_cube(x, y, z, CUBE_SIZE) def set_key(self, key, value): self.key_map[key] = value def move(self, task): dt = globalClock.getDt() # print "DT: %f" % dt # ****: Movement # Get Ralph's position before we do anything to it start_pos = self.ralph.getPos() # User wants to turn if self.key_map["left"]: self.ralph.setH(self.ralph.getH() + 300 * dt) if self.key_map["right"]: self.ralph.setH(self.ralph.getH() - 300 * dt) # Perform a move forward if self.key_map["forward"]: self.ralph.setY(self.ralph, -25 * dt) # ****: Animation if self.key_map["forward"] or self.key_map["left"] or self.key_map["right"]: if not self.is_moving: self.ralph.loop("run") self.is_moving = True else: if self.is_moving: self.ralph.stop() self.ralph.pose("walk", 5) self.is_moving = False # ****: Camera Movement if self.key_map["cam-left"]: self.camera.setX(self.camera, -20 * dt) elif self.key_map["cam-right"]: self.camera.setX(self.camera, +20 * dt) # If the camera is too far from ralph, move it closer # If the camera is too close to ralph, move it farther camvec = self.ralph.getPos() - self.camera.getPos() camvec.setZ(0) camdist = camvec.length() camvec.normalize() if camdist > 10.0: self.camera.setPos(self.camera.getPos() + camvec * (camdist - 10)) camdist = 10.0 if camdist < 5.0: self.camera.setPos(self.camera.getPos() - camvec * (5 - camdist)) camdist - 5.0 # ****: Collision """ Normally, we would have to call traverse() to check for collisions. However, the class ShowBase that we inherit from has a task to do this for us If we assign a CollisionTraverser to self.cTrav Adjust ralph's Z coordinate. If ralph's ray hit terrain update his Z. If it hit anything else, or didn't hit anything put him back where he was last frame. """ entries = list(self.ralph_ground_handler.getEntries()) entries.sort(key=lambda x: x.getSurfacePoint(render).getZ()) # print "Entry count: %d" % len(entries) if len(entries) > 0 and entries[0].getIntoNode().getName() == "terrain": self.ralph.setZ(entries[0].getSurfacePoint(render).getZ()) else: self.ralph.setPos(start_pos) # Keep the camera at one foot above the terrain or two feet above ralph, whichever is greater entries = list(self.cam_ground_handler.getEntries()) entries.sort(key=lambda x: x.getSurfacePoint(render).getZ()) if len(entries) > 0 and entries[0].getIntoNode().getName() == "terrain": self.camera.setZ(entries[0].getSurfacePoint(render).getZ() + 1.0) if self.camera.getZ() < self.ralph.getZ() + 2.0: self.camera.setZ(self.ralph.getZ() + 2.0) """ The camera should look in rlaphs direction, but it should also try to stay horizontal so look at a floater which hovers above ralph's head """ self.camera.lookAt(self.floater) return task.cont
class Raycaster(Entity): def __init__(self): super().__init__(name='raycaster', eternal=True) self._picker = CollisionTraverser() # Make a traverser self._pq = CollisionHandlerQueue() # Make a handler self._pickerNode = CollisionNode('raycaster') self._pickerNode.set_into_collide_mask(0) self._pickerNP = self.attach_new_node(self._pickerNode) self._picker.addCollider(self._pickerNP, self._pq) self._pickerNP.show() def distance(self, a, b): return math.sqrt(sum((a - b)**2 for a, b in zip(a, b))) def raycast(self, origin, direction=(0, 0, 1), distance=math.inf, traverse_target=scene, ignore=list(), debug=False): self.position = origin self.look_at(self.position + direction) self._pickerNode.clearSolids() # if thickness == (0,0): if distance == math.inf: ray = CollisionRay() ray.setOrigin(Vec3(0, 0, 0)) ray.setDirection(Vec3(0, 1, 0)) else: ray = CollisionSegment(Vec3(0, 0, 0), Vec3(0, distance, 0)) self._pickerNode.addSolid(ray) if debug: self._pickerNP.show() else: self._pickerNP.hide() self._picker.traverse(traverse_target) if self._pq.get_num_entries() == 0: self.hit = Hit(hit=False) return self.hit ignore += tuple([e for e in scene.entities if not e.collision]) self._pq.sort_entries() self.entries = [ # filter out ignored entities e for e in self._pq.getEntries() if e.get_into_node_path().parent not in ignore ] if len(self.entries) == 0: self.hit = Hit(hit=False) return self.hit self.collision = self.entries[0] nP = self.collision.get_into_node_path().parent point = self.collision.get_surface_point(nP) point = Vec3(point[0], point[2], point[1]) world_point = self.collision.get_surface_point(render) world_point = Vec3(world_point[0], world_point[2], world_point[1]) hit_dist = self.distance(self.world_position, world_point) if nP.name.endswith('.egg'): nP = nP.parent self.hit = Hit(hit=True) for e in scene.entities: if e == nP: # print('cast nP to Entity') self.hit.entity = e self.hit.point = point self.hit.world_point = world_point self.hit.distance = hit_dist normal = self.collision.get_surface_normal( self.collision.get_into_node_path().parent) self.hit.normal = (normal[0], normal[2], normal[1]) normal = self.collision.get_surface_normal(render) self.hit.world_normal = (normal[0], normal[2], normal[1]) return self.hit self.hit = Hit(hit=False) return self.hit def boxcast(self, origin, direction=(0, 0, 1), distance=math.inf, thickness=(1, 1), traverse_target=scene, ignore=list(), debug=False): if isinstance(thickness, (int, float, complex)): thickness = (thickness, thickness) resolution = 3 rays = list() debugs = list() for y in range(3): for x in range(3): pos = origin + Vec3( lerp(-(thickness[0] / 2), thickness[0] / 2, x / (3 - 1)), lerp(-(thickness[1] / 2), thickness[1] / 2, y / (3 - 1)), 0) ray = self.raycast(pos, direction, distance, traverse_target, ignore, False) rays.append(ray) if debug and ray.hit: d = Entity(model='cube', origin_z=-.5, position=pos, scale=(.02, .02, distance), ignore=True) d.look_at(pos + Vec3(direction)) debugs.append(d) # print(pos, hit.point) if ray.hit and ray.distance > 0: d.scale_z = ray.distance d.color = color.green from ursina import destroy # [destroy(e, 1/60) for e in debugs] rays.sort(key=lambda x: x.distance) closest = rays[0] return Hit( hit=sum([int(e.hit) for e in rays]) > 0, entity=closest.entity, point=closest.point, world_point=closest.world_point, distance=closest.distance, normal=closest.normal, world_normal=closest.world_normal, hits=[e.hit for e in rays], entities=list(set([e.entity for e in rays])), # get unique entities hit )
class RoamingRalphDemo(ShowBase): def __init__(self): # Set up the window, camera, etc. ShowBase.__init__(self) # Set the background color to black self.win.setClearColor((0, 0, 0, 1)) # This is used to store which keys are currently pressed. self.keyMap = { "left": 0, "right": 0, "forward": 0, "cam-left": 0, "cam-right": 0} # Post the instructions self.title = addTitle( "Panda3D Tutorial: Roaming Ralph (Walking on Uneven Terrain)") self.inst1 = addInstructions(0.06, "[ESC]: Quit") self.inst2 = addInstructions(0.12, "[Left Arrow]: Rotate Ralph Left") self.inst3 = addInstructions(0.18, "[Right Arrow]: Rotate Ralph Right") self.inst4 = addInstructions(0.24, "[Up Arrow]: Run Ralph Forward") self.inst6 = addInstructions(0.30, "[A]: Rotate Camera Left") self.inst7 = addInstructions(0.36, "[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/world") self.environ.reparentTo(render) # Create the main character, Ralph ralphStartPos = self.environ.find("**/start_point").getPos() self.ralph = Actor("models/ralph", {"run": "models/ralph-run", "walk": "models/ralph-walk"}) self.ralph.reparentTo(render) self.ralph.setScale(.2) self.ralph.setPos(ralphStartPos + (0, 0, 0.5)) # Create a floater object, which floats 2 units above ralph. We # use this as a target for the camera to look at. self.floater = NodePath(PandaNode("floater")) self.floater.reparentTo(self.ralph) self.floater.setZ(2.0) # Accept the control keys for movement and rotation self.accept("escape", sys.exit) self.accept("arrow_left", self.setKey, ["left", True]) self.accept("arrow_right", self.setKey, ["right", True]) self.accept("arrow_up", self.setKey, ["forward", True]) self.accept("a", self.setKey, ["cam-left", True]) self.accept("s", self.setKey, ["cam-right", True]) self.accept("arrow_left-up", self.setKey, ["left", False]) self.accept("arrow_right-up", self.setKey, ["right", False]) self.accept("arrow_up-up", self.setKey, ["forward", False]) self.accept("a-up", self.setKey, ["cam-left", False]) self.accept("s-up", self.setKey, ["cam-right", False]) taskMgr.add(self.move, "moveTask") # Game state variables self.isMoving = False # Set up the camera self.disableMouse() self.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, 9) self.ralphGroundRay.setDirection(0, 0, -1) self.ralphGroundCol = CollisionNode('ralphRay') self.ralphGroundCol.addSolid(self.ralphGroundRay) self.ralphGroundCol.setFromCollideMask(CollideMask.bit(0)) self.ralphGroundCol.setIntoCollideMask(CollideMask.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, 9) self.camGroundRay.setDirection(0, 0, -1) self.camGroundCol = CollisionNode('camRay') self.camGroundCol.addSolid(self.camGroundRay) self.camGroundCol.setFromCollideMask(CollideMask.bit(0)) self.camGroundCol.setIntoCollideMask(CollideMask.allOff()) self.camGroundColNp = self.camera.attachNewNode(self.camGroundCol) self.camGroundHandler = CollisionHandlerQueue() self.cTrav.addCollider(self.camGroundColNp, self.camGroundHandler) # Uncomment this line to see the collision rays #self.ralphGroundColNp.show() #self.camGroundColNp.show() # Uncomment this line to show a visual representation of the # collisions occuring #self.cTrav.showCollisions(render) # Create some lighting ambientLight = AmbientLight("ambientLight") ambientLight.setColor((.3, .3, .3, 1)) directionalLight = DirectionalLight("directionalLight") directionalLight.setDirection((-5, -5, -5)) directionalLight.setColor((1, 1, 1, 1)) directionalLight.setSpecularColor((1, 1, 1, 1)) render.setLight(render.attachNewNode(ambientLight)) render.setLight(render.attachNewNode(directionalLight)) # Records the state of the arrow keys def setKey(self, key, value): self.keyMap[key] = value # Accepts arrow keys to move either the player or the menu cursor, # Also deals with grid checking and collision detection def move(self, task): # Get the time that elapsed since last frame. We multiply this with # the desired speed in order to find out with which distance to move # in order to achieve that desired speed. dt = globalClock.getDt() # If the camera-left key is pressed, move camera left. # If the camera-right key is pressed, move camera right. if self.keyMap["cam-left"]: self.camera.setX(self.camera, -20 * dt) if self.keyMap["cam-right"]: self.camera.setX(self.camera, +20 * dt) # save ralph's initial position so that we can restore it, # in case he falls off the map or runs into something. startpos = self.ralph.getPos() # If a move-key is pressed, move ralph in the specified direction. if self.keyMap["left"]: self.ralph.setH(self.ralph.getH() + 300 * dt) if self.keyMap["right"]: self.ralph.setH(self.ralph.getH() - 300 * dt) if self.keyMap["forward"]: self.ralph.setY(self.ralph, -25 * dt) # If ralph is moving, loop the run animation. # If he is standing still, stop the animation. if self.keyMap["forward"] or self.keyMap["left"] or self.keyMap["right"]: if self.isMoving is False: self.ralph.loop("run") self.isMoving = True else: if self.isMoving: self.ralph.stop() self.ralph.pose("walk", 5) self.isMoving = False # If the camera is too far from ralph, move it closer. # If the camera is too close to ralph, move it farther. camvec = self.ralph.getPos() - self.camera.getPos() camvec.setZ(0) camdist = camvec.length() camvec.normalize() if camdist > 10.0: self.camera.setPos(self.camera.getPos() + camvec * (camdist - 10)) camdist = 10.0 if camdist < 5.0: self.camera.setPos(self.camera.getPos() - camvec * (5 - camdist)) camdist = 5.0 # Normally, we would have to call traverse() to check for collisions. # However, the class ShowBase that we inherit from has a task to do # this for us, if we assign a CollisionTraverser to self.cTrav. #self.cTrav.traverse(render) # Adjust ralph's Z coordinate. If ralph's ray hit terrain, # update his Z. If it hit anything else, or didn't hit anything, put # him back where he was last frame. entries = list(self.ralphGroundHandler.getEntries()) entries.sort(key=lambda x: x.getSurfacePoint(render).getZ()) if len(entries) > 0 and entries[0].getIntoNode().getName() == "terrain": self.ralph.setZ(entries[0].getSurfacePoint(render).getZ()) else: self.ralph.setPos(startpos) # Keep the camera at one foot above the terrain, # or two feet above ralph, whichever is greater. entries = list(self.camGroundHandler.getEntries()) entries.sort(key=lambda x: x.getSurfacePoint(render).getZ()) if len(entries) > 0 and entries[0].getIntoNode().getName() == "terrain": self.camera.setZ(entries[0].getSurfacePoint(render).getZ() + 1.0) if self.camera.getZ() < self.ralph.getZ() + 2.0: self.camera.setZ(self.ralph.getZ() + 2.0) # The camera should look in ralph's direction, # but it should also try to stay horizontal, so look at # a floater which hovers above ralph's head. self.camera.lookAt(self.floater) return task.cont
class Demo(ShowBase): _speed = 25 def reset(self): x = random.random() * 40 - 20 y = random.random() * 40 - 20 self.ralph.setPos((x, y, 0)) def __init__(self, img_size=512, screen_off=True): self.img_size = img_size loadPrcFileData("", "transform-cache false") loadPrcFileData("", "audio-library-name null") # Prevent ALSA errors loadPrcFileData("", "win-size %d %d" % (img_size, img_size)) loadPrcFileData("", "parallax-mapping-samples 3\n" "parallax-mapping-scale 0.1") if screen_off: # Spawn an offscreen buffer loadPrcFileData("", "window-type offscreen") # Initialize the ShowBase class from which we inherit, which will # create a window and set up everything we need for rendering into it. ShowBase.__init__(self) # Load the 'abstract room' model. This is a model of an # empty room containing a pillar, a pyramid, and a bunch # of exaggeratedly bumpy textures. self.room = loader.loadModel("models/abstractroom") self.room.reparentTo(render) # Create the main character, Ralph self.ralph = Actor("models/ralph", {"run": "models/ralph-run", "walk": "models/ralph-walk"}) self.ralph.reparentTo(render) self.ralph.setScale(.2) self.reset() self.pieces = [Piece(self.room) for _ in range(200)] ################################################## cnodePath = self.room.attachNewNode(CollisionNode('cnode')) plane = CollisionPlane(Plane(Vec3(1, 0, 0), Point3(-60, 0, 0))) # left cnodePath.node().addSolid(plane) plane = CollisionPlane( Plane(Vec3(-1, 0, 0), Point3(60, 0, 0))) # right cnodePath.node().addSolid(plane) plane = CollisionPlane(Plane(Vec3(0, 1, 0), Point3(0, -60, 0))) # back cnodePath.node().addSolid(plane) plane = CollisionPlane( Plane(Vec3(0, -1, 0), Point3(0, 60, 0))) # front cnodePath.node().addSolid(plane) sphere = CollisionSphere(-25, -25, 0, 12.5) cnodePath.node().addSolid(sphere) box = CollisionBox(Point3(5, 5, 0), Point3(45, 45, 10)) cnodePath.node().addSolid(box) # Make the mouse invisible, turn off normal mouse controls self.disableMouse() self.camLens.setFov(80) # Set the current viewing target self.focus = LVector3(55, -55, 20) self.heading = 180 self.pitch = 0 self.mousex = 0 self.mousey = 0 self.last = 0 self.mousebtn = [0, 0, 0] # Add a light to the scene. self.lightpivot = render.attachNewNode("lightpivot") self.lightpivot.setPos(0, 0, 25) self.lightpivot.hprInterval(10, LPoint3(360, 0, 0)).loop() plight = PointLight('plight') plight.setColor((5, 5, 5, 1)) plight.setAttenuation(LVector3(0.7, 0.05, 0)) plnp = self.lightpivot.attachNewNode(plight) plnp.setPos(45, 0, 0) self.room.setLight(plnp) # Add an ambient light alight = AmbientLight('alight') alight.setColor((0.2, 0.2, 0.2, 1)) alnp = render.attachNewNode(alight) self.room.setLight(alnp) # Create a sphere to denote the light sphere = loader.loadModel("models/icosphere") sphere.reparentTo(plnp) self.cameraModel = self.ralph camera.reparentTo(self.cameraModel) camera.setZ(2) self.cTrav = CollisionTraverser() cs = CollisionSphere(0, 0, 0, 1) cnodePath = self.ralph.attachNewNode(CollisionNode('cnode')) cnodePath.node().addSolid(cs) cnodePath.show() self.ralphGroundHandler = CollisionHandlerQueue() self.cTrav.addCollider(cnodePath, self.ralphGroundHandler) self.cnodePath = cnodePath # Tell Panda that it should generate shaders performing per-pixel # lighting for the room. self.room.setShaderAuto() self.shaderenable = 1 tex = Texture() self.depthmap = tex tex.setFormat(Texture.FDepthComponent) altBuffer = self.win.makeTextureBuffer( "hello", img_size, img_size, tex, True) self.altBuffer = altBuffer altCam = self.makeCamera(altBuffer) altCam.reparentTo(self.ralph) # altRender) altCam.setZ(2) l = altCam.node().getLens() l.setFov(80) ################### end init def step(self, rotation): dt = 0.02 self.ralph.setH(self.ralph.getH() + rotation * dt) self.ralph.setY(self.ralph, self._speed * dt) for _ in range(5): random.choice(self.pieces).step(dt) f = taskMgr.getAllTasks()[-1].getFunction() f(None) f = taskMgr.getAllTasks()[2].getFunction() f(None) entries = (self.ralphGroundHandler.getEntries()) if len(entries) > 0: self.reset() return 1 else: return 0 def resetGame(self): while True: self.reset() for p in self.pieces: p.reset() if 0 == self.step(0): break def renderFrame(self): base.graphicsEngine.renderFrame() def getDepthMapT(self): tex = self.depthmap #tex = self.altBuffer.getTexture() r = tex.getRamImage() s = r.getData() i = np.fromstring(s, dtype='float32') i = i.reshape((self.img_size, self.img_size)) i = np.flipud(i) return i
class RoamingRalphDemo(ShowBase): def __init__(self): # Set up the window, camera, etc. ShowBase.__init__(self) # Set the background color to black self.win.setClearColor((0, 0, 0, 1)) # This is used to store which keys are currently pressed. self.keyMap = { "left": 0, "right": 0, "forward": 0, "cam-left": 0, "cam-right": 0 } # Post the instructions self.title = addTitle( "Panda3D Tutorial: Roaming Ralph (Walking on Uneven Terrain)") self.inst1 = addInstructions(0.06, "[ESC]: Quit") self.inst2 = addInstructions(0.12, "[Left Arrow]: Rotate Ralph Left") self.inst3 = addInstructions(0.18, "[Right Arrow]: Rotate Ralph Right") self.inst4 = addInstructions(0.24, "[Up Arrow]: Run Ralph Forward") self.inst6 = addInstructions(0.30, "[A]: Rotate Camera Left") self.inst7 = addInstructions(0.36, "[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/world") self.environ.reparentTo(render) # Create the main character, Ralph ralphStartPos = self.environ.find("**/start_point").getPos() self.ralph = Actor("models/ralph", { "run": "models/ralph-run", "walk": "models/ralph-walk" }) self.ralph.reparentTo(render) self.ralph.setScale(.2) self.ralph.setPos(ralphStartPos + (0, 0, 0.5)) # Create a floater object, which floats 2 units above ralph. We # use this as a target for the camera to look at. self.floater = NodePath(PandaNode("floater")) self.floater.reparentTo(self.ralph) self.floater.setZ(2.0) # Accept the control keys for movement and rotation self.accept("escape", sys.exit) self.accept("arrow_left", self.setKey, ["left", True]) self.accept("arrow_right", self.setKey, ["right", True]) self.accept("arrow_up", self.setKey, ["forward", True]) self.accept("a", self.setKey, ["cam-left", True]) self.accept("s", self.setKey, ["cam-right", True]) self.accept("arrow_left-up", self.setKey, ["left", False]) self.accept("arrow_right-up", self.setKey, ["right", False]) self.accept("arrow_up-up", self.setKey, ["forward", False]) self.accept("a-up", self.setKey, ["cam-left", False]) self.accept("s-up", self.setKey, ["cam-right", False]) taskMgr.add(self.move, "moveTask") # Game state variables self.isMoving = False # Set up the camera self.disableMouse() self.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, 9) self.ralphGroundRay.setDirection(0, 0, -1) self.ralphGroundCol = CollisionNode('ralphRay') self.ralphGroundCol.addSolid(self.ralphGroundRay) self.ralphGroundCol.setFromCollideMask(CollideMask.bit(0)) self.ralphGroundCol.setIntoCollideMask(CollideMask.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, 9) self.camGroundRay.setDirection(0, 0, -1) self.camGroundCol = CollisionNode('camRay') self.camGroundCol.addSolid(self.camGroundRay) self.camGroundCol.setFromCollideMask(CollideMask.bit(0)) self.camGroundCol.setIntoCollideMask(CollideMask.allOff()) self.camGroundColNp = self.camera.attachNewNode(self.camGroundCol) self.camGroundHandler = CollisionHandlerQueue() self.cTrav.addCollider(self.camGroundColNp, self.camGroundHandler) # Uncomment this line to see the collision rays #self.ralphGroundColNp.show() #self.camGroundColNp.show() # Uncomment this line to show a visual representation of the # collisions occuring #self.cTrav.showCollisions(render) # Create some lighting ambientLight = AmbientLight("ambientLight") ambientLight.setColor((.3, .3, .3, 1)) directionalLight = DirectionalLight("directionalLight") directionalLight.setDirection((-5, -5, -5)) directionalLight.setColor((1, 1, 1, 1)) directionalLight.setSpecularColor((1, 1, 1, 1)) render.setLight(render.attachNewNode(ambientLight)) render.setLight(render.attachNewNode(directionalLight)) # Records the state of the arrow keys def setKey(self, key, value): self.keyMap[key] = value # Accepts arrow keys to move either the player or the menu cursor, # Also deals with grid checking and collision detection def move(self, task): # Get the time that elapsed since last frame. We multiply this with # the desired speed in order to find out with which distance to move # in order to achieve that desired speed. dt = globalClock.getDt() # If the camera-left key is pressed, move camera left. # If the camera-right key is pressed, move camera right. if self.keyMap["cam-left"]: self.camera.setX(self.camera, -20 * dt) if self.keyMap["cam-right"]: self.camera.setX(self.camera, +20 * dt) # save ralph's initial position so that we can restore it, # in case he falls off the map or runs into something. startpos = self.ralph.getPos() # If a move-key is pressed, move ralph in the specified direction. if self.keyMap["left"]: self.ralph.setH(self.ralph.getH() + 300 * dt) if self.keyMap["right"]: self.ralph.setH(self.ralph.getH() - 300 * dt) if self.keyMap["forward"]: self.ralph.setY(self.ralph, -25 * dt) # If ralph is moving, loop the run animation. # If he is standing still, stop the animation. if self.keyMap["forward"] or self.keyMap["left"] or self.keyMap[ "right"]: if self.isMoving is False: self.ralph.loop("run") self.isMoving = True else: if self.isMoving: self.ralph.stop() self.ralph.pose("walk", 5) self.isMoving = False # If the camera is too far from ralph, move it closer. # If the camera is too close to ralph, move it farther. camvec = self.ralph.getPos() - self.camera.getPos() camvec.setZ(0) camdist = camvec.length() camvec.normalize() if camdist > 10.0: self.camera.setPos(self.camera.getPos() + camvec * (camdist - 10)) camdist = 10.0 if camdist < 5.0: self.camera.setPos(self.camera.getPos() - camvec * (5 - camdist)) camdist = 5.0 # Normally, we would have to call traverse() to check for collisions. # However, the class ShowBase that we inherit from has a task to do # this for us, if we assign a CollisionTraverser to self.cTrav. #self.cTrav.traverse(render) # Adjust ralph's Z coordinate. If ralph's ray hit terrain, # update his Z. If it hit anything else, or didn't hit anything, put # him back where he was last frame. entries = list(self.ralphGroundHandler.getEntries()) entries.sort(key=lambda x: x.getSurfacePoint(render).getZ()) if len(entries) > 0 and entries[0].getIntoNode().getName( ) == "terrain": self.ralph.setZ(entries[0].getSurfacePoint(render).getZ()) else: self.ralph.setPos(startpos) # Keep the camera at one foot above the terrain, # or two feet above ralph, whichever is greater. entries = list(self.camGroundHandler.getEntries()) entries.sort(key=lambda x: x.getSurfacePoint(render).getZ()) if len(entries) > 0 and entries[0].getIntoNode().getName( ) == "terrain": self.camera.setZ(entries[0].getSurfacePoint(render).getZ() + 1.0) if self.camera.getZ() < self.ralph.getZ() + 2.0: self.camera.setZ(self.ralph.getZ() + 2.0) # The camera should look in ralph's direction, # but it should also try to stay horizontal, so look at # a floater which hovers above ralph's head. self.camera.lookAt(self.floater) return task.cont
class RoamingRalphDemo(CosmoniumBase): def get_local_position(self): return base.camera.get_pos() def create_terrain_appearance(self): self.terrain_appearance.set_shadow(self.shadow_caster) def create_terrain_heightmap(self): self.heightmap = PatchedHeightmap('heightmap', self.noise_size, self.height_scale, self.size, self.size, True, ShaderHeightmapPatchFactory(self.noise)) def create_terrain_biome(self): self.biome = PatchedHeightmap('biome', self.biome_size, 1.0, self.size, self.size, False, ShaderHeightmapPatchFactory(self.biome_noise)) def create_terrain_shader(self): # control4 = HeightColorMap('colormap', # [ # ColormapLayer(0.00, top=LRGBColor(0, 0.1, 0.24)), # ColormapLayer(0.40, top=LRGBColor(0, 0.1, 0.24)), # ColormapLayer(0.49, top=LRGBColor(0, 0.6, 0.6)), # ColormapLayer(0.50, bottom=LRGBColor(0.9, 0.8, 0.6), top=LRGBColor(0.5, 0.4, 0.3)), # ColormapLayer(0.80, top=LRGBColor(0.2, 0.3, 0.1)), # ColormapLayer(0.90, top=LRGBColor(0.7, 0.6, 0.4)), # ColormapLayer(1.00, bottom=LRGBColor(1, 1, 1), top=LRGBColor(1, 1, 1)), # ]) appearance = DetailMap(self.terrain_control, self.heightmap, create_normals=True) data_source = [HeightmapDataSource(self.heightmap, PatchedGpuTextureSource, filtering=HeightmapDataSource.F_none), HeightmapDataSource(self.biome, PatchedGpuTextureSource, filtering=HeightmapDataSource.F_none), TextureDictionaryDataSource(self.terrain_appearance, TextureDictionaryDataSource.F_hash)] if settings.allow_tesselation: tesselation_control = ConstantTesselationControl(invert_v=False) else: tesselation_control = None if self.fog is not None: after_effects = [Fog(**self.fog)] else: after_effects = None self.terrain_shader = BasicShader(appearance=appearance, tesselation_control=tesselation_control, geometry_control=DisplacementGeometryControl(self.heightmap), data_source=data_source, after_effects=after_effects) def create_tile(self, x, y): self.terrain_shape.add_root_patch(x, y) def create_terrain(self): self.tile_factory = TileFactory(self.tile_density, self.size, self.has_water, self.water) self.terrain_shape = TiledShape(self.tile_factory, self.size, self.max_lod, lod_control=VertexSizeMaxDistancePatchLodControl(self.max_distance, self.max_vertex_size)) self.create_terrain_heightmap() self.create_terrain_biome() self.create_terrain_appearance() self.create_terrain_shader() self.terrain = HeightmapSurface( 'surface', 0, self.terrain_shape, self.heightmap, self.biome, self.terrain_appearance, self.terrain_shader, self.size, clickable=False, average=True) self.terrain.set_parent(self) self.terrain.create_instance() def toggle_water(self): if not self.has_water: return self.water.visible = not self.water.visible self.terrain_shape.check_settings() def get_height(self, position): height = self.terrain.get_height(position) if self.has_water and self.water.visible and height < self.water.level: height = self.water.level return height #Used by populator def get_height_patch(self, patch, u, v): height = self.terrain.get_height_patch(patch, u, v) if self.has_water and self.water.visible and height < self.water.level: height = self.water.level return height def skybox_init(self): skynode = base.cam.attachNewNode('skybox') self.skybox = loader.loadModel('ralph-data/models/rgbCube') self.skybox.reparentTo(skynode) self.skybox.setTextureOff(1) self.skybox.setShaderOff(1) self.skybox.setTwoSided(True) # make big enough to cover whole terrain, else there'll be problems with the water reflections self.skybox.setScale(1.5* self.size) self.skybox.setBin('background', 1) self.skybox.setDepthWrite(False) self.skybox.setDepthTest(False) self.skybox.setLightOff(1) self.skybox.setShaderOff(1) self.skybox.setFogOff(1) #self.skybox.setColor(.55, .65, .95, 1.0) self.skybox_color = LColor(pow(0.5, 1/2.2), pow(0.6, 1/2.2), pow(0.7, 1/2.2), 1.0) self.skybox.setColor(self.skybox_color) def objects_density_for_patch(self, patch): scale = 1 << patch.lod return int(self.objects_density / scale + 1.0) def create_populator(self): if settings.allow_instancing: TerrainPopulator = GpuTerrainPopulator else: TerrainPopulator = CpuTerrainPopulator self.rock_collection = TerrainPopulator(RockFactory(self), self.objects_density_for_patch, self.objects_density, RandomObjectPlacer(self)) self.tree_collection = TerrainPopulator(TreeFactory(self), self.objects_density_for_patch, self.objects_density, RandomObjectPlacer(self)) self.object_collection = MultiTerrainPopulator() self.object_collection.add_populator(self.rock_collection) self.object_collection.add_populator(self.tree_collection) def set_light_angle(self, angle): self.light_angle = angle self.light_quat.setFromAxisAngleRad(angle * pi / 180, LVector3.forward()) self.light_dir = self.light_quat.xform(LVector3.up()) cosA = self.light_dir.dot(LVector3.up()) self.vector_to_star = self.light_dir if self.shadow_caster is not None: self.shadow_caster.set_direction(-self.light_dir) if self.directionalLight is not None: self.directionalLight.setDirection(-self.light_dir) if cosA >= 0: coef = sqrt(cosA) self.light_color = (1, coef, coef, 1) self.directionalLight.setColor(self.light_color) self.skybox.setColor(self.skybox_color * cosA) else: self.light_color = (1, 0, 0, 1) self.directionalLight.setColor(self.light_color) self.skybox.setColor(self.skybox_color * 0) self.update() def update(self): self.object_collection.update_instance() self.terrain.update_instance(None, None) def apply_instance(self, instance): pass def create_instance_delayed(self): pass def get_apparent_radius(self): return 0 def get_name(self): return "terrain" def is_emissive(self): return False def __init__(self): CosmoniumBase.__init__(self) config = RalphConfigParser() (self.noise, self.biome_noise, self.terrain_control, self.terrain_appearance, self.water, self.fog) = config.load_and_parse('ralph-data/ralph.yaml') self.tile_density = 64 self.default_size = 128 self.max_vertex_size = 64 self.max_lod = 10 self.size = 128 * 8 self.max_distance = 1.001 * self.size * sqrt(2) self.noise_size = 512 self.biome_size = 128 self.noise_scale = 0.5 * self.size / self.default_size self.objects_density = int(25 * (1.0 * self.size / self.default_size) * (1.0 * self.size / self.default_size)) self.objects_density = 250 self.height_scale = 100 * 5.0 self.has_water = True self.fullscreen = False self.shadow_caster = None self.light_angle = None self.light_dir = LVector3.up() self.vector_to_star = self.light_dir self.light_quat = LQuaternion() self.light_color = (1.0, 1.0, 1.0, 1.0) self.directionalLight = None self.shadow_size = self.default_size / 8 self.shadow_box_length = self.height_scale self.observer = RalphCamera(self.cam, self.camLens) self.observer.init() self.distance_to_obs = float('inf') self.height_under = 0.0 self.scene_position = LVector3() self.scene_scale_factor = 1 self.scene_orientation = LQuaternion() #Size of an edge seen from 4 units above self.edge_apparent_size = (1.0 * self.size / self.tile_density) / (4.0 * self.observer.pixel_size) print("Apparent size:", self.edge_apparent_size) self.win.setClearColor((135.0/255, 206.0/255, 235.0/255, 1)) # This is used to store which keys are currently pressed. self.keyMap = { "left": 0, "right": 0, "forward": 0, "backward": 0, "cam-left": 0, "cam-right": 0, "cam-up": 0, "cam-down": 0, "sun-left": 0, "sun-right": 0, "turbo": 0} # Set up the environment # # Create some lighting self.vector_to_obs = base.cam.get_pos() self.vector_to_obs.normalize() if True: self.shadow_caster = ShadowCaster(1024) self.shadow_caster.create() self.shadow_caster.set_lens(self.shadow_size, -self.shadow_box_length / 2.0, self.shadow_box_length / 2.0, -self.light_dir) self.shadow_caster.set_pos(self.light_dir * self.shadow_box_length / 2.0) self.shadow_caster.bias = 0.1 else: self.shadow_caster = None self.ambientLight = AmbientLight("ambientLight") self.ambientLight.setColor((settings.global_ambient, settings.global_ambient, settings.global_ambient, 1)) self.directionalLight = DirectionalLight("directionalLight") self.directionalLight.setDirection(-self.light_dir) self.directionalLight.setColor(self.light_color) self.directionalLight.setSpecularColor(self.light_color) render.setLight(render.attachNewNode(self.ambientLight)) render.setLight(render.attachNewNode(self.directionalLight)) render.setShaderAuto() base.setFrameRateMeter(True) self.create_terrain() self.create_populator() self.terrain_shape.set_populator(self.object_collection) self.create_tile(0, 0) self.skybox_init() self.set_light_angle(45) # Create the main character, Ralph ralphStartPos = LPoint3() self.ralph = Actor("ralph-data/models/ralph", {"run": "ralph-data/models/ralph-run", "walk": "ralph-data/models/ralph-walk"}) self.ralph.reparentTo(render) self.ralph.setScale(.2) self.ralph.setPos(ralphStartPos + (0, 0, 0.5)) self.ralph_shape = InstanceShape(self.ralph) self.ralph_shape.parent = self self.ralph_shape.set_owner(self) self.ralph_shape.create_instance() self.ralph_appearance = ModelAppearance(self.ralph) self.ralph_appearance.set_shadow(self.shadow_caster) self.ralph_shader = BasicShader() self.ralph_appearance.bake() self.ralph_appearance.apply(self.ralph_shape, self.ralph_shader) self.ralph_shader.apply(self.ralph_shape, self.ralph_appearance) self.ralph_shader.update(self.ralph_shape, self.ralph_appearance) # Create a floater object, which floats 2 units above ralph. We # use this as a target for the camera to look at. self.floater = NodePath(PandaNode("floater")) self.floater.reparentTo(self.ralph) self.floater.setZ(2.0) # Accept the control keys for movement and rotation self.accept("escape", sys.exit) self.accept("control-q", sys.exit) self.accept("arrow_left", self.setKey, ["left", True]) self.accept("arrow_right", self.setKey, ["right", True]) self.accept("arrow_up", self.setKey, ["forward", True]) self.accept("arrow_down", self.setKey, ["backward", True]) self.accept("shift", self.setKey, ["turbo", True]) self.accept("a", self.setKey, ["cam-left", True], direct=True) self.accept("s", self.setKey, ["cam-right", True], direct=True) self.accept("u", self.setKey, ["cam-up", True], direct=True) self.accept("u-up", self.setKey, ["cam-up", False]) self.accept("d", self.setKey, ["cam-down", True], direct=True) self.accept("d-up", self.setKey, ["cam-down", False]) self.accept("o", self.setKey, ["sun-left", True], direct=True) self.accept("o-up", self.setKey, ["sun-left", False]) self.accept("p", self.setKey, ["sun-right", True], direct=True) self.accept("p-up", self.setKey, ["sun-right", False]) self.accept("arrow_left-up", self.setKey, ["left", False]) self.accept("arrow_right-up", self.setKey, ["right", False]) self.accept("arrow_up-up", self.setKey, ["forward", False]) self.accept("arrow_down-up", self.setKey, ["backward", False]) self.accept("shift-up", self.setKey, ["turbo", False]) self.accept("a-up", self.setKey, ["cam-left", False]) self.accept("s-up", self.setKey, ["cam-right", False]) self.accept("w", self.toggle_water) self.accept("h", self.print_debug) self.accept("f2", self.connect_pstats) self.accept("f3", self.toggle_filled_wireframe) self.accept("shift-f3", self.toggle_wireframe) self.accept("f5", self.bufferViewer.toggleEnable) self.accept("f8", self.terrain_shape.dump_tree) self.accept('alt-enter', self.toggle_fullscreen) taskMgr.add(self.move, "moveTask") # Game state variables self.isMoving = False # Set up the camera self.camera.setPos(self.ralph.getX(), self.ralph.getY() + 10, 2) self.camera_height = 2.0 render.set_shader_input("camera", self.camera.get_pos()) self.cTrav = CollisionTraverser() self.ralphGroundRay = CollisionRay() self.ralphGroundRay.setOrigin(0, 0, 9) self.ralphGroundRay.setDirection(0, 0, -1) self.ralphGroundCol = CollisionNode('ralphRay') self.ralphGroundCol.addSolid(self.ralphGroundRay) self.ralphGroundCol.setFromCollideMask(CollideMask.bit(0)) self.ralphGroundCol.setIntoCollideMask(CollideMask.allOff()) self.ralphGroundColNp = self.ralph.attachNewNode(self.ralphGroundCol) self.ralphGroundHandler = CollisionHandlerQueue() self.cTrav.addCollider(self.ralphGroundColNp, self.ralphGroundHandler) # Uncomment this line to see the collision rays #self.ralphGroundColNp.show() # Uncomment this line to show a visual representation of the # collisions occuring #self.cTrav.showCollisions(render) #self.terrain_shape.test_lod(LPoint3d(*self.ralph.getPos()), self.distance_to_obs, self.pixel_size, self.terrain_appearance) #self.terrain_shape.update_lod(LPoint3d(*self.ralph.getPos()), self.distance_to_obs, self.pixel_size, self.terrain_appearance) #self.terrain.shape_updated() self.terrain.update_instance(LPoint3d(*self.ralph.getPos()), None) # Records the state of the arrow keys def setKey(self, key, value): self.keyMap[key] = value # Accepts arrow keys to move either the player or the menu cursor, # Also deals with grid checking and collision detection def move(self, task): # Get the time that elapsed since last frame. We multiply this with # the desired speed in order to find out with which distance to move # in order to achieve that desired speed. dt = globalClock.getDt() # If the camera-left key is pressed, move camera left. # If the camera-right key is pressed, move camera right. if self.keyMap["cam-left"]: self.camera.setX(self.camera, -20 * dt) if self.keyMap["cam-right"]: self.camera.setX(self.camera, +20 * dt) if self.keyMap["cam-up"]: self.camera_height *= (1 + 2 * dt) if self.keyMap["cam-down"]: self.camera_height *= (1 - 2 * dt) if self.camera_height < 1.0: self.camera_height = 1.0 if self.keyMap["sun-left"]: self.set_light_angle(self.light_angle + 30 * dt) if self.keyMap["sun-right"]: self.set_light_angle(self.light_angle - 30 * dt) # save ralph's initial position so that we can restore it, # in case he falls off the map or runs into something. startpos = self.ralph.getPos() # If a move-key is pressed, move ralph in the specified direction. delta = 25 if self.keyMap["turbo"]: delta *= 10 if self.keyMap["left"]: self.ralph.setH(self.ralph.getH() + 300 * dt) if self.keyMap["right"]: self.ralph.setH(self.ralph.getH() - 300 * dt) if self.keyMap["forward"]: self.ralph.setY(self.ralph, -delta * dt) if self.keyMap["backward"]: self.ralph.setY(self.ralph, delta * dt) #self.limit_pos(self.ralph) # If ralph is moving, loop the run animation. # If he is standing still, stop the animation. if self.keyMap["forward"] or self.keyMap["backward"] or self.keyMap["left"] or self.keyMap["right"]: if self.isMoving is False: self.ralph.loop("run") self.isMoving = True else: if self.isMoving: self.ralph.stop() self.ralph.pose("walk", 5) self.isMoving = False # If the camera is too far from ralph, move it closer. # If the camera is too close to ralph, move it farther. camvec = self.ralph.getPos() - self.camera.getPos() camvec.setZ(0) camdist = camvec.length() camvec.normalize() if camdist > 10.0: self.camera.setPos(self.camera.getPos() + camvec * (camdist - 10)) camdist = 10.0 if camdist < 5.0: self.camera.setPos(self.camera.getPos() - camvec * (5 - camdist)) camdist = 5.0 # Normally, we would have to call traverse() to check for collisions. # However, the class ShowBase that we inherit from has a task to do # this for us, if we assign a CollisionTraverser to self.cTrav. self.cTrav.traverse(render) if False: # Adjust ralph's Z coordinate. If ralph's ray hit anything, put # him back where he was last frame. entries = list(self.ralphGroundHandler.getEntries()) entries.sort(key=lambda x: x.getSurfacePoint(render).getZ()) if len(entries) > 0: self.ralph.setPos(startpos) ralph_height = self.get_height(self.ralph.getPos()) self.ralph.setZ(ralph_height) # Keep the camera at one foot above the terrain, # or two feet above ralph, whichever is greater. camera_height = self.get_height(self.camera.getPos()) + 1.0 if camera_height < ralph_height + self.camera_height: self.camera.setZ(ralph_height + self.camera_height) else: self.camera.setZ(camera_height) #self.limit_pos(self.camera) # The camera should look in ralph's direction, # but it should also try to stay horizontal, so look at # a floater which hovers above ralph's head. self.camera.lookAt(self.floater) #self.shadow_caster.set_pos(self.ralph.get_pos()) self.shadow_caster.set_pos(self.ralph.get_pos() - camvec * camdist + camvec * self.shadow_size / 2) render.set_shader_input("camera", self.camera.get_pos()) self.vector_to_obs = base.cam.get_pos() self.vector_to_obs.normalize() if self.isMoving: #self.terrain_shape.test_lod(LPoint3d(*self.ralph.getPos()), self.distance_to_obs, self.pixel_size, self.terrain_appearance) pass#self.terrain_shape.update_lod(LPoint3d(*self.ralph.getPos()), self.distance_to_obs, self.pixel_size, self.terrain_appearance) self.object_collection.update_instance() self.terrain.update_instance(LPoint3d(*self.ralph.getPos()), None) return task.cont def print_debug(self): print("Height:", self.get_height(self.ralph.getPos()), self.terrain.get_height(self.ralph.getPos())) print("Ralph:", self.ralph.get_pos()) print("Camera:", base.camera.get_pos())
class Raycaster(Entity): line_model = Mesh(vertices=[Vec3(0, 0, 0), Vec3(0, 0, 1)], mode='line') _boxcast_box = Entity(model='cube', origin_z=-.5, collider='box', color=color.white33, enabled=False) def __init__(self): super().__init__(name='raycaster', eternal=True) self._picker = CollisionTraverser() # Make a traverser self._pq = CollisionHandlerQueue() # Make a handler self._pickerNode = CollisionNode('raycaster') self._pickerNode.set_into_collide_mask(0) self._pickerNP = self.attach_new_node(self._pickerNode) self._picker.addCollider(self._pickerNP, self._pq) def distance(self, a, b): return sqrt(sum((a - b)**2 for a, b in zip(a, b))) def raycast(self, origin, direction=(0, 0, 1), distance=inf, traverse_target=scene, ignore=list(), debug=False): self.position = origin self.look_at(self.position + direction) self._pickerNode.clearSolids() ray = CollisionRay() ray.setOrigin(Vec3(0, 0, 0)) ray.setDirection(Vec3(0, 0, 1)) self._pickerNode.addSolid(ray) if debug: temp = Entity(position=origin, model=Raycaster.line_model, scale=Vec3(1, 1, min(distance, 9999)), add_to_scene_entities=False) temp.look_at(self.position + direction) destroy(temp, 1 / 30) self._picker.traverse(traverse_target) if self._pq.get_num_entries() == 0: self.hit = HitInfo(hit=False, distance=distance) return self.hit ignore += tuple([e for e in scene.entities if not e.collision]) self._pq.sort_entries() self.entries = [ # filter out ignored entities e for e in self._pq.getEntries() if e.get_into_node_path().parent not in ignore and self.distance( self.world_position, Vec3( *e.get_surface_point(render))) <= distance ] if len(self.entries) == 0: self.hit = HitInfo(hit=False, distance=distance) return self.hit self.collision = self.entries[0] nP = self.collision.get_into_node_path().parent point = Vec3(*self.collision.get_surface_point(nP)) world_point = Vec3(*self.collision.get_surface_point(render)) hit_dist = self.distance(self.world_position, world_point) self.hit = HitInfo(hit=True, distance=distance) for e in scene.entities: if e == nP: self.hit.entity = e nPs = [e.get_into_node_path().parent for e in self.entries] self.hit.entities = [e for e in scene.entities if e in nPs] self.hit.point = point self.hit.world_point = world_point self.hit.distance = hit_dist self.hit.normal = Vec3(*self.collision.get_surface_normal( self.collision.get_into_node_path().parent).normalized()) self.hit.world_normal = Vec3( *self.collision.get_surface_normal(render).normalized()) return self.hit self.hit = HitInfo(hit=False, distance=distance) return self.hit def boxcast(self, origin, direction=(0, 0, 1), distance=9999, thickness=(1, 1), traverse_target=scene, ignore=list(), debug=False): # similar to raycast, but with width and height if isinstance(thickness, (int, float, complex)): thickness = (thickness, thickness) Raycaster._boxcast_box.enabled = True Raycaster._boxcast_box.collision = True Raycaster._boxcast_box.position = origin Raycaster._boxcast_box.scale = Vec3(abs(thickness[0]), abs(thickness[1]), abs(distance)) Raycaster._boxcast_box.always_on_top = debug Raycaster._boxcast_box.visible = debug Raycaster._boxcast_box.look_at(origin + direction) hit_info = Raycaster._boxcast_box.intersects( traverse_target=traverse_target, ignore=ignore) if hit_info.world_point: hit_info.distance = ursinamath.distance(origin, hit_info.world_point) else: hit_info.distance = distance if debug: Raycaster._boxcast_box.collision = False Raycaster._boxcast_box.scale_z = hit_info.distance invoke(setattr, Raycaster._boxcast_box, 'enabled', False, delay=.2) else: Raycaster._boxcast_box.enabled = False return hit_info
class Picker(Viewer): """ View and click objects in a scene.""" def __init__(self): # Parent init. super(Picker, self).__init__() self.disableMouse() # Picker stuff. self.contact_margin = Vec3(0.01, 0.01, 0.01) self.parser = None self.marked = None self.attached_pairs = set() self.contacts = None self.contact_points = None self.contact_bottoms = None self.compound_components = [] self.compound_objects = [] self.joints = JointManager() self.wire_attrib = RenderModeAttrib.make(RenderModeAttrib.MWireframe, 4.) self.attachment_colors = (Vec4(0.1, 0.1, 1., 1.), Vec4(0.1, 0.8, 0.1, 1.), Vec4(1., 0.1, 1., 1.), Vec4(1., 0.5, 0.1, 1.), Vec4(1., 1., 1., 1.)) self.max_attach = 999 self.permanent_events += ["mouse1"] # Make cursor dot. self.cursor = self._build_cursor("cross") s = 0.08 self.cursor.setScale(s, s, s) self.cursor.setColor(1, 1, 1, 1) self.cursor.reparentTo(self.aspect2d) self.taskMgr.add(self.draw_cursor2d, "draw_cursor2d") self.permanent_tasks.append("draw_cursor2d") def init_ssos(self, *args, **kwargs): super(Picker, self).init_ssos(*args, **kwargs) def init_physics(self, *args, **kwargs): super(Picker, self).init_physics(*args, **kwargs) self.joints.bbase = self.bbase def init_picker(self): # Collision traverser self.traverser = CollisionTraverser("traverser") # Collision handler self.handler = CollisionHandlerQueue() # Initialize and set up picker ray node and NodePath self.picker = CollisionNode("mouse_ray") self.pickerNP = self.camera.attachNewNode(self.picker) self.picker.setFromCollideMask(GeomNode.getDefaultCollideMask()) self.picker_ray = CollisionRay() self.picker.addSolid(self.picker_ray) self.traverser.addCollider(self.pickerNP, self.handler) mark_color = (1, 1, 1, 0.3) self.base_mark = self.create_mark(color=mark_color) connector_color = (1, 1, 1, 1) self.base_connector = self.create_connector(color=connector_color) def _build_cursor(self, shape="sphere"): if shape == "sphere": cursor = self._load("sphere.bam") elif shape == "cross": cursor = LineNodePath() lines = [[Point3(-0.5, 0, 0), Point3(0.5, 0, 0)], [Point3(0, 0, -0.5), Point3(0, 0, 0.5)]] cursor.drawLines(lines) cursor.setThickness(1) cursor.create() # cursor = NodePath("cross") # S = {"cylinderX.bam": ((0., 0., 0.), (1., 0.1, 0.1)), # "cylinderY.bam": ((0., 0., 0.), (0.1, 1., 0.1)), # "cylinderZ.bam": ((0., 0., 0.), (0.1, 0.1, 1.))} # for k, v in S.iteritems(): # m = self._load(k) # m.setName(k) # m.setPos(*v[0]) # m.setScale(*v[1]) # m.reparentTo(cursor) #BP() return cursor def create_mark(self, color): """ Makes a graphical mark object.""" # Make a graphical box. props = dict(name="mark", color=color, model="box-round.egg") obj = GSO(props=props) return obj def create_connector(self, color): """ Makes a graphical connector object.""" # Make a graphical box. props = dict(name="connector", color=color, model="connector.egg") obj = GSO(props=props) return obj def start_picker(self, pickables): # Set pickable objs. for i, obj in enumerate(pickables): obj.setTag("pickable", str(i)) # Add mouse events. self.accept("mouse1", self.clicked, extraArgs=[1]) # Start contact detector. detector = ContactDetector(self.bbase.world, self.scene, margin=self.contact_margin) self.contacts = detector.contacts self.contact_bodies = detector.bodies self.contact_points = detector.points parser = Parser(self.contacts, self.contact_bodies) self.contact_bottoms = parser.bottom_bodies self.connectors = {} def stop_picker(self): self.removeTask("mouse1") def goto_sso(self, *args, **kwargs): self.clear_attachments() self.stop_picker() super(Picker, self).goto_sso(*args, **kwargs) self.remove_physics() # Start picker. pickables = self.sso.descendants(type_=PSO) self.start_picker(pickables) self.attach_physics() def get_picked_obj(self): mpos = self.mouseWatcherNode.getMouse() self.picker_ray.setFromLens(self.cam.node(), mpos.getX(), mpos.getY()) self.traverser.traverse(self.render) if self.handler.getNumEntries() > 0: # This is so we get the closest object self.handler.sortEntries() entries = self.handler.getEntries() for entry in entries: picked_obj = entry.getIntoNodePath().findNetTag("pickable") if not picked_obj.isEmpty(): break if picked_obj.isEmpty(): picked_obj = None else: picked_obj = None return picked_obj def clicked(self, button): """ Mouse click handler.""" if self.mouseWatcherNode.hasMouse(): # Get picked object picked_obj = self.get_picked_obj() if picked_obj is not None: if self.marked is None: # New mark activated. self.marked = picked_obj self.show_marked(picked_obj, True) event = "mark" elif picked_obj == self.marked: # Existing mark deactivated. self.show_marked(picked_obj, False) self.marked = None event = "unmark" else: # New attachment or detachment. pair = tuple(sorted((self.marked, picked_obj))) ij = tuple( sorted((self.contact_bodies.index(pair[0]), self.contact_bodies.index(pair[1])))) if ij in self.contacts: f_add = (ij, pair) not in self.attached_pairs if (not f_add or len(self.attached_pairs) < self.max_attach): self.store_attachment(ij, pair, f_add) self.show_marked(self.marked, False) self.marked = None event = "attach" if f_add else "detach" else: print("Max attachments already reached.") event = "max-attach" else: event = "non-contact" else: event = "non-pick" return picked_obj, event def store_attachment(self, ij, pair, f_add): """ Stores the attached objects, and draws them.""" if f_add: self.attached_pairs.add((ij, pair)) self.show_attachment(ij, True) self.attach_pair(pair, True) else: try: self.attached_pairs.remove((ij, pair)) except KeyError: pass else: self.attach_pair(pair, False) self.show_attachment(ij, False) def clear_attachments(self): """ Clear all attachments.""" if self.marked: self.show_marked(self.marked, False) self.marked = None self.mark = None for ij, pair in self.attached_pairs: self.attach_pair(pair, False) self.show_attachment(ij, False) self.attached_pairs = set() # self.reset_compounds() self.contacts = None self.contact_bodies = None self.contact_points = None self.contact_bottoms = None def _make_mark(self, node, extent, name): """ Makes a mark GSO.""" mark = self.base_mark.copy() mat = node.getMat(self.scene) mark.apply_prop(dict(name=name), other=self.scene) mark.setMat(self.scene, mat) mark.setScale(self.scene, mark.getScale(self.scene) + extent) mark.wrtReparentTo(node) return mark def show_marked(self, node, f_on): """ Turns on/off marked graphic.""" if f_on: extent = Vec3(0.15, 0.15, 0.15) name = "mark" self.mark = self._make_mark(node, extent, name) self.mark.init_tree(tags=("model", )) # Exclude object from casting shadows self.mark.hide(self.shadow_mask) self.mark.setTransparency(TransparencyAttrib.MAlpha) self.mark.setDepthWrite(False) self.mark.setBin("fixed", 0, priority=5) else: self.mark.removeNode() def _make_connector(self, parent, points, extent, name): """ Makes connector object.""" connector = self.base_connector.copy() scale = Vec3(*(np.ptp(points, axis=0))) scale_extended = scale + extent pos = Point3(*(np.min(points, axis=0) + scale / 2.)) connector.apply_prop(dict(name=name, scale=scale_extended, pos=pos), other=self.scene) connector.wrtReparentTo(parent) return connector def show_attachment(self, ij, f_on): """ Turns on/off attachment graphic.""" if f_on: parent = self.contact_bottoms[ij] points = self.contact_points[ij] extent = Vec3(0.15, 0.15, 0.15) name = "connector_%d-%d" % ij self.connectors[ij] = self._make_connector(parent, points, extent, name) self.connectors[ij].init_tree(tags=("model", )) else: self.connectors.pop(ij).removeNode() # def attach_pair(self, pair, f_on): # """ Adds/removes physical attachment between a pair of nodes.""" # key = tuple(sorted(p.node() for p in pair)) # # key = frozenset(pair) # if f_on: # # Create the joint and add it. # self.joints[key] = self.joints.make_fixed(*pair) # else: # # Remove it. # del self.joints[key] def attach_physics(self): # Attach `self.scene` to the physics world. try: exclude = zip(*self.compound_components)[0] except IndexError: exclude = [] bnodes = [ bnode for bnode in self.scene.descendants(type_=PSO) if bnode not in exclude ] for bnode in bnodes: bnode.init_resources(tags=("shape", )) bnode.setCollideMask(BitMask32.allOn()) bnode.node().setDeactivationEnabled(False) self.bbase.attach(bnodes) def reset_compounds(self): for n, p in self.compound_components: n.wrtReparentTo(p) self.compound_components = [] for cnode in self.compound_objects: cnode.destroy_resources() cnode.removeNode() self.compound_objects = [] def make_attachment_graph(self): if not self.contact_bodies: return None n = len(self.contact_bodies) mtx = np.zeros((n, n), dtype="i") for (i, j), _ in self.attached_pairs: # i = self.contact_bodies.index(pair[0]) # j = self.contact_bodies.index(pair[1]) mtx[i, j] = 1 # mtx[j, i] = 1 graph = nx.from_numpy_matrix(mtx) return graph def attach_pair(self, pair, f_on): """ Adds/removes physical attachment between a pair of nodes.""" # Get the connected subgroups. graph = self.make_attachment_graph() sgs = [sg for sg in nx.connected_components(graph) if len(sg) > 1] self.reset_compounds() # Iterate over subgroups, creating compound shapes. for sg in sgs: nodes = [self.contact_bodies[i] for i in sg] parents = [c.getParent() for c in nodes] self.compound_components.extend(zip(nodes, parents)) cname = "+".join([str(i) for i in sorted(sg)]) cnode = CPSO(cname) cnode.reparentTo(self.scene) cnode.add(nodes) cnode.init_tree(tags=("shape", )) cnode.destroy_component_shapes() self.compound_objects.append(cnode)
class RacingGame(ShowBase): # method completely taken from RoamingRalph demo: def addInstructions(self, pos, msg): return OnscreenText(text=msg, style=1, fg=(1, 1, 1, 1), scale=.05, shadow=(0, 0, 0, 1), parent=base.a2dTopLeft, pos=(0.08, -pos - 0.04), align=TextNode.ALeft) def addWin(self, time): msg = ( "You finished the course in: \n%d seconds \n press z to race again" % (time)) self.winText = OnscreenText(text=msg, style=1, fg=(1, 1, 1, 1), scale=.2, shadow=(0, 0, 0, 1), parent=base.a2dTopLeft, pos=(0.10, -0.5), align=TextNode.ALeft) def destroyWin(self): if (self.winText != None): self.winText.destroy() def setUpFlyingInstructions(self): self.inst[0] = self.addInstructions(.06, "Arrow Keys to move around") self.inst[1] = self.addInstructions(.12, "w and s to control pitch") self.inst[2] = self.addInstructions(.18, "a and d to control yaw") self.inst[3] = self.addInstructions(.24, "h to switch to driving mode") self.inst[4] = self.addInstructions(.3, "mouse click to add object") self.inst[5] = self.addInstructions(.36, "m to go to main") def startMovement(self): taskMgr.add(self.move, "moveTask") def destroyInstructions(self): # got way to destroy text from: # https://www.panda3d.org/manual/index.php/OnscreenText for element in self.inst: element.destroy() def startCreating(self): self.switchToCreateMode() self.destroyStartScreenButtons() self.startMovement() def startDriving(self): self.switchToDrivingMode() self.destroyStartScreenButtons() self.startMovement() def startTutorial(self): self.mode = self.modeTutorial self.setUpTutorial() self.destroyStartScreenButtons() self.startMovement() def setUpStartScreenButtons(self): self.creatingButton = DirectButton(text="Start Creating", scale=.1, command=self.startCreating, pos=(0, 0, .2)) self.drivingButton = DirectButton(text="Start Driving", scale=.1, command=self.startDriving, pos=(0, 0, 0)) self.tutorialButton = DirectButton(text="Start Tutorial", scale=.1, command=self.startTutorial, pos=(0, 0, -.2)) def destroyStartScreenButtons(self): self.creatingButton.destroy() self.drivingButton.destroy() self.tutorialButton.destroy() def setAddObjectTree(self): self.createdObject = self.createTree def setAddObjectRock(self): self.createdObject = self.createRock def setAddObjectPole(self): self.createdObject = self.createPole def setUpCreateButtons(self): # todo: add toggle for instructions so that they do not always interfere with button self.treeButton = DirectButton(text="Add Block", scale=.1, command=self.setAddObjectTree, pos=(0, 0, .85)) #self.rockButton = DirectButton(text="Add Rock", scale=.1, command=self.setAddObjectRock, pos=(-.5,.9,.85)) self.poleButton = DirectButton(text="Add Pole", scale=.1, command=self.setAddObjectPole, pos=(.5, 0, .85)) def setUpCreateObjects(self): self.createdObject = 0 self.createTree = 0 self.createRock = 1 self.createPole = 2 def destroyCreateButtons(self): self.treeButton.destroy() #self.rockButton.destroy() self.poleButton.destroy() def setUpDrivingInstructions(self): self.inst[0] = self.addInstructions( .06, "Right arrow and left arrow to turn") self.inst[1] = self.addInstructions( .12, "Forward and Backward arrow to go forward and backward") self.inst[2] = self.addInstructions(.18, "h to switch to add object mode") self.inst[3] = self.addInstructions(.24, "z to switch to start the race") self.inst[4] = self.addInstructions(.30, "m to go to main") def setUpWindow(self): # set up the egg files needed # since this method is made for python 2.7 but these objects are made for python 3.5, seems to be incompatible #m = Mountain("TestMountain", 60) #m.editFile() #p = Pole("TestPole", 0, 0, 0) #p.editFile() #c = Car("TestCar", 0,0,0) #c.editFile() #b = BeaconLight("BeaconLight",0,0,0) #b.editFile() # Set up the window, camera, etc ShowBase.__init__(self) # Set the background color to black self.win.setClearColor((0, 1, 1, 1)) # this is the model I created using mountainMaker.py self.environ = loader.loadModel("TestMountain1") self.environ.reparentTo(render) self.car = loader.loadModel("TestCar") # found how to solve the problem of only being able to see objects from # certain angles with this: # http://www.panda3d.org/manual/index.php/Backface_Culling_and_Frontface_Culling self.car.setTwoSided(True) self.car.reparentTo(render) # Create some lighting # this is a part that is completely unchanged from demo ambientLight = AmbientLight("ambientLight") ambientLight.setColor((.3, .3, .3, 1)) directionalLight = DirectionalLight("directionalLight") directionalLight.setDirection((-5, -5, -5)) directionalLight.setColor((1, 1, 1, 1)) directionalLight.setSpecularColor((1, 1, 1, 1)) # to get light from other direction to light up things on both sides directionalLight2 = DirectionalLight("directionalLight") directionalLight2.setDirection((5, 5, 5)) directionalLight2.setColor((1, 1, 1, 1)) directionalLight2.setSpecularColor((1, 1, 1, 1)) render.setLight(render.attachNewNode(ambientLight)) render.setLight(render.attachNewNode(directionalLight)) render.setLight(render.attachNewNode(directionalLight2)) def setUpCar(self): # for adjusting so that the position is the center of the car self.adjustedXForCenter = 10 / 2 self.adjustedYForCenter = 20 / 2 # for some reason can't change this or the collisions do not work self.carPositionX = 20 self.carPositionY = 20 self.carPositionZ = 100 # note for rotating camera: from this website: # https://www.panda3d.org/manual/index.php/Common_State_Changes # setHpr(Yaw, Pitch, Roll) # setting up initial conditions for which way camera is rotated self.carYaw = 0 self.carPitch = 0 (actualXPos, actualYPos) = RacingGame.findActualCenter( self, self.carPositionX, self.carPositionY, self.adjustedXForCenter, self.adjustedYForCenter, self.carYaw) self.car.setX(actualXPos) self.car.setY(actualYPos) self.car.setZ(self.carPositionZ) self.car.setHpr(self.carYaw, self.carPitch, 0) def setUpCamera(self): # for flying mode self.cameraPositionX = 500 self.cameraPositionY = 500 self.cameraPositionZ = 40 # note for rotating camera: from this website: # https://www.panda3d.org/manual/index.php/Common_State_Changes # setHpr(Yaw, Pitch, Roll) # setting up initial conditions for which way camera is rotated self.cameraYaw = 0 self.cameraPitch = 0 # Set up the camera self.disableMouse() # should probably clean up these magic numbers self.camera.setPos(self.cameraPositionX, self.cameraPositionY, self.cameraPositionZ) def setUpKeyMap(self): # This is used to store which keys are currently pressed. self.keyMap = { "left": 0, "right": 0, "forward": 0, "cam-left": 0, "cam-right": 0, "backward": 0, "cam-up": 0, "cam-down": 0, "add-car": 0, "switch-mode": 0, "mouse-click": 0, "race-start": 0, "to-main": 0 } # Accept the control keys for movement and rotation #setting up keys for movement self.accept("escape", sys.exit) self.accept("arrow_left", self.setKey, ["left", True]) self.accept("arrow_right", self.setKey, ["right", True]) self.accept("arrow_up", self.setKey, ["forward", True]) self.accept("arrow_down", self.setKey, ["backward", True]) self.accept("arrow_left-up", self.setKey, ["left", False]) self.accept("arrow_right-up", self.setKey, ["right", False]) self.accept("arrow_up-up", self.setKey, ["forward", False]) self.accept("arrow_down-up", self.setKey, ["backward", False]) self.accept("m", self.setKey, ["to-main", True]) self.accept("m-up", self.setKey, ["to-main", False]) # starting race self.accept("z", self.setKey, ["race-start", True]) self.accept("z-up", self.setKey, ["race-start", False]) # adding car self.accept("mouse1", self.setKey, ["mouse-click", True]) self.accept("mouse1-up", self.setKey, ["mouse-click", False]) # setting up orientation of the camera self.accept("a", self.setKey, ["cam-left", True]) self.accept("s", self.setKey, ["cam-down", True]) self.accept("a-up", self.setKey, ["cam-left", False]) self.accept("s-up", self.setKey, ["cam-down", False]) self.accept("d", self.setKey, ["cam-right", True]) self.accept("d-up", self.setKey, ["cam-right", False]) self.accept("w", self.setKey, ["cam-up", True]) self.accept("w-up", self.setKey, ["cam-up", False]) # to switch between tasks self.accept("h", self.setKey, ["switch-mode", True]) self.accept("h-up", self.setKey, ["switch-mode", False]) def __init__(self): self.setUpWindow() self.setUpKeyMap() #instructions self.inst = [""] * 6 self.setUpFlyingInstructions() self.mouseClicked = False self.winText = None # important for setting the size relative to everything else # found it here : https://www.panda3d.org/manual/index.php/Common_State_Changes # set the mode that the player is currently in self.mode = 0 self.modeFly = 2 self.modeRace = 1 self.modeStart = 0 self.modeTutorial = 3 # to ensure that when pressing h it only switches once each press self.hasSwitched = False self.raceBegan = False self.raceTime = 0 self.poleOn = 0 self.setUpCamera() self.setUpCar() self.setUpCarCollider() self.setUpMouseCollider() self.setUpStartScreenButtons() # make the rocks and other stuff that will show up self.setUpCreateObjects() self.objects = [] self.poles = [] self.beaconLight = None self.beaconLightZ = 50 self.target = None taskMgr.add(self.move, "moveTask") def findCollisionTube(self): # using bassically same formula as Y position # decided not to use this because possible problems with ground getting # hit instead of things in front degToRad = math.pi / 180 xAddition = ( self.adjustedXForCenter * math.cos(self.carYaw * degToRad) + self.adjustedYForCenter * math.sin(self.carYaw * degToRad)) / 2 yAddition = ( self.adjustedXForCenter * math.cos(self.carYaw * degToRad) + self.adjustedYForCenter * math.sin(self.carYaw * degToRad)) def findCarFrontDir(self): degToRad = math.pi / 180 xDir = -1 * math.sin(degToRad * self.carYaw) * math.cos( degToRad * self.carPitch) yDir = 1 * math.cos(degToRad * self.carYaw) * math.cos( degToRad * self.carPitch) zDir = 1 * math.sin(degToRad * self.carPitch) return (xDir, yDir, zDir) def setUpCarCollider(self): self.carCollideTrav = CollisionTraverser() base.cTrav = self.carCollideTrav self.handler = CollisionHandlerQueue() self.carRay = CollisionRay(self.carPositionX, self.carPositionY, self.carPositionZ, 0, 0, -1) self.carForwardHandler = CollisionHandlerQueue() # so that it doesn't collide with things forward and backward. degToRad = math.pi / 180 (xDir, yDir, zDir) = self.findCarFrontDir() self.carRayForward = CollisionRay(self.carPositionX, self.carPositionY, self.carPositionZ, xDir, yDir, zDir) self.carForwardCollision = CollisionNode("forwardCollision") self.carForwardCollision.addSolid(self.carRayForward) self.carForwardCollision.setIntoCollideMask(CollideMask.allOff()) self.carForwardCollisionNode = self.car.attachNewNode( self.carForwardCollision) (centerX, centerY) = self.findActualCenter(0, 0, self.adjustedXForCenter, self.adjustedYForCenter, self.carYaw) self.carRayForward.setOrigin(5, 10, 5) self.carCollision = CollisionNode("groundCollision") self.carCollision.addSolid(self.carRay) self.carCollision.setIntoCollideMask(CollideMask.allOff()) self.carCollisionNode = self.car.attachNewNode(self.carCollision) self.carCollideTrav.addCollider(self.carCollisionNode, self.handler) self.carCollideTrav.addCollider(self.carForwardCollisionNode, self.carForwardHandler) self.carForwardCollisionNode.show() def setUpMouseCollider(self): # clicking on objects stuff came from here: # https://www.panda3d.org/manual/index.php/Collision_Traversers # https://www.panda3d.org/manual/index.php/Collision_Handlers # will not use the traverser set up by car because slow # instead we will render each time clicked self.mouseCollideTrav = CollisionTraverser("mouseTraverse") self.mousehandler = CollisionHandlerQueue() # edit this so that from Object is the camera # self.mouseCollideTrav.addCollider(fromObject, queue) # self.mouseCollideTrav.traverse(render) # this next part came from: # https://www.panda3d.org/manual/index.php/Clicking_on_3D_Objects pickerNode = CollisionNode("mouseRay") pickerNode.setFromCollideMask(GeomNode.getDefaultCollideMask()) pickerNode.setIntoCollideMask(CollideMask.allOff()) self.pickerRay = CollisionRay() pickerNode.addSolid(self.pickerRay) pickerNp = camera.attachNewNode(pickerNode) self.mouseCollideTrav.addCollider(pickerNp, self.mousehandler) # Records the state of the arrow keys def setKey(self, key, value): self.keyMap[key] = value def switchToCreateMode(self): self.mode = self.modeFly self.destroyInstructions() self.setUpFlyingInstructions() self.hasSwitched = True self.setUpCreateButtons() def switchToDrivingMode(self): self.mode = self.modeRace self.destroyInstructions() self.setUpDrivingInstructions() self.hasSwitched = True def switchToMainMode(self): self.mode = 0 self.setUpStartScreenButtons() def setTutorialText(self, str): # reusing inst so that it is easy to erase self.inst[0] = self.addInstructions(.06, str) def move(self, task): if (self.keyMap["to-main"]): self.switchToMainMode() elif (self.mode == self.modeFly): if self.keyMap["switch-mode"] and not self.hasSwitched: self.switchToDrivingMode() self.destroyCreateButtons() elif not self.keyMap["switch-mode"]: # to ensure switch mode only happens once per button pressed self.hasSwitched = False self.findNewCameraPosition() self.checkAndAddNewObject() elif (self.mode == self.modeRace): if self.keyMap["switch-mode"] and not self.hasSwitched: self.destroyWin() self.switchToCreateMode() elif not self.keyMap["switch-mode"]: # this ensures that when key is pressed only switch states once self.hasSwitched = False self.findCarNewXandY() # when implementing the use of the mouse look here: # https://www.panda3d.org/manual/index.php/Clicking_on_3D_Objects self.setCarZ() self.setCameraPositionBehindCar() if (self.keyMap["race-start"] and not self.raceBegan and len(self.poles) >= 2): self.destroyWin() # try so that if there are not enough poles then the game # doesn't crash try: self.raceBegan = True self.raceTime = time.time() # puts in the car where the first two poles are # first col of poles is model, second x coord, thrid y coord, fourth z coord self.carPositionX = (self.poles[0][1] + self.poles[1][1]) / 2 self.carPositionY = (self.poles[0][2] + self.poles[1][2]) / 2 # meant to show where car should go # got info on how to do point lights from here: # https://www.panda3d.org/manual/index.php/Lighting self.beaconLight = PointLight("beaconLight") self.beaconLight.setColor((1, 1, 1, 1)) self.beaconLight.setAttenuation((0, 0, 1)) self.beaconLightHolder = render.attachNewNode( self.beaconLight) (beaconLightX, beaconLightY) = self.getNextGateCenter() # target for driver if (self.target == None): self.target = loader.loadModel("BeaconLight.egg") self.target.setTwoSided(True) self.target.reparentTo(render) if (beaconLightX != None): self.beaconLightHolder.setPos(beaconLightX, beaconLightY, self.beaconLightZ) render.setLight(self.beaconLightHolder) self.target.setPos(beaconLightX, beaconLightY, self.beaconLightZ) except: # not enough poles pass if (self.raceBegan): # minus 1 just in case non even if (self.poleOn + 1 >= len(self.poles)): self.raceBegan = False self.addWin(time.time() - self.raceTime) # since race ended try: self.target.destroy() except: pass self.beaconLight = None # use object + lights self.beaconLightHolder = None self.poleOn = 0 else: acceptableError = 100 # formula I created: (p2y-p1y)/(p2x-p1x)*(p2x-pAx)+p2y = # expected pAy # so if the actual car positionY is within acceptableError of # pAy then the car is between the two poles # if in between poles middleX = (self.poles[self.poleOn][1] + self.poles[self.poleOn + 1][1]) / 2 middleY = (self.poles[self.poleOn][2] + self.poles[self.poleOn + 1][2]) / 2 expectedCarY = ( (self.poles[self.poleOn][2] - self.poles[self.poleOn + 1][2]) / (self.poles[self.poleOn][1] - self.poles[self.poleOn + 1][1]) * (self.poles[self.poleOn][1] - self.carPositionX) + self.poles[self.poleOn][2]) # do not really care about car being inbetween pole in z axis # because 2 demensional if (expectedCarY + acceptableError > self.carPositionY and expectedCarY - acceptableError < self.carPositionY): self.poleOn += 2 # only when last pole found is it necesary to add light # to guide to next place elsewhere (beaconLightX, beaconLightY) = self.getNextGateCenter() if (beaconLightX != None): self.beaconLightHolder.setPos( beaconLightX, beaconLightY, self.beaconLightZ) self.target.setPos(beaconLightX, beaconLightY, self.beaconLightZ) elif (self.mode == self.modeTutorial): self.destroyWin() # do the tutorial part for the creating timeBeforeNext = 2 if (self.tutorialPause == True): if (time.time() - self.tutorialActionTime > timeBeforeNext): self.tutorialPause = False else: if (self.tutorialStep == -1): self.destroyInstructions() self.setTutorialText( "use w and s to move camera up and down") self.tutorialStep += 1 # do this until the user has completed all of the task self.checkTutorialStep( 0, (self.keyMap["cam-up"] or self.keyMap["cam-down"]), "use a and d to rotate camera right and left") self.checkTutorialStep( 1, (self.keyMap["cam-left"] or self.keyMap["cam-right"]), "use up-arrow and down-arrow to turn camera forward and backward" ) self.checkTutorialStep( 2, (self.keyMap["forward"] or self.keyMap["backward"]), "use left-arrow and right-arrow to slide camera left and right" ) self.checkTutorialStep( 3, (self.keyMap["left"] or self.keyMap["right"]), "use mouse click to place objects on terrain") self.checkTutorialStep( 4, (self.keyMap["mouse-click"]), "use up-arrow and down-arrow to move car forward and backward", (self.switchToDrivingMode, self.destroyInstructions)) # need to ensure that the mode stays as tutorial self.mode = self.modeTutorial # then tutorial part for the driving self.checkTutorialStep( 5, (self.keyMap["forward"] or self.keyMap["backward"]), "use right-arrow and left-arrow to rotate car left and right" ) self.checkTutorialStep( 6, (self.keyMap["left"] or self.keyMap["right"]), "Use poles to make the race course\n use z to start race") self.checkTutorialStep( 7, True, "Follow yellow block through the gates till you win") self.checkTutorialStep( 8, True, "Watch for high Terrain and blocks because you can not get through those" ) self.checkTutorialStep(9, True, "") if (self.tutorialStep > 9): # switch to main self.switchToMainMode() # for movement if (self.tutorialStep <= 4): self.findNewCameraPosition() self.checkAndAddNewObject() if (self.tutorialStep > 4 and self.tutorialStep <= 9): self.findCarNewXandY() self.setCarZ() self.setCameraPositionBehindCar() return task.cont def getNextGateCenter(self): if (len(self.poles) > self.poleOn + 1): positionX = (self.poles[self.poleOn][1] + self.poles[self.poleOn + 1][1]) / 2 positionY = (self.poles[self.poleOn][2] + self.poles[self.poleOn + 1][2]) / 2 return (positionX, positionY) else: return (None, None) def checkTutorialStep(self, step, keysNeeded, nextText, functions=None): if (self.tutorialStep == step and self.tutorialNextStep == True): if (functions != None): for func in functions: func() self.destroyInstructions() self.setTutorialText(nextText) self.tutorialStep += 1 self.tutorialNextStep = False elif (self.tutorialStep == step and keysNeeded): self.tutorialNextStep = True self.tutorialActionTime = time.time() self.tutorialPause = True def setUpTutorial(self): self.tutorialStep = -1 self.tutorialPause = False # this records when last tutorial action taken self.tutorialActionTime = 0 self.tutorialNextStep = False def checkAndAddNewObject(self): # clicking on 3D objects comes from here: # https://www.panda3d.org/manual/index.php/Clicking_on_3D_Objects # checks if it needs to add any objects: if (self.keyMap["mouse-click"] and self.mouseClicked == False): self.mouseClicked = True # found way to speed this up by only doing collision check # when mouse clicked by not using cTrav like in this method # the way I did it I found here: # https://www.panda3d.org/manual/index.php/Clicking_on_3D_Objects # self.mouseCollideTrav.traverse(render) if (base.mouseWatcherNode.hasMouse()): mousePos = base.mouseWatcherNode.getMouse() self.pickerRay.setFromLens(base.camNode, mousePos.getX(), mousePos.getY()) # do not put this before previous line, will get the coordinates # of last mouse click and where pickerRay was # ahhhhhhh!!!!!!!!!!! self.mouseCollideTrav.traverse(render) if (self.mousehandler.getNumEntries() > 0): entries = list(self.mousehandler.getEntries()) # pathagorean formula for sorting entries.sort( key=lambda x: ((x.getSurfacePoint(render).getX( ) - self.cameraPositionX)**2 + (x.getSurfacePoint( render).getY() - self.cameraPositionY)**2)**.5) newX = entries[0].getSurfacePoint(render).getX() newY = entries[0].getSurfacePoint(render).getY() newZ = entries[0].getSurfacePoint(render).getZ() adjustedX = 10 / 2 adjustedY = 20 / 2 yaw = 0 # have to adjust this once there are different sized # objects added (actualXPos, actualYPos) = RacingGame.findActualCenter( self, newX, newY, adjustedX, adjustedY, yaw) if (self.createdObject == self.createPole): self.poles.append([ loader.loadModel("TestPole.egg"), actualXPos, actualYPos, newZ ]) self.poles[len(self.poles) - 1][0].reparentTo(render) self.poles[len(self.poles) - 1][0].setTwoSided(True) self.poles[len(self.poles) - 1][0].setPos( actualXPos, actualYPos, newZ) else: newCar = loader.loadModel("TestCar.egg") newCar.reparentTo(render) newCar.setPos(actualXPos, actualYPos, newZ) # should take out because slow, but objects can not be # seen without this because only seen from one direction # even though normal vectors set up. newCar.setTwoSided(True) self.objects.append(newCar) elif (not self.keyMap["mouse-click"]): # elif because for some reason mouseClicked was becoming false # while click still pressed self.mouseClicked = False def findNewCameraPosition(self): # Get the time that elapsed since last frame. We multiply this with # the desired speed in order to find out with which distance to move # in order to achieve that desired speed. dt = globalClock.getDt() # the angle is in degrees with 360 equal to full rotation angleAdjustment = 100 if self.keyMap["cam-left"]: self.cameraYaw += angleAdjustment * dt if self.keyMap["cam-right"]: self.cameraYaw -= angleAdjustment * dt if self.keyMap["cam-up"]: self.cameraPitch += angleAdjustment * dt if self.keyMap["cam-down"]: self.cameraPitch -= angleAdjustment * dt positionAdjustment = 500 # should switch rad and Deg in variable name radToDeg = math.pi / 180 # the x and y component of left and right moves, do not need to # compensate in z axis because not doing any roll, so there should be # no zComponent xComponent = math.cos(self.cameraYaw * radToDeg) yComponent = math.sin(self.cameraYaw * radToDeg) if self.keyMap["left"]: self.cameraPositionX -= positionAdjustment * dt * xComponent self.cameraPositionY -= positionAdjustment * dt * yComponent if self.keyMap["right"]: self.cameraPositionX += positionAdjustment * dt * xComponent self.cameraPositionY += positionAdjustment * dt * yComponent # for going forward, the orientation is rotated 90 degrees so need to # change components xComponent = math.cos(self.cameraYaw * radToDeg + math.pi / 2) * math.cos(self.cameraPitch * radToDeg) yComponent = math.sin(self.cameraYaw * radToDeg + math.pi / 2) * math.cos(self.cameraPitch * radToDeg) zComponent = math.sin(self.cameraPitch * radToDeg) if self.keyMap["forward"]: self.cameraPositionX += positionAdjustment * dt * xComponent self.cameraPositionY += positionAdjustment * dt * yComponent self.cameraPositionZ += positionAdjustment * dt * zComponent if self.keyMap["backward"]: self.cameraPositionX -= positionAdjustment * dt * xComponent self.cameraPositionY -= positionAdjustment * dt * yComponent self.cameraPositionZ -= positionAdjustment * dt * zComponent self.camera.setX(self.cameraPositionX) self.camera.setY(self.cameraPositionY) self.camera.setZ(self.cameraPositionZ) self.camera.setHpr(self.cameraYaw, self.cameraPitch, 0) def carForwardImpact(self): # update position so that it is pointing right directions #(dirX, dirY, dirZ) = self.findCarFrontDir() #self.carRayForward.setDirection(dirX, dirY, dirZ) degToRad = math.pi / 180 # + math.pi since it is taking corner and going to center rather # than opposite that function actually does #(centerX,centerY) = self.findActualCenter(0,0,self.adjustedXForCenter, # self.adjustedYForCenter, self.carYaw # +math.pi) posAboveGround = 5 # need to update with new coordinates self.carCollideTrav.traverse(render) collisions = list(self.carForwardHandler.getEntries()) # closest collision using pythagorean formula collisions.sort( key=lambda x: (x.getSurfacePoint(render).getX() - self.carPositionX)**2 + (x.getSurfacePoint(render).getY() - self.carPositionY)**2) if (len(collisions) > 0): (actualX, actualY) = RacingGame.findActualCenter( self, self.carPositionX, self.carPositionY, self.adjustedXForCenter, self.adjustedYForCenter, self.carYaw) distance = ( (collisions[0].getSurfacePoint(render).getX() - actualX)**2 + (collisions[0].getSurfacePoint(render).getY() - actualY)** 2)**.5 error = .9 # so that the collisionray does not detect car itself return distance / 2 <= self.adjustedYForCenter * .9 else: return False def findCarNewXandY(self): # Get the time that elapsed since last frame. We multiply this with # the desired speed in order to find out with which distance to move # in order to achieve that desired speed. deltaTime = globalClock.getDt() degreeAdjustment = 60 positionAdjustment = 100 # should switch rad and Deg in variable name radToDeg = math.pi / 180 # the x and y component of left and right moves, do not need to # compensate in z axis because not doing any roll, so there should be # no zComponent xComponent = math.sin(self.carYaw * radToDeg) yComponent = math.cos(self.carYaw * radToDeg) if self.keyMap["left"]: self.carYaw += degreeAdjustment * deltaTime if self.keyMap["right"]: self.carYaw -= degreeAdjustment * deltaTime if (self.keyMap["forward"] and not self.carForwardImpact()): self.carPositionX -= positionAdjustment * deltaTime * xComponent self.carPositionY += positionAdjustment * deltaTime * yComponent if self.keyMap["backward"]: self.carPositionX += positionAdjustment * deltaTime * xComponent self.carPositionY -= positionAdjustment * deltaTime * yComponent # need to consider both the x and y component of offset for both # because x slowly changes to y as it turns (actualXPos, actualYPos) = RacingGame.findActualCenter( self, self.carPositionX, self.carPositionY, self.adjustedXForCenter, self.adjustedYForCenter, self.carYaw) self.car.setX(actualXPos) self.car.setY(actualYPos) self.car.setZ(self.carPositionZ) self.car.setHpr(self.carYaw, self.carPitch, 0) def setCarZ(self): # almost directly taken from ralph example entries = list(self.handler.getEntries()) entries.sort(key=lambda x: x.getSurfacePoint(render).getZ()) # worry about which thing it collides with later if (len(entries) > 0): # and entries[0].getIntoNode().getName() == "mountainCollide": self.carPositionZ = (entries[0].getSurfacePoint(render).getZ()) else: # because at 25 everything should be below car and do not want # to continually go up or else it may go up forever. self.carPositionZ = 25 self.setCameraPositionBehindCar() def setCameraPositionBehindCar(self): # Modify view of camera so that it is behind car # should be named degToRad radToDeg = math.pi / 180 distanceBehind = 200 distanceAbove = 60 self.camera.setHpr(self.carYaw, -.5, 0) (actualXPos, actualYPos) = self.findActualCenter( self.carPositionX, self.carPositionY, self.adjustedXForCenter, self.adjustedXForCenter, self.carYaw) camX = actualXPos + distanceBehind * math.sin(radToDeg * self.carYaw) camY = actualYPos - distanceBehind * math.cos(radToDeg * self.carYaw) camZ = self.carPositionZ + distanceAbove self.camera.setPos(camX, camY, camZ) def findActualCenter(self, positionX, positionY, adjustedX, adjustedY, yaw): # will need to fix this later, it seems to be adjusting wrong # so that it is puting box away from click instead of on it # update, I think this is fixed?!? degToRad = math.pi / 180 actualXPos = (positionX - adjustedX * math.cos(degToRad * yaw) + adjustedY * math.sin(degToRad * yaw)) actualYPos = (positionY - adjustedY * math.cos(degToRad * yaw) - adjustedX * math.sin(degToRad * yaw)) return (actualXPos, actualYPos)
class Character(DirectObject, XMLExportable, PropertiesTableAbstract, GameEntity): def __init__(self, attributes, showCollisions, grid_currentx, grid_currenty, grid_playable_pos, parent): GameEntity.__init__(self, parent) #running parent constructor self.movtask = 0 self.showCollisions = showCollisions self.grid_currentx = grid_currentx self.grid_currenty = grid_currenty self.grid_playable_pos = grid_playable_pos self.attributes = attributes self.onPicked = '' self.onWalked = '' self.typeName = 'character' self.properties = { 'url': '', 'onWalked': '', 'onPicked': '', 'id': '', 'inclination': '', 'scale': '', 'hitboxscale': '', 'speed': '', 'playable': '', 'direction': '' } if attributes.has_key('url'): self.properties['url'] = attributes['url'].value else: print "WARNING: url not defined, loading placeholder" self.properties['url'] = 'misc/placeholder' if attributes.has_key('id'): self.properties['id'] = attributes['id'].value else: self.properties['id'] = 'all' if attributes.has_key('inclination'): self.properties['inclination'] = float( attributes['inclination'].value) else: self.properties['inclination'] = 30.0 if attributes.has_key('scale'): self.properties['scale'] = float(attributes['scale'].value) else: self.properties['scale'] = 1.0 if attributes.has_key('hitboxscale'): self.properties['hitboxscale'] = float( attributes['hitboxscale'].value) else: self.properties['hitboxscale'] = 1.0 if attributes.has_key('speed'): self.properties['speed'] = float(attributes['speed'].value) else: self.properties['speed'] = 1.0 #self.isNPC remains true while isPlayable is changable if attributes.has_key('playable'): self.playable = playable = attributes['playable'].value if self.playable == 'false': self.isNPC = False #print "setting ", self.properties['id'], " to ", self.isNPC else: self.isNPC = True #print "setting ", self.properties['id'], " to ", self.isNPC else: self.playable = playable = 'false' self.isNPC = True self.properties['playable'] = self.playable if attributes.has_key('direction'): self.properties['direction'] = attributes['direction'].value else: self.properties['direction'] = "down" if attributes.has_key('onWalked'): self.properties['onWalked'] = self.onWalked = attributes[ 'onWalked'].value else: self.properties['onWalked'] = self.onWalked = "" if attributes.has_key('onPicked'): self.properties['onPicked'] = self.onPicked = attributes[ 'onPicked'].value self.generateNode(showCollisions) def generateNode(self, showCollisions): self.destroy() #setting local variable attributes = self.attributes #defaulted to None self.pickCTrav = None #movement self.state = "still" self.showCollisions = showCollisions self.movtask = 0 self.currentlydown = [] self.currentlyfollowed = 0 self.pickRequest = False #public props self.node = NodePath("characternode") self.node.setTwoSided(True) self.wtop = self.applyNearestFilter( loader.loadModel( resourceManager.getResource(self.properties['url']) + '/wtop.egg')) self.wdown = self.applyNearestFilter( loader.loadModel( resourceManager.getResource(self.properties['url']) + '/wdown.egg')) self.wleft = self.applyNearestFilter( loader.loadModel( resourceManager.getResource(self.properties['url']) + '/wleft.egg')) self.wright = self.applyNearestFilter( loader.loadModel( resourceManager.getResource(self.properties['url']) + '/wright.egg')) self.stop = self.applyNearestFilter( loader.loadModel( resourceManager.getResource(self.properties['url']) + '/stop.egg')) self.sdown = self.applyNearestFilter( loader.loadModel( resourceManager.getResource(self.properties['url']) + '/sdown.egg')) self.sleft = self.applyNearestFilter( loader.loadModel( resourceManager.getResource(self.properties['url']) + '/sleft.egg')) self.sright = self.applyNearestFilter( loader.loadModel( resourceManager.getResource(self.properties['url']) + '/sright.egg')) #Texture.FTNearest self.wtop.reparentTo(self.node) self.wdown.reparentTo(self.node) self.wleft.reparentTo(self.node) self.wright.reparentTo(self.node) self.stop.reparentTo(self.node) self.sdown.reparentTo(self.node) self.sleft.reparentTo(self.node) self.sright.reparentTo(self.node) self.leftdown = False self.rightdown = False self.downdown = False self.topdown = False if self.playable == "true": self.setPlayable( False) #seems nonsense, triggered on grid.changeMap event self.node.setTag( "playable", "true" ) #setting this to make it recognizable from grid changeMap api self.setCollisions(True) self.setPickCollisions(True) else: self.setPlayable(False) self.node.setTag("playable", "false") self.setCollisions(False) self.setPickCollisions(False) #self.node.setX((-32/2)+0.5) self.node.setP(-(360 - int(self.properties['inclination']))) self.node.setScale(float(self.properties['scale'])) self.node.setTransparency(TransparencyAttrib.MAlpha) self.lastpos = self.node.getPos() self.showAllSubnodes() #taskMgr.doMethodLater(4, self.face, 'charload'+self.properties['id'], [self.properties['direction']]) self.face(self.properties['direction']) #set unique id self.node.setTag("id", self.properties['id']) #setting scripting part self.node.setTag("onWalked", self.onWalked) self.node.setTag("onPicked", self.onPicked) #storing a pointer of the gamenode self.node.setPythonTag("gamenode", self) self.npc_walk_stack = [] self.npc_walk_happening = False self.globalLock = False self.setX(self.grid_currentx) self.setY(self.grid_currenty) if self.isNPC != True: print "attempting creation of NPC in ", self.grid_currentx, "-", self.grid_currenty if attributes.has_key('playable'): if self.isNPC != False: if ((self.grid_playable_pos.getX() != 0) and (self.grid_playable_pos.getY() != 0)): print 'GRID: moving player to ' + str( self.grid_playable_pos) self.setX(self.grid_playable_pos.getX()) self.setY(self.grid_playable_pos.getY()) #automatic reparenting (and showing) when (re)generating node self.node.wrtReparentTo(self.parent.node) def getName(self): return 'Character: ' + self.properties['id'] def xmlAttributes(self): return self.properties def xmlTypeName(self): return self.typeName ''' Sanitize properties data to be of correct type from string ''' def sanitizeProperties(self): #sanitizing data self.properties['inclination'] = float(self.properties['inclination']) self.properties['hitboxscale'] = float(self.properties['hitboxscale']) self.properties['speed'] = float(self.properties['speed']) self.properties['scale'] = float(self.properties['scale']) self.updateTilePosition() #interface needed by PropertiesTable # regenerates the node at every change def onPropertiesUpdated(self): self.sanitizeProperties() self.generateNode(self.showCollisions) #interface needed by PropertiesTable #TODO: implement as real interface? def getPropertyList(self): return self.properties #interface needed by PropertiesTable def setProperty(self, key, value): self.properties[key] = value def setSpeed(self, s): self.properties['speed'] = s def applyNearestFilter(self, model): for tex in model.findAllTextures(): tex.setMinfilter(Texture.FT_nearest) tex.setMagfilter(Texture.FT_nearest) return model ''' make the npc walk in direction for units ''' def npc_push_walk(self, direction, units): #locking script execution self.globalLock = True script.addOneCustomLock(self) #start the walking self.npc_walk_stack.append([direction, units]) self.npc_walk_helper() #apicall def npc_walk_helper(self): x = self.node.getX() y = self.node.getZ() #concurrent protection if self.npc_walk_happening == True: return #returning if no movement has to be performed if len(self.npc_walk_stack) < 0: return movement = self.npc_walk_stack.pop(0) direction = movement[0] units = movement[1] self.npc_targetx = x self.npc_targety = y self.npc_direction = direction if (direction == "down"): self.npc_targety = self.npc_targety - units elif (direction == "up"): self.npc_targety = self.npc_targety + units elif (direction == "left"): self.npc_targetx = self.npc_targetx - units elif (direction == "right"): self.npc_targetx = self.npc_targetx + units self.setAnim(direction) self.npc_walk_happening = True self.npc_movtask = taskMgr.add(self.npc_walk_task, "npc_moveCharacterTask" + self.properties['id'], uponDeath=self.npc_walk_callback) def npc_walk_task(self, task): dt = globalClock.getDt() if (self.npc_direction == 'left'): self.node.setX(self.node.getX() - 1 * dt * self.properties['speed']) currentx = self.node.getX() if currentx <= self.npc_targetx: return task.done if (self.npc_direction == 'right'): self.node.setX(self.node.getX() + 1 * dt * self.properties['speed']) currentx = self.node.getX() if currentx >= self.npc_targetx: return task.done if (self.npc_direction == 'up'): self.node.setZ(self.node.getZ() + 1 * dt * self.properties['speed']) currenty = self.node.getZ() if currenty >= self.npc_targety: return task.done if (self.npc_direction == 'down'): self.node.setZ(self.node.getZ() - 1 * dt * self.properties['speed']) currenty = self.node.getZ() if currenty <= self.npc_targety: return task.done return task.cont def npc_walk_callback(self, task): self.face(self.npc_direction) #unlocking concurrent movement protection self.npc_walk_happening = False if len(self.npc_walk_stack) > 0: self.npc_walk_helper() else: #character ended walking, unlock self.globalLock = False ''' write destroyfunction ''' def destroy(self): #not accepting events self.ignoreAll() #destroying everything down if self.node != None: self.node.remove_node() #removing all tasks if self.movtask != 0: taskMgr.remove(self.movtask) self.movtask = 0 def face(self, direction): if direction == "left": self.hideAllSubnodes() self.sleft.show() if direction == "right": self.hideAllSubnodes() self.sright.show() if direction == "top" or direction == "up": #let's keep retrocompatibility self.hideAllSubnodes() self.stop.show() if direction == "down": self.hideAllSubnodes() self.sdown.show() def setCollisions(self, value): if value == True: b = self.node.getBounds().getRadius() self.cTrav = CollisionTraverser() self.collisionTube = CollisionSphere( b / 2, 0, b / 2, 0.035 * self.properties['hitboxscale']) self.collisionNode = CollisionNode('characterTube') self.collisionNode.addSolid(self.collisionTube) self.collisionNodeNp = self.node.attachNewNode(self.collisionNode) self.collisionHandler = CollisionHandlerQueue() self.cTrav.addCollider(self.collisionNodeNp, self.collisionHandler) if self.showCollisions == True or main.editormode: # Uncomment this line to see the collision rays self.collisionNodeNp.show() # Uncomment this line to show a visual representation of the # collisions occuring self.cTrav.showCollisions(render) else: b = self.node.getBounds().getRadius() self.collisionTube = CollisionSphere( b / 2, 0, b / 2, 0.035 * self.properties['hitboxscale']) #allowing playables to collide with npcs if self.isNPC == True: #TODO: fix because it's completely f****d up self.collisionNode = CollisionNode('characterTube') else: self.collisionNode = CollisionNode('characterNPCTube') self.collisionNode.addSolid(self.collisionTube) self.collisionNodeNp = self.node.attachNewNode(self.collisionNode) #set if camera has to effectively follow the character #while it moves def setFollowedByCamera(self, value): #camera follow if value: if self.currentlyfollowed != True: customCamera.follow(self) self.currentlyfollowed = True else: if self.currentlyfollowed != False: customCamera.dontFollow() self.currentlyfollowed = False def setPickCollisions(self, value): if value: print "setting pick collisions" b = self.node.getBounds().getRadius() self.pickCTrav = CollisionTraverser() self.pickCollisionTube = CollisionSphere( b / 2, 0, b / 2, 0.035 * self.properties['hitboxscale'] + 0.01) self.pickCollisionNode = CollisionNode('characterPickTube') self.pickCollisionNode.addSolid(self.pickCollisionTube) self.pickCollisionNodeNp = NodePath(self.pickCollisionNode) self.pickCollisionNodeNp.reparentTo(self.node) self.pickCollisionHandler = CollisionHandlerQueue() self.pickCTrav.addCollider(self.pickCollisionNodeNp, self.pickCollisionHandler) if self.showCollisions == True: # Uncomment this line to see the collision rays self.pickCollisionNodeNp.show() # Uncomment this line to show a visual representation of the # collisions occuring self.pickCTrav.showCollisions(render) else: #dereferincing all pick colliders (must be done in order not to collide onto NPCs) self.pickCTrav = None self.pickCollisionTube = None self.pickCollisionNode = None self.pickCollisionNodeNp = None self.pickCollisionHandler = None #used to set playability in real time #useful when we want to switch context/scripted scenes def setPlayable(self, value): if self.isNPC != False: if value == True: #down events self.accept("arrow_left", self.arrowLeftDown) self.accept("arrow_right", self.arrowRightDown) self.accept("arrow_up", self.arrowUpDown) self.accept("arrow_down", self.arrowDownDown) #up events self.accept("arrow_left-up", self.arrowLeftUp) self.accept("arrow_right-up", self.arrowRightUp) self.accept("arrow_up-up", self.arrowUpUp) self.accept("arrow_down-up", self.arrowDownUp) self.accept("space", self.spaceDown) self.node.setTag("playable", "true") self.setFollowedByCamera(True) self.accept("pauseGameplay", self.setPlayable, [False]) #can pause play else: self.ignoreAll() self.node.setTag("playable", "false") self.setFollowedByCamera(False) self.resetMovement() #reset every movement happening self.accept("resumeGameplay", self.setPlayable, [True]) #can resume play if not NPC #estimate loading time 4 seconds... lol... UPDATE: seems fixed in newer panda versions, inspect def showAllSubnodes(self): self.wtop.show() self.wdown.show() self.wleft.show() self.wright.show() self.stop.show() self.sdown.show() self.sleft.show() self.sright.show() def hideAllSubnodes(self): self.wtop.hide() self.wdown.hide() self.wleft.hide() self.wright.hide() self.stop.hide() self.sdown.hide() self.sleft.hide() self.sright.hide() def setMovement(self, value): if value == True: if self.movtask == 0: self.movtask = taskMgr.add(self.moveCharacter, "moveCharacterTask") if value == False: if self.movtask != 0: if len(self.currentlydown) == 0: taskMgr.remove(self.movtask) self.movtask = 0 ''' reset every movement actually happening ''' def resetMovement(self): if self.leftdown == True: self.face("left") if self.rightdown == True: self.face("right") if self.downdown == True: self.face("down") if self.topdown == True: self.face("top") self.leftdown = False self.rightdown = False self.downdown = False self.topdown = False self.currentlydown = [] self.setMovement(False) def setAnim(self, direction=''): self.hideAllSubnodes() if direction == '': if len(self.currentlydown) > 0: if self.currentlydown[-1] == 'left': self.wleft.show() if self.currentlydown[-1] == 'right': self.wright.show() if self.currentlydown[-1] == 'top': self.wtop.show() if self.currentlydown[-1] == 'down': self.wdown.show() else: if direction == 'left': self.wleft.show() if direction == 'right': self.wright.show() if direction == 'up': self.wtop.show() if direction == 'down': self.wdown.show() #pick request function def spaceDown(self): self.pickRequest = True #movement related functions def arrowLeftDown(self): #track key down self.leftdown = True self.setMovement(True) #show changes to screen self.currentlydown.append("left") self.setAnim() def arrowLeftUp(self): self.leftdown = False self.setMovement(False) #show changes to screen if len(self.currentlydown) == 1: self.hideAllSubnodes() self.sleft.show() if "left" in self.currentlydown: self.currentlydown.remove("left") if len(self.currentlydown) > 0: self.setAnim() def arrowRightDown(self): #track key down self.rightdown = True self.setMovement(True) #show changes to screen self.currentlydown.append("right") self.setAnim() def arrowRightUp(self): self.setMovement(False) self.rightdown = False #show changes to screen if len(self.currentlydown) == 1: self.hideAllSubnodes() self.sright.show() if "right" in self.currentlydown: self.currentlydown.remove("right") if len(self.currentlydown) > 0: self.setAnim() def arrowDownDown(self): #track key down self.downdown = True self.setMovement(True) #show changes to screen self.currentlydown.append("down") self.setAnim() def arrowDownUp(self): self.downdown = False self.setMovement(False) #show changes to screen if len(self.currentlydown) == 1: self.hideAllSubnodes() self.sdown.show() if "down" in self.currentlydown: self.currentlydown.remove("down") if len(self.currentlydown) > 0: self.setAnim() def arrowUpDown(self): #track key down self.topdown = True self.setMovement(True) #show changes to screen self.currentlydown.append("top") self.setAnim() def arrowUpUp(self): self.topdown = False self.setMovement(False) #show changes to screen if len(self.currentlydown) == 1: self.hideAllSubnodes() self.stop.show() if "top" in self.currentlydown: self.currentlydown.remove("top") if len(self.currentlydown) > 0: self.setAnim() def moveCharacter(self, task): dt = globalClock.getDt() if len(self.currentlydown) > 0: if self.currentlydown[-1] == 'left': self.node.setX(self.node.getX() - 1 * dt * self.properties['speed']) if self.currentlydown[-1] == 'right': self.node.setX(self.node.getX() + 1 * dt * self.properties['speed']) if self.currentlydown[-1] == 'top': self.node.setZ(self.node.getZ() + 1 * dt * self.properties['speed']) if self.currentlydown[-1] == 'down': self.node.setZ(self.node.getZ() - 1 * dt * self.properties['speed']) #check collisions if self.cTrav != None: self.cTrav.traverse(render) if self.pickCTrav != None: self.pickCTrav.traverse(render) #entries python list entries = list(self.collisionHandler.getEntries()) pickentries = list(self.pickCollisionHandler.getEntries()) for e in entries[:]: if e.getIntoNodePath().getName() == "characterPickTube": entries.remove(e) for e in pickentries[:]: if e.getIntoNodePath().getName() == "characterTube": pickentries.remove(e) if len(entries) == 0: self.lastpos = self.node.getPos() else: sp = entries[0].getSurfacePoint(self.node) #surface point objectNode = entries[0].getIntoNodePath().getParent( ) #into object node groundNode = entries[0].getIntoNodePath() #into object node if objectNode.hasTag("collideandwalk"): if objectNode.getTag("collideandwalk") != "yes": self.node.setPos(self.lastpos) else: self.node.setPos(self.lastpos) #if node is a real object (not a wall) if objectNode.hasTag("avoidable"): if objectNode.getTag( "avoidable" ) == "true": #see if object is intelligently avoidable if objectNode.hasTag("xscaled") and objectNode.hasTag( "yscaled"): if len( self.currentlydown ) > 0: #at least 1, avoids list index out of range exception if self.currentlydown[ -1] == 'left' or self.currentlydown[ -1] == 'right': #TODO: fix the shiet, not always working bottomObjPos = objectNode.getZ() - ( float(objectNode.getTag("yscaled")) / 2) topObjPos = objectNode.getZ() + ( float(objectNode.getTag("yscaled")) / 2) if self.node.getZ() < bottomObjPos: self.node.setZ(self.node.getZ() - 1 * dt * self.properties['speed']) if self.node.getZ() > topObjPos: self.node.setZ(self.node.getZ() + 1 * dt * self.properties['speed']) pass if self.currentlydown[ -1] == 'top' or self.currentlydown[ -1] == 'down': leftObjPos = objectNode.getX() - ( float(objectNode.getTag("xscaled")) / 2) rightObjPos = objectNode.getX() + ( float(objectNode.getTag("xscaled")) / 2) if self.node.getX() < leftObjPos: self.node.setX(self.node.getX() - 1 * dt * self.properties['speed']) if self.node.getX() > rightObjPos: self.node.setX(self.node.getX() + 1 * dt * self.properties['speed']) self.lastpos = self.node.getPos() for entry in entries: objectNode = entry.getIntoNodePath().getParent() onWalked = objectNode.getTag("onWalked") if len(onWalked) > 0: eval(onWalked) #oh lol, danger detected here evaluatedOnce = False if self.pickRequest == True: for entry in pickentries: objectNode = entry.getIntoNodePath().getParent() onPicked = objectNode.getTag("onPicked") if len(onPicked) > 0 and evaluatedOnce == False: eval(onPicked) #oh lol, danger detected again here evaluatedOnce = True else: if hasattr(objectNode.getPythonTag('gamenode'), 'name'): print "WARNING: picking on this object is not defined: ", objectNode.getPythonTag( 'gamenode').name print "X: ", objectNode.getX() print "Y: ", objectNode.getZ() self.pickRequest = False #resetting request #this is needed for empty pick if self.pickRequest == True: self.pickRequest = False #resetting request return Task.cont def getWorldPos(self): return self.node.getPos(render) def setX(self, x): self.node.setX(x) self.lastpos.setX(x) def setY(self, y): self.node.setZ(y) self.lastpos.setZ(y) #here for polymorph def getTileX(self): return self.parent.getX() #here for polymorph def getTileY(self): return self.parent.getY()
class Raycaster(Entity): def __init__(self): super().__init__(name='raycaster', eternal=True) self._picker = CollisionTraverser() # Make a traverser self._pq = CollisionHandlerQueue() # Make a handler self._pickerNode = CollisionNode('raycaster') self._pickerNode.set_into_collide_mask(0) self._pickerNP = self.attach_new_node(self._pickerNode) self._picker.addCollider(self._pickerNP, self._pq) self._pickerNP.show() def distance(self, a, b): return sqrt(sum((a - b)**2 for a, b in zip(a, b))) def raycast(self, origin, direction=(0, 0, 1), distance=inf, traverse_target=scene, ignore=list(), debug=False): self.position = origin self.look_at(self.position + direction) self._pickerNode.clearSolids() # if thickness == (0,0): if distance == inf: ray = CollisionRay() ray.setOrigin(Vec3(0, 0, 0)) # ray.setDirection(Vec3(0,1,0)) ray.setDirection(Vec3(0, 0, 1)) else: # ray = CollisionSegment(Vec3(0,0,0), Vec3(0,distance,0)) ray = CollisionSegment(Vec3(0, 0, 0), Vec3(0, 0, distance)) self._pickerNode.addSolid(ray) if debug: self._pickerNP.show() else: self._pickerNP.hide() self._picker.traverse(traverse_target) if self._pq.get_num_entries() == 0: self.hit = HitInfo(hit=False) return self.hit ignore += tuple([e for e in scene.entities if not e.collision]) self._pq.sort_entries() self.entries = [ # filter out ignored entities e for e in self._pq.getEntries() if e.get_into_node_path().parent not in ignore ] if len(self.entries) == 0: self.hit = HitInfo(hit=False) return self.hit self.collision = self.entries[0] nP = self.collision.get_into_node_path().parent point = self.collision.get_surface_point(nP) # point = Vec3(point[0], point[2], point[1]) point = Vec3(point[0], point[1], point[2]) world_point = self.collision.get_surface_point(render) # world_point = Vec3(world_point[0], world_point[2], world_point[1]) world_point = Vec3(world_point[0], world_point[1], world_point[2]) hit_dist = self.distance(self.world_position, world_point) if nP.name.endswith('.egg'): nP = nP.parent self.hit = HitInfo(hit=True) for e in scene.entities: if e == nP: # print('cast nP to Entity') self.hit.entity = e self.hit.point = point self.hit.world_point = world_point self.hit.distance = hit_dist self.hit.normal = Vec3(*self.collision.get_surface_normal( self.collision.get_into_node_path().parent).normalized()) self.hit.world_normal = Vec3( *self.collision.get_surface_normal(render).normalized()) return self.hit self.hit = HitInfo(hit=False) return self.hit def boxcast(self, origin, direction=(0, 0, 1), distance=9999, thickness=(1, 1), traverse_target=scene, ignore=list(), debug=False): if isinstance(thickness, (int, float, complex)): thickness = (thickness, thickness) temp = Entity(position=origin, model='cube', origin_z=-.5, scale=Vec3(abs(thickness[0]), abs(thickness[1]), abs(distance)), collider='box', color=color.white33, always_on_top=debug, visible=debug) temp.look_at(origin + direction) hit_info = temp.intersects(traverse_target=traverse_target, ignore=ignore) if debug: temp.collision = False destroy(temp, delay=.1) else: destroy(temp) return hit_info
class Mouse(): def __init__(self): self.enabled = False self.locked = False self.position = Vec3(0, 0, 0) self.delta = Vec3(0, 0, 0) self.prev_x = 0 self.prev_y = 0 self.start_x = 0 self.start_y = 0 self.velocity = Vec3(0, 0, 0) self.prev_click_time = time.time() self.double_click_distance = .5 self.hovered_entity = None # returns the closest hovered entity with a collider. self.left = False self.right = False self.middle = False self.delta_drag = Vec3(0, 0, 0) self.update_step = 1 self.traverse_target = scene self._i = 0 self._mouse_watcher = None self._picker = CollisionTraverser() # Make a traverser self._pq = CollisionHandlerQueue() # Make a handler self._pickerNode = CollisionNode('mouseRay') self._pickerNP = camera.attach_new_node(self._pickerNode) self._pickerRay = CollisionRay() # Make our ray self._pickerNode.addSolid(self._pickerRay) self._picker.addCollider(self._pickerNP, self._pq) self._pickerNode.set_into_collide_mask(0) self.raycast = True self.collision = None self.collisions = list() self.enabled = True @property def x(self): if not self._mouse_watcher.has_mouse(): return 0 return self._mouse_watcher.getMouseX( ) / 2 * window.aspect_ratio # same space as ui stuff @x.setter def x(self, value): self.position = (value, self.y) @property def y(self): if not self._mouse_watcher.has_mouse(): return 0 return self._mouse_watcher.getMouseY() / 2 @y.setter def y(self, value): self.position = (self.x, value) @property def position(self): return Vec3(self.x, self.y, 0) @position.setter def position(self, value): base.win.move_pointer( 0, round(value[0] + (window.size[0] / 2) + (value[0] / 2 * window.size[0]) * 1.124), # no idea why I have * with 1.124 round(value[1] + (window.size[1] / 2) - (value[1] * window.size[1])), ) def __setattr__(self, name, value): if name == 'visible': window.set_cursor_hidden(not value) application.base.win.requestProperties(window) if name == 'locked': try: object.__setattr__(self, name, value) window.set_cursor_hidden(value) if value: window.set_mouse_mode(window.M_relative) else: window.set_mouse_mode(window.M_absolute) application.base.win.requestProperties(window) except: pass try: super().__setattr__(name, value) # return except: pass def input(self, key): if not self.enabled: return if key.endswith('mouse down'): self.start_x = self.x self.start_y = self.y elif key.endswith('mouse up'): self.delta_drag = Vec3(self.x - self.start_x, self.y - self.start_y, 0) if key == 'left mouse down': self.left = True if self.hovered_entity: if hasattr(self.hovered_entity, 'on_click'): self.hovered_entity.on_click() for s in self.hovered_entity.scripts: if hasattr(s, 'on_click'): s.on_click() # double click if time.time( ) - self.prev_click_time <= self.double_click_distance: base.input('double click') if self.hovered_entity: if hasattr(self.hovered_entity, 'on_double_click'): self.hovered_entity.on_double_click() for s in self.hovered_entity.scripts: if hasattr(s, 'on_double_click'): s.on_double_click() self.prev_click_time = time.time() if key == 'left mouse up': self.left = False if key == 'right mouse down': self.right = True if key == 'right mouse up': self.right = False if key == 'middle mouse down': self.middle = True if key == 'middle mouse up': self.middle = False def update(self): if not self.enabled or not self._mouse_watcher.has_mouse(): self.velocity = Vec3(0, 0, 0) return self.moving = self.x + self.y != self.prev_x + self.prev_y if self.moving: if self.locked: self.velocity = self.position self.position = (0, 0) else: self.velocity = Vec3(self.x - self.prev_x, (self.y - self.prev_y) / window.aspect_ratio, 0) else: self.velocity = Vec3(0, 0, 0) if self.left or self.right or self.middle: self.delta = Vec3(self.x - self.start_x, self.y - self.start_y, 0) self.prev_x = self.x self.prev_y = self.y self._i += 1 if self._i < self.update_step: return # collide with ui self._pickerNP.reparent_to(scene.ui_camera) self._pickerRay.set_from_lens(camera._ui_lens_node, self.x * 2 / window.aspect_ratio, self.y * 2) self._picker.traverse(camera.ui) if self._pq.get_num_entries() > 0: # print('collided with ui', self._pq.getNumEntries()) self.find_collision() return # collide with world self._pickerNP.reparent_to(camera) self._pickerRay.set_from_lens(scene.camera.lens_node, self.x * 2 / window.aspect_ratio, self.y * 2) try: self._picker.traverse(self.traverse_target) except: # print('error: mouse._picker could not traverse', self.traverse_target) return if self._pq.get_num_entries() > 0: self.find_collision() else: # print('mouse miss', base.render) # unhover all if it didn't hit anything for entity in scene.entities: if hasattr(entity, 'hovered') and entity.hovered: entity.hovered = False self.hovered_entity = None if hasattr(entity, 'on_mouse_exit'): entity.on_mouse_exit() for s in entity.scripts: if hasattr(s, 'on_mouse_exit'): s.on_mouse_exit() @property def normal(self): if not self.collision: return None return self.collision.normal @property def world_normal(self): if not self.collision: return None return self.collision.world_normal @property def point(self): # returns the point hit in local space if self.collision: return self.collision.point return None @property def world_point(self): if self.collision: return self.collision.world_point return None def find_collision(self): self.collisions = list() self.collision = None if not self.raycast or self._pq.get_num_entries() == 0: self.unhover_everything_not_hit() return False self._pq.sortEntries() for entry in self._pq.getEntries(): for entity in scene.entities: if entry.getIntoNodePath( ).parent == entity and entity.collision: if entity.collision: hit = HitInfo( hit=entry.collided(), entity=entity, distance=distance(entry.getSurfacePoint(scene), camera.getPos()), point=entry.getSurfacePoint(entity), world_point=entry.getSurfacePoint(scene), normal=entry.getSurfaceNormal(entity), world_normal=entry.getSurfaceNormal(scene), ) self.collisions.append(hit) break if self.collisions: self.collision = self.collisions[0] self.hovered_entity = self.collision.entity if not self.hovered_entity.hovered: self.hovered_entity.hovered = True if hasattr(self.hovered_entity, 'on_mouse_enter'): self.hovered_entity.on_mouse_enter() for s in self.hovered_entity.scripts: if hasattr(s, 'on_mouse_enter'): s.on_mouse_enter() self.unhover_everything_not_hit() def unhover_everything_not_hit(self): for e in scene.entities: if e == self.hovered_entity: continue if e.hovered: e.hovered = False if hasattr(e, 'on_mouse_exit'): e.on_mouse_exit() for s in e.scripts: if hasattr(s, 'on_mouse_exit'): s.on_mouse_exit()
class CogdoFlyingCameraManager: def __init__(self, cam, parent, player, level): self._toon = player.toon self._camera = cam self._parent = parent self._player = player self._level = level self._enabled = False def enable(self): if self._enabled: return self._toon.detachCamera() self._prevToonY = 0.0 levelBounds = self._level.getBounds() l = Globals.Camera.LevelBoundsFactor self._bounds = ((levelBounds[0][0] * l[0], levelBounds[0][1] * l[0]), (levelBounds[1][0] * l[1], levelBounds[1][1] * l[1]), (levelBounds[2][0] * l[2], levelBounds[2][1] * l[2])) self._lookAtZ = self._toon.getHeight() + Globals.Camera.LookAtToonHeightOffset self._camParent = NodePath('CamParent') self._camParent.reparentTo(self._parent) self._camParent.setPos(self._toon, 0, 0, 0) self._camParent.setHpr(180, Globals.Camera.Angle, 0) self._camera.reparentTo(self._camParent) self._camera.setPos(0, Globals.Camera.Distance, 0) self._camera.lookAt(self._toon, 0, 0, self._lookAtZ) self._cameraLookAtNP = NodePath('CameraLookAt') self._cameraLookAtNP.reparentTo(self._camera.getParent()) self._cameraLookAtNP.setPosHpr(self._camera.getPos(), self._camera.getHpr()) self._levelBounds = self._level.getBounds() self._enabled = True self._frozen = False self._initCollisions() def _initCollisions(self): self._camCollRay = CollisionRay() camCollNode = CollisionNode('CameraToonRay') camCollNode.addSolid(self._camCollRay) camCollNode.setFromCollideMask(OTPGlobals.WallBitmask | OTPGlobals.CameraBitmask | ToontownGlobals.FloorEventBitmask | ToontownGlobals.CeilingBitmask) camCollNode.setIntoCollideMask(0) self._camCollNP = self._camera.attachNewNode(camCollNode) self._camCollNP.show() self._collOffset = Vec3(0, 0, 0.5) self._collHandler = CollisionHandlerQueue() self._collTrav = CollisionTraverser() self._collTrav.addCollider(self._camCollNP, self._collHandler) self._betweenCamAndToon = {} self._transNP = NodePath('trans') self._transNP.reparentTo(render) self._transNP.setTransparency(True) self._transNP.setAlphaScale(Globals.Camera.AlphaBetweenToon) self._transNP.setBin('fixed', 10000) def _destroyCollisions(self): self._collTrav.removeCollider(self._camCollNP) self._camCollNP.removeNode() del self._camCollNP del self._camCollRay del self._collHandler del self._collOffset del self._betweenCamAndToon self._transNP.removeNode() del self._transNP def freeze(self): self._frozen = True def unfreeze(self): self._frozen = False def disable(self): if not self._enabled: return self._destroyCollisions() self._camera.wrtReparentTo(render) self._cameraLookAtNP.removeNode() del self._cameraLookAtNP self._camParent.removeNode() del self._camParent del self._prevToonY del self._lookAtZ del self._bounds del self._frozen self._enabled = False def update(self, dt = 0.0): self._updateCam(dt) self._updateCollisions() def _updateCam(self, dt): toonPos = self._toon.getPos() camPos = self._camParent.getPos() x = camPos[0] z = camPos[2] toonWorldX = self._toon.getX(render) maxX = Globals.Camera.MaxSpinX toonWorldX = clamp(toonWorldX, -1.0 * maxX, maxX) spinAngle = Globals.Camera.MaxSpinAngle * toonWorldX * toonWorldX / (maxX * maxX) newH = 180.0 + spinAngle self._camParent.setH(newH) spinAngle = spinAngle * (pi / 180.0) distBehindToon = Globals.Camera.SpinRadius * cos(spinAngle) distToRightOfToon = Globals.Camera.SpinRadius * sin(spinAngle) d = self._camParent.getX() - clamp(toonPos[0], *self._bounds[0]) if abs(d) > Globals.Camera.LeewayX: if d > Globals.Camera.LeewayX: x = toonPos[0] + Globals.Camera.LeewayX else: x = toonPos[0] - Globals.Camera.LeewayX x = self._toon.getX(render) + distToRightOfToon boundToonZ = min(toonPos[2], self._bounds[2][1]) d = z - boundToonZ if d > Globals.Camera.MinLeewayZ: if self._player.velocity[2] >= 0 and toonPos[1] != self._prevToonY or self._player.velocity[2] > 0: z = boundToonZ + d * INVERSE_E ** (dt * Globals.Camera.CatchUpRateZ) elif d > Globals.Camera.MaxLeewayZ: z = boundToonZ + Globals.Camera.MaxLeewayZ elif d < -Globals.Camera.MinLeewayZ: z = boundToonZ - Globals.Camera.MinLeewayZ if self._frozen: y = camPos[1] else: y = self._toon.getY(render) - distBehindToon self._camParent.setPos(x, smooth(camPos[1], y), smooth(camPos[2], z)) if toonPos[2] < self._bounds[2][1]: h = self._cameraLookAtNP.getH() if d >= Globals.Camera.MinLeewayZ: self._cameraLookAtNP.lookAt(self._toon, 0, 0, self._lookAtZ) elif d <= -Globals.Camera.MinLeewayZ: self._cameraLookAtNP.lookAt(self._camParent, 0, 0, self._lookAtZ) self._cameraLookAtNP.setHpr(h, self._cameraLookAtNP.getP(), 0) self._camera.setHpr(smooth(self._camera.getHpr(), self._cameraLookAtNP.getHpr())) self._prevToonY = toonPos[1] def _updateCollisions(self): pos = self._toon.getPos(self._camera) + self._collOffset self._camCollRay.setOrigin(pos) direction = -Vec3(pos) direction.normalize() self._camCollRay.setDirection(direction) self._collTrav.traverse(render) nodesInBetween = {} if self._collHandler.getNumEntries() > 0: self._collHandler.sortEntries() for entry in self._collHandler.getEntries(): name = entry.getIntoNode().getName() if name.find('col_') >= 0: np = entry.getIntoNodePath().getParent() if np not in nodesInBetween: nodesInBetween[np] = np.getParent() for np in nodesInBetween.keys(): if np in self._betweenCamAndToon: del self._betweenCamAndToon[np] else: np.setTransparency(True) np.wrtReparentTo(self._transNP) if np.getName().find('lightFixture') >= 0: np.find('**/*floor_mesh').hide() elif np.getName().find('platform') >= 0: np.find('**/*Floor').hide() for np, parent in self._betweenCamAndToon.items(): np.wrtReparentTo(parent) np.setTransparency(False) if np.getName().find('lightFixture') >= 0: np.find('**/*floor_mesh').show() elif np.getName().find('platform') >= 0: np.find('**/*Floor').show() self._betweenCamAndToon = nodesInBetween