class Camera: '''Stores and changes the camera position while detecting collision''' SPEED = 0 WALK = .3 SPRINT = 1 ROTATE = 2 WIDTH = .8 def __init__(self, x=0, y=0, z=0): '''Initializes everything, including sound''' self.pos_X = x self.pos_Y = y self.pos_Z = z self.start_pos=(x,y,z) self.rot_X = 0 self.rot_Y = 0 self.rot_Z = 0 self.mouse_x = 0 self.mouse_y = 0 self.keys = {} self.keys["w"] = False self.keys["a"] = False self.keys["s"] = False self.keys["d"] = False self.keys["shift"] = False self.aware = 5 self.points = 0 self.dead_timeout = 0 self.key_timeout = 0 self.treasure_timeout = 0 self.soundboard = GameSounds() self.footSound = self.soundboard.toSound("Sound/footsteps.wav") self.footSound.set_volume(0.5) self.collisionSound = self.soundboard.toSound("Sound/crashsound.wav") self.collisionSound.set_volume(1.5) self.pickSound = self.soundboard.toSound("Sound/picksound.wav") self.treasureSound = self.soundboard.toSound("Sound/cash.wav") self.zomSound = self.soundboard.toSound("Sound/zombie.wav") def renderCamera(self): '''Translates and rotates the camera to the correct position''' glRotatef(-self.rot_X , 1.0, 0.0, 0.0) glRotatef(-self.rot_Y , 0.0, 1.0, 0.0) glRotatef(-self.rot_Z , 0.0, 0.0, 1.0) glTranslatef(-self.pos_X, -self.pos_Y, -self.pos_Z) def move(self, objects): '''Controls the movement of the player.''' tmp_keys = self.keys.copy() #Check for sprint if tmp_keys["shift"] == True: self.SPEED = self.SPRINT else: self.SPEED = self.WALK moved = self.move_by_keys(tmp_keys, 1) if moved[0]: self.pos_X = moved[1] self.pos_Y = moved[2] self.pos_Z = moved[3] if self.check_collisions(objects): moved = self.move_by_keys(tmp_keys, -1) self.pos_X = moved[1] self.pos_Y = moved[2] self.pos_Z = moved[3] elif moved[0]: self.footSound.play() def rotate(self, x, y, z): '''Rotates by x, y, and z''' self.rot_X += x self.rot_Y += y self.rot_Z += z def strafe(self, amt): '''Strafes left or right, bassed on the angle''' tmp_Z = math.cos(self.rot_X*math.pi/180)*math.sin(-self.rot_Y*math.pi/180)*amt tmp_X = math.cos(self.rot_X*math.pi/180)*math.cos(self.rot_Y*math.pi/180)*amt return (tmp_X, tmp_Z) #Use to allow for change in height based on angle #self.pos_Y += math.cos(self.rot_X*math.pi/180)*math.sin(-self.rot_Z*math.pi/180)*amt def walk(self, amt): '''Walks forward and back based on the angle you're facing''' tmp_Z = math.cos(self.rot_X*math.pi/180)*math.cos(self.rot_Y*math.pi/180)*amt tmp_X = math.cos(self.rot_X*math.pi/180)*math.sin(self.rot_Y*math.pi/180)*amt return (tmp_X, tmp_Z) #Use to allow for change in height based on angle #self.pos_Y += math.cos(self.rot_Z*math.pi/180)*math.sin(-self.rot_X*math.pi/180)*amt def height(self, amt): '''Goes up or down. Always along the y axis''' self.pos_Y += amt def check_collisions(self, objects): '''Checks if object is within range of camera's aware variable. If so, a hit test is performed upon the object. Returns true upon detecting collision with any object''' for obj in objects: x2, y2, z2 = obj.get_pos() moved, tmp_x, tmp_y, tmp_z = self.move_by_keys(self.keys, 1) if obj.get_dist(self.pos_X, self.pos_Y, self.pos_Z) < self.aware: if self.hitTest(obj, tmp_x, tmp_y, tmp_z): return True else: if obj.get_type()=='zombie': if obj.get_dist(self.pos_X, self.pos_Y, self.pos_Z) < 5.5: self.zomSound.play() # Need a vector from the camera position to the zombie and check to see if there is an object in the way def hitTest(self, obj, x, y, z): '''Checks the given x, y, and z coordinates against the bounding box of the object, returns true if there is a collision''' w = obj.width for i in range(1,5): x2, z2 = self.get_sides(i) x += x2 z += z2 tmp_x, tmp_y, tmp_z = obj.get_pos() if x < tmp_x + w/2 and x > tmp_x - w/2 and z < tmp_z + w/2 and z > tmp_z - w/2: if obj.get_type()=='zombie': # If a zombie is encountered, you are transported back to the start self.pos_X=self.start_pos[0] self.pos_Y=self.start_pos[1] self.pos_Z=self.start_pos[2] self.dead_timeout = 50 elif obj.get_type()=='key': if not obj.get_has(): obj.get_key() self.key_timeout = 50 self.pickSound.play() obj.get_door().open() elif obj.get_type()=='door': if not obj.is_open(): self.collisionSound.play() return True elif obj.get_type()=='chest': if not obj.get_has(): obj.get_chest() self.treasure_timeout = 50 self.points += obj.get_points() self.treasureSound.play() else: self.collisionSound.play() return True return False def get_sides(self, side): '''Returns points of given side of bounding box''' tmp_X = 0 tmp_Z = 0 if side == 1: tmp_Z = math.cos(self.rot_X*math.pi/180)*math.cos(self.rot_Y*math.pi/180)*(-self.WIDTH/2) tmp_X = math.cos(self.rot_X*math.pi/180)*math.sin(self.rot_Y*math.pi/180)*(-self.WIDTH/2) if side == 2: tmp_Z = math.cos(self.rot_X*math.pi/180)*math.sin(-self.rot_Y*math.pi/180)*(self.WIDTH/2) tmp_X = math.cos(self.rot_X*math.pi/180)*math.cos(self.rot_Y*math.pi/180)*(self.WIDTH/2) if side == 3: tmp_Z = math.cos(self.rot_X*math.pi/180)*math.cos(self.rot_Y*math.pi/180)*(-self.WIDTH/2) tmp_X = math.cos(self.rot_X*math.pi/180)*math.sin(self.rot_Y*math.pi/180)*(-self.WIDTH/2) if side == 4: tmp_Z = math.cos(self.rot_X*math.pi/180)*math.sin(-self.rot_Y*math.pi/180)*(self.WIDTH/2) tmp_X = math.cos(self.rot_X*math.pi/180)*math.cos(self.rot_Y*math.pi/180)*(self.WIDTH/2) return (tmp_X, tmp_Z) #Use to allow for change in height based on angle #self.pos_Y += math.cos(self.rot_X*math.pi/180)*math.sin(-self.rot_Z*math.pi/180)*amt def move_by_keys(self, tmp_keys, direction): '''returns the projected coordinates of the player, and takes a direction -- 1 = forward, 2 = reverse, also returns true upon movement''' tmp_X = self.pos_X tmp_Y = self.pos_Y tmp_Z = self.pos_Z moved = False if tmp_keys['a']: x, z = self.strafe(-self.SPEED*direction) tmp_Z += z tmp_X += x moved = True if tmp_keys['d']: x, z = self.strafe(self.SPEED*direction) tmp_Z += z tmp_X += x moved = True if tmp_keys['w']: x, z = self.walk(-self.SPEED*direction) tmp_Z += z tmp_X += x moved = True if tmp_keys['s']: x, z = self.walk(self.SPEED*direction) tmp_Z += z tmp_X += x moved = True return (moved, tmp_X, tmp_Y, tmp_Z) '''def project_move_other(self): tmp_X = self.pos_X tmp_Y = self.pos_Y tmp_Z = self.pos_Z x, z = self.walk(self.SPEED) tmp_Z += z tmp_X += x return (tmp_X, tmp_Z)''' def get_camera_distance(self, x2, y2, z2): '''Returns the distance from given point''' tmp_x = (self.pos_X - x2)**2 tmp_y = (self.pos_Y - y2)**2 tmp_z = (self.pos_Z - z2)**2 return math.sqrt(tmp_x+tmp_y+tmp_z)
class RenderWorld: '''This is the class that renders maze. Camera angles are handled by Camera.py. ''' WINDOW_WIDTH = 700 WINDOW_HEIGHT = 700 MAP_SIZE =100 def __init__(self, file_name): '''Sets up camera, modes, lighting, sounds, and objects.''' self.set_up_graphics() self.makeLights() self.objects, self.player_loc = LoadWorld.load(file_name) self.camera = Camera(self.player_loc[0],0,self.player_loc[1]) glClearColor(.529,.8078,.980,0) glutIdleFunc(self.display) glutDisplayFunc(self.display) glutIgnoreKeyRepeat(GLUT_KEY_REPEAT_OFF) glutKeyboardFunc(self.keyPressed) glutKeyboardUpFunc(self.keyUp) glutSetCursor(GLUT_CURSOR_NONE) glutPassiveMotionFunc(self.mouseMove) self.floor_texture = Image.open("Graphics/checkerboard.bmp") self.ix, self.iy = self.floor_texture.size self.floor = self.floor_texture.tostring("raw", "RGBX", 0, -1) self.door = Model('Graphics/basicdoor.obj','door') self.key = Model('Graphics/Key.obj', 'key') self.zombie = Model('Graphics/zombie.obj', 'zombie')#try zombie5.obj for fun sometime self.chest = Model('Graphics/treasure.obj', 'chest') self.soundboard = GameSounds() self.footSound = self.soundboard.toSound("Sound/footsteps.wav") self.collisionSound = self.soundboard.toSound("Sound/crashsound.wav") self.pickSound = self.soundboard.toSound("Sound/picksound.wav") self.zomSound = self.soundboard.toSound("Sound/zombie.wav") self.fanSound = self.soundboard.toSound("Sound/fanfare.wav") self.soundboard.loadMusic("Sound/music.wav") self.soundboard.playMusic() self.zomstart = time() glutMainLoop() def set_up_graphics(self): '''Sets up OpenGL to provide double buffering, RGB coloring, depth testing, the correct window size, perspective rendering/fulcrum clipping, and a title.''' glutInit() glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH) glutInitWindowSize(self.WINDOW_WIDTH, self.WINDOW_HEIGHT) glutCreateWindow('Mazeworld!') glMatrixMode(GL_PROJECTION) gluPerspective(45,1,.15,100) glMatrixMode(GL_MODELVIEW) glEnable(GL_DEPTH_TEST) def makeLights(self): '''Sets up the light sources and their positions. We have an ambient light and two sets of specular/diffuse lights.''' self.diffuse_pos1 = (50,5,0,1) glLightfv(GL_LIGHT0, GL_DIFFUSE, (.5, .5, .5, 1)) glLightfv(GL_LIGHT0, GL_POSITION, self.diffuse_pos1) glLightfv(GL_LIGHT1, GL_AMBIENT, (.2, .2, .2, 1)) glLightfv(GL_LIGHT1, GL_POSITION, (0, 0, 1, 0)) glLightfv(GL_LIGHT2, GL_SPECULAR, (.8, .8, .8, 1)) glLightfv(GL_LIGHT2, GL_POSITION, self.diffuse_pos1) self.diffuse_pos2 = (0,1,0,1) glLightfv(GL_LIGHT3, GL_DIFFUSE, (.5, .5, .5, 1)) glLightfv(GL_LIGHT3, GL_POSITION, self.diffuse_pos2) glLightfv(GL_LIGHT4, GL_SPECULAR, (.8, .8, .8, 1)) glLightfv(GL_LIGHT4, GL_POSITION, self.diffuse_pos2) glEnable(GL_LIGHTING) glEnable(GL_LIGHT0) glEnable(GL_LIGHT1) glEnable(GL_LIGHT2) def display(self, x=0, y=0): '''Called for every refresh; redraws the floor and objects based on the camera angle. Calls collision detection, handles the appropriate objects for keys, doors, etc.''' glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) glLoadIdentity() self.camera.move(self.objects) self.camera.check_collisions(self.objects) self.camera.renderCamera() self.renderLightSource() self.makeFloor() # Transparent objects! glEnable(GL_BLEND) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) self.sort_by_dist() for obj in self.objects: if obj.get_dist(self.camera.pos_X, self.camera.pos_Y, self.camera.pos_Z) < 15: color = obj.get_color() pos = obj.get_pos() obj_type = obj.get_type() glPushMatrix() # Set the objects shininess, ambient, diffuse, and # specular reflections. The objects are slightly # transparent. <- (nick: Why?) glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, 15) glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, [color[0], color[1], color[2], .5]) glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, [.4, .4, .4, 1]) glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, [.9, .9, .9, .5]) glTranslate(pos[0], pos[1], pos[2]) if obj_type == 'block': glutSolidCube(2) elif obj_type == 'key' or obj_type == 'chest': if not obj.get_has(): glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, 75) glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, [color[0], color[1], color[2], .5]) glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, [.4, .4, .4, .7]) glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, [.9, .9, .9, .6]) self.makeobj(obj.get_type()) elif obj_type == 'zombie': glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, 75) glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, [color[0], color[1], color[2], .5]) glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, [.4, .4, .4, .7]) glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, [.9, .9, .9, .6]) zomX, zomY, zomZ = pos # We already grabbed pos. self.makeobj(obj.get_type()) elif obj_type == 'chest': glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, 75) glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, [color[0], color[1], color[2], .5]) glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, [.4, .4, .4, .7]) glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, [.9, .9, .9, .6]) self.makeobj(obj.get_type()) elif obj_type == 'door': if obj.get_key().get_has(): glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, [color[0], color[1], color[2], .2]) glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, [.4, .4, .4, .2]) glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, [.9, .9, .9, .2]) else: glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, 75) glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, [color[0], color[1], color[2], .5]) glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, [.4, .4, .4, .7]) glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, [.9, .9, .9, .6]) glRotate(obj.get_rotation(), 0, 1, 0) self.makeobj(obj.get_type()) else: glutSolidSphere(2, 40, 40) glPopMatrix() glDisable(GL_BLEND) Overlay.draw_overlay(self.camera, self.soundboard.paused) if self.camera.key_timeout > 0: Overlay.draw_text("Got a key!") self.camera.key_timeout -= 1 if self.camera.dead_timeout > 0: Overlay.draw_text("A zombie killed you!") self.camera.dead_timeout -= 1 if self.camera.treasure_timeout > 0: Overlay.draw_text("Got treasure!") self.camera.treasure_timeout -= 1 glutSwapBuffers() def mouseMove(self, x, y): '''Called when the mouse is moved.''' factor = 2 tmp_x = (self.camera.mouse_x - x)/factor tmp_y = (self.camera.mouse_y - y)/factor if tmp_x > self.camera.ROTATE: tmp_x = self.camera.ROTATE self.camera.rotate(0, tmp_x, 0) x = self.WINDOW_WIDTH/2 y = self.WINDOW_HEIGHT/2 glutWarpPointer(x, y) self.camera.mouse_x = x self.camera.mouse_y = y def keyPressed(self, key, x, y): '''Called when a key is pressed.''' if key.lower() in self.camera.keys: self.camera.keys[key.lower()] = True if glutGetModifiers() == GLUT_ACTIVE_SHIFT: self.camera.keys["shift"] = True if key == 'j': self.camera.rotate(0,3,0) elif key == 'l': self.camera.rotate(0,-3,0) elif key == 'i': self.camera.rotate(3,0,0) elif key == 'k': self.camera.rotate(-3,0,0) elif key == 'm': if self.soundboard.paused: self.soundboard.unpauseMusic() else: self.soundboard.pauseMusic() elif key == 'x': exit(0) def keyUp(self, key, x, y): '''Called when a key is released.''' # Speed things up by not checking if the key is in the map self.camera.keys[key.lower()] = False if not glutGetModifiers() == GLUT_ACTIVE_SHIFT: self.camera.keys["shift"] = False def renderLightSource(self): '''Resets the light sources to the right position.''' glLightfv(GL_LIGHT0, GL_POSITION, self.diffuse_pos1) glLightfv(GL_LIGHT2, GL_POSITION, self.diffuse_pos2) glLightfv(GL_LIGHT3, GL_POSITION, self.diffuse_pos2) glLightfv(GL_LIGHT4, GL_POSITION, self.diffuse_pos2) def makeFloor(self): '''Makes a floor of the correct size and places an self.floor_texture (texture) on it.''' glEnable(GL_TEXTURE_2D) glPixelStorei(GL_UNPACK_ALIGNMENT,1) glTexImage2D(GL_TEXTURE_2D, 0, 3, self.ix, self.iy, 0, GL_RGBA, GL_UNSIGNED_BYTE, self.floor) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST) glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL) glBegin(GL_QUADS) glTexCoord2f(0.0, 0.0) glVertex(-self.MAP_SIZE,-.5,-self.MAP_SIZE) glTexCoord2f(1.0, 0.0) glVertex(self.MAP_SIZE,-.5,-self.MAP_SIZE) glTexCoord2f(1.0, 1.0) glVertex(self.MAP_SIZE,-.5,self.MAP_SIZE) glTexCoord2f(0.0, 1.0) glVertex(-self.MAP_SIZE,-.5,self.MAP_SIZE) glEnd() glDisable(GL_TEXTURE_2D) def makeobj(self, kind): '''Makes the desired object from the loaded obj file.''' if kind == 'key': self.key.rawDraw() elif kind == 'door': self.door.rawDraw() elif kind == 'zombie': self.zombie.rawDraw() elif kind == 'chest': self.chest.rawDraw() def sort_by_dist(self): for obj in self.objects: obj.get_dist(self.camera.pos_X, self.camera.pos_Y, self.camera.pos_Z) self.objects = sorted(self.objects, key=lambda obj: obj.dist, reverse=True) def get_visible(self, lst): '''Only draws the objects sitting in front of the camera. Everything behind it is left undrawn.''' to_use = [] for item in lst: c = item.dist x,z = self.camera.project_move_other() b = self.camera.get_camera_distance(x, 0, z) a = item.get_dist(x, 0, z) angle = 0 try: num = ((b**2)+(c**2)-(a**2))/(2*b*c) angle = math.acos(num)/math.pi*180 except: pass if angle > 90: to_use.append(item) return to_use