class World(DirectObject): def __init__(self): base.win.setClearColor(Vec4(0, 0, 0, 1)) # enable physics (and particle) engine self.throwMode = False self.freelook = False self.score = OnscreenText('0', pos=(-1.32, 0.9), fg=(1, 1, 1, 1), bg=(0, 0, 0, 0.5), scale=0.1, align=TextNode.ALeft) # Load the environment in which Eve will walk. Set its parent # to the render variable so that it is a top-lplayerl node. self.env = loader.loadModel('models/world/world.egg.pz') self.env.reparentTo(render) self.env.setPos(0, 0, 0) self.createCollisionHandlers() # Create an Actor instance for Eve. We also specify the animation # models that we want to use as a dictionary, where we can use to # keys to refer to the animations later on. The start point of Eve # is hardcoded in the world model somewhere, so we look that up. self.player = Eve('Eve', self, self.env.find('**/start_point').getPos()) #self.player.nodePath.setZ(self.player.nodePath.getZ() + 10) self.player.nodePath.reparentTo(render) # Create a floater object that always floats 2 units above Eve. # We make sure that it is attached to Eve by reparenting it to # Eve's object instance. self.floater = NodePath(PandaNode('floater')) self.floater.reparentTo(self.player.nodePath) self.floater.setZ(self.floater.getZ() + 2) # load baseball self.baseball = Baseball('baseball', self, self.player.nodePath.getPos()) self.baseball.nodePath.reparentTo(render) self.player.pickUpItem(self.baseball) # Load the panda bear self.panda = Panda('panda', self, self.player.nodePath.getPos()) self.panda.nodePath.reparentTo(render) # Disable controlling the camera using the mouse. Note that this does # not disable the mouse completely, it merely disables the camera # movement by mouse. base.disableMouse() self.hideMouseCursor() # Set the initial position for the camera as X, Y and Z values. base.camera.setPos(self.player.nodePath.getX(), self.player.nodePath.getY() + 10, 2) # Disable modifier button compound events. base.mouseWatcherNode.setModifierButtons(ModifierButtons()) base.buttonThrowers[0].node().setModifierButtons(ModifierButtons()) # Register any control callbacks. self.accept('escape', sys.exit) self.accept('d', self.dropItem) self.accept('f', self.toggleFullscreen) self.accept('space', self.enterThrowMode) self.accept('space-up', self.leaveThrowMode) # Also make sure that we can, at any time, request the state (pressed # or not) for these keys. self.keys = keys.KeyStateManager() self.keys.registerKeys({ 'arrow_left': 'left', 'arrow_right': 'right', 'arrow_up': 'forward', 'arrow_down': 'backward', 'shift': 'shift', 'r': 'reset' }) self.mouse = mouse.MousePointerManager(0) # Schedule the move method to be executed in the game's main loop. taskMgr.add(self.update, 'update') def hideMouseCursor(self): props = WindowProperties() props.setCursorHidden(True) base.win.requestProperties(props) def toggleFullscreen(self): props = WindowProperties() props.setFullscreen(not base.win.getProperties().getFullscreen()) base.win.requestProperties(props) def enableFreelook(self): self.freelook = True # Make sure we reset the MouseMovementManager's last known mouse position, # so we don't get a huge delta on the first attempt. self.mouse.reset() base.camera.setP(0) def disableFreelook(self): self.freelook = False def createCollisionHandlers(self): # Create a new collision traverser instance. We will use this to determine # if any collisions occurred after performing movement. self.cTrav = CollisionTraverser() camGroundRay = CollisionRay() camGroundRay.setOrigin(0, 0, 1000) camGroundRay.setDirection(0, 0, -1) camGroundCol = CollisionNode('camRay') camGroundCol.addSolid(camGroundRay) camGroundCol.setFromCollideMask(BitMask32.bit(0)) camGroundCol.setIntoCollideMask(BitMask32.allOff()) camGroundColNp = base.camera.attachNewNode(camGroundCol) self.camGroundHandler = CollisionHandlerQueue() self.cTrav.addCollider(camGroundColNp, self.camGroundHandler) # register the collision pusher self.pusher = CollisionHandlerPusher() # register collision event pattern names self.pusher.addInPattern('col-%fn-into') def update(self, task): # get the time passed since the last frame timePassed = globalClock.getDt() # update player self.player.forceMove(timePassed) self.panda.forceMove(timePassed) # Do collision detection. This iterates all the collider nodes self.cTrav.traverse(render) # check if player's move is valid self.player.validateMove() self.panda.validateMove() # Set the initial position for the camera as X, Y and Z values. base.camera.setPos(self.player.nodePath.getPos()) if self.throwMode: # Position the camera a bit above the ground. base.camera.setZ(base.camera, 1.5) if self.freelook: mx, my = self.mouse.getDelta() h = -mx * 0.1 p = -my * 0.1 base.camera.setHpr(base.camera, h, p, 0) self.player.nodePath.setH(self.player.nodePath, h) else: # Set the heading, pitch and roll of the camera. base.camera.setHpr(self.player.nodePath.getHpr()) else: # Set the heading, pitch and roll of the camera. base.camera.setHpr(self.player.nodePath.getHpr()) # Position the camera somewhat behind the player. base.camera.setY(base.camera, 10) # Make sure the camera is above the ground. camGroundEntry = self.getGroundEntry(self.camGroundHandler) if camGroundEntry is not None and camGroundEntry.getIntoNode( ).getName() == 'terrain': base.camera.setZ( camGroundEntry.getSurfacePoint(render).getZ() + 1.5) # Let the camera look at the floater object above Eve. base.camera.lookAt(self.floater) return Task.cont def dropItem(self): self.player.dropItem() def getGroundEntry(self, collisionHandler): # Put all the collision entries into a Python list so we can sort it, # properly. entries = [] for i in range(collisionHandler.getNumEntries()): entries.append(collisionHandler.getEntry(i)) # Sort the list by the collision points' Z values, making sure the # highest value ends up at the front of the list. entries.sort(lambda x, y: cmp( y.getSurfacePoint(render).getZ(), x.getSurfacePoint(render).getZ())) if len(entries) > 0: return entries[0] else: return None def enterThrowMode(self): self.throwMode = True self.player.enterStrafeMode() self.enableFreelook() def leaveThrowMode(self): self.throwMode = False self.player.leaveStrafeMode() self.disableFreelook()
class Unit(Actor): gravity = 30 def __init__(self, models = None, anims = None, sphereString = "**/CollisionSphere", game = None, xStart = 0, yStart = 0, zStart = 0, radius = 3): Actor.__init__(self, models, anims) self.game = game self.health = 10 self.heightOffset = 3 #set up the position self.setPos(xStart, yStart, zStart) self.prevPosition = self.getPos() #self.lastPosition = Point3() self.vel = Vec3() self.accel = Vec3(0, 0, -Unit.gravity) #define the position that will be treated as the center of the map self.wCenter = Point3(0, 0, 0) #the radius of the sphere around this self.radius = 3.5 #the base damage this unit deals upon collision self.collisionAttackPower = 2.5 #set up Panda's collisions #first the pusher cSphere = CollisionSphere((0, 0, 1), 2) cNode = CollisionNode("unit") cNode.addSolid(cSphere) cNode.setIntoCollideMask(BitMask32(PLAYER_ENEMY_OBJECTS)) cNode.setFromCollideMask(BitMask32(PLAYER_ENEMY_OBJECTS)) self.collisionNodePath = self.attachNewNode(cNode) #self.collisionNodePath.show() #set pattern for event sent on collision # "%in" is substituted with the name of the into object, "%fn" is substituted with the name of the from object #do the collision pusher self.collisionPusher = CollisionHandlerPusher() self.collisionPusher.addCollider(self.collisionNodePath, self) self.collisionPusher.addInPattern("%fn-into-%in") self.collisionPusher.addOutPattern("fn-out-%in") game.cTrav.addCollider(self.collisionNodePath, self.collisionPusher) #check for colllisions with the ground self.groundRay = CollisionRay() self.groundRay.setOrigin(0, 0, 4000) self.groundRay.setDirection(0, 0, -1) self.groundCol = CollisionNode('unitRay') self.groundCol.addSolid(self.groundRay) self.groundCol.setFromCollideMask(BitMask32(TERRAIN_RAY_MASK)) self.groundCol.setIntoCollideMask(BitMask32.allOff()) self.groundColNode = self.attachNewNode(self.groundCol) self.groundHandler = CollisionHandlerQueue() game.cTrav.addCollider(self.groundColNode, self.groundHandler) #can be thought of as the inverse of the unit's mass self.accelMultiplier = 45 self.friction = 1.7 self.disableFriction = False self.nodePath = None self.shootable = True #finally set the python tag self.setPythonTag("unit", self) def applyForceFrom(self, magnitude, sourcePosition): forceVector = self.getPos() - sourcePosition forceVector.normalize() forceVector *= magnitude self.applyForce(forceVector) def applyForce(self, forceVector): self.accel += forceVector * self.accelMultiplier def applyConstantVelocityFrom(self, magnitude, sourcePosition): velVector = self.getPos() - sourcePosition velVector.normalize() velVector *= magnitude self.applyConstantVelocity(velVector) def applyConstantVelocity(self, velVector): self.vel = velVector def takeDamage(self, num): self.health -= num if self.health <= 0: self.die() def die(self): self.game.actors[self.getName()] = None self.delete() def turn(self, magnitude): pass def update(self, time): self.vel += self.accel * time self.accel.set(0, 0, -Unit.gravity) if not self.disableFriction: self.vel -= self.vel * (self.friction * time) self.setFluidPos(self.getPos() + self.vel * time) self.setZ(max(-100, self.getZ())) def collideWithUnit(self, other): velDiff = self.vel - other.vel if velDiff.lengthSquared() > 450: Unit.takeDamage(self, other.collisionAttackPower) Unit.takeDamage(other, self.collisionAttackPower) self.vel *= 0.8 other.vel *= 0.8 def collideWithObstacle(self): if self.vel.lengthSquared() > 500: Unit.takeDamage(self, max(1, self.collisionAttackPower)) self.vel *= 0.5 def terrainCollisionCheck(self): entries = [] length = self.groundHandler.getNumEntries() for i in range(length): entry = self.groundHandler.getEntry(i) entries.append(entry) entries.sort(lambda x, y: cmp(y.getSurfacePoint(render).getZ(), x.getSurfacePoint(render).getZ())) if (len(entries) > 0): for entry in entries: if entry.getIntoNode().getName() == "craterCollisionPlane": zVal = entry.getSurfacePoint(render).getZ() if zVal >= MAX_HEIGHT:#apply a force toward the center self.applyForce(self.wCenter - Point3((self.getX() * GROUND_REPULSION_MULTIPLIER), (self.getY() * GROUND_REPULSION_MULTIPLIER), 0)) ''' this is a little bit hackish, what is done is that a force in the x and y direction is created proportional to your distance from the origin. this will only work effectively if the crater is xy centered in the environment ''' else: self.setZ(max(zVal, self.getZ())) break