#!/usr/bin/env/python # Tells Linux systems how to run this file. """ Sequence of physics calculation: The default Y for standing on is the exact top of the block, ie. N.0. Eventually want to check multiple horizontal points. Calculate new_x, new_y. Start checking points below new_y, every (say) .5 units. (This handles high fall speed.) footing_handled = False For each point: If it's a solid block: If new_y is below it: new_y = block's height (N.0). speed_y = 0.0 footing_handled = True break Elif it's a partial block: If new_y is below its partial height: new_y = block's partial height speed_y = 0.0 footing_handled = True break Else: continue if not footing_handled: Fall """ __author__ = "Kris Schnee" __version__ = "2010.1.29" __license__ = "Private for now" ##### Imported Modules ##### ## Standard import time ##import os ## Third-party import pygame from pygame.locals import * ## Mine ##### Constants ##### ## Level size in tiles LEVEL_SIZE_X = 40 LEVEL_SIZE_Y = 20 ## Tile size in pixels TILE_SIZE_X = 25 TILE_SIZE_Y = 25 TILE_HEIGHT_INCREMENT = TILE_SIZE_Y / 10.0 PLAYER_SIZE = (TILE_SIZE_X,TILE_SIZE_Y*2) PLAYER_DRAW_OFFSET = (-PLAYER_SIZE[0]/2,-PLAYER_SIZE[1]) SCREEN_SIZE = (LEVEL_SIZE_X*TILE_SIZE_X,LEVEL_SIZE_Y*TILE_SIZE_Y) screen = pygame.display.set_mode(SCREEN_SIZE) pygame.display.set_caption("Squirrelly, Squirrelly Movement Demo") TILES = {} BACKGROUND = None try: ## Load graphics assert 1 == 0 except: ## Improvised graphics t_block = pygame.surface.Surface((TILE_SIZE_X,TILE_SIZE_Y)) t_block.fill((32,128,64)) TILES["block"] = t_block for n in range(1,10): t_block = pygame.surface.Surface((TILE_SIZE_X,TILE_SIZE_Y)) t_block.set_colorkey((0,0,0)) y = TILE_SIZE_Y - int(TILE_HEIGHT_INCREMENT*n) y2 = TILE_SIZE_Y - y pygame.draw.rect(t_block,(64,64,64),(0,y,TILE_SIZE_X,y2),0) TILES["block"+str(n)] = t_block t_ice = pygame.surface.Surface((TILE_SIZE_X,TILE_SIZE_Y)) t_ice.fill((200,200,255)) TILES["ice"] = t_ice BACKGROUND = pygame.surface.Surface(SCREEN_SIZE) for y in range(SCREEN_SIZE[1]): pygame.draw.line(BACKGROUND,(0,0,255-(y/2)),(0,y),(SCREEN_SIZE[0],y)) PLAYER_SPRITE = pygame.surface.Surface(PLAYER_SIZE) PLAYER_SPRITE.set_colorkey((0,0,0)) ## pygame.draw.rect(PLAYER_SPRITE,(255,0,0),(0,0,PLAYER_SIZE[0],PLAYER_SIZE[1]),1) PLAYER_SPRITE.fill((255,255,0)) LOAD_LEVEL_GUIDE = {" ":"blank", ".":"block", "i":"ice", } for n in range(1,10): LOAD_LEVEL_GUIDE[str(n)] = "block"+str(n) TILE_DICT = {" ":None, ".":TILES["block"], "i":TILES["ice"], } for n in range(1,10): TILE_DICT[str(n)] = TILES["block"+str(n)] ## Physics FALL_SPEED = .1 CLIMB_SPEED = .4 JUMP_SPEED = -0.8 TERMINAL_VELOCITY = 0.49 RUN_SPEED = 0.49 MIN_SPEED = .1 WALL_JUMP_AIR_CONTROL_BLOCKED = 5 WALL_JUMP_SPEED = RUN_SPEED * 2.0 TERRAINS = {"block":{"slipping":.5}, "ice":{"slipping":.99}, } TERRAINS_SOLID = ["block","ice"] STAIR_HEIGHT = {"block":1.0,"ice":1.0} for n in range(1,10): STAIR_HEIGHT["block"+str(n)] = n/10.0 ## TERRAINS_SOLID.append("block"+str(n)) TERRAINS["block"+str(n)] = {"slipping":.5} TERRAIN_EMPTY = "empty" MAX_STAIRSTEP = .5 ABOVEGROUND_MARGIN = .1 ## Controls K_DASH = K_LSHIFT K_GRAB = K_z ##### Classes ##### class Squirrel: def __init__(self,**options): self.coords = list(options.get("coords",[13,LEVEL_SIZE_Y-3])) self.speed_x = 0.0 self.speed_y = 0.0 self.size_x = 1.0 self.size_y = 2.0 self.half_x = self.size_x / 2.0 self.half_y = self.size_y / 2.0 self.dash = False self.jumping = False self.air_control_disabled = 0 ## Number of frames left till restored class Game: def __init__(self): self.done = False self.level = [] for x in range(LEVEL_SIZE_X): self.level.append([]) for y in range(LEVEL_SIZE_Y): self.level[-1].append("blank") self.stairmap = [] for x in range(LEVEL_SIZE_X): self.stairmap.append([]) for y in range(LEVEL_SIZE_Y): self.stairmap[-1].append(.1) self.tilemap = [] for x in range(LEVEL_SIZE_X): self.tilemap.append([]) for y in range(LEVEL_SIZE_Y): self.tilemap[-1].append(None) ## Player object self.player = Squirrel() self.frames = 0 self.start_time = time.time() self.LoadLevel("level") def LoadLevel(self,name="level"): if not name.endswith(".txt"): name += ".txt" f = open(name,"r") text = f.read().replace("\n","") f.close() self.level = [] for x in range(LEVEL_SIZE_X): self.level.append([]) for y in range(LEVEL_SIZE_Y): self.level[-1].append( LOAD_LEVEL_GUIDE[ text[(LEVEL_SIZE_X*y)+x] ] ) self.stairmap = [] for x in range(LEVEL_SIZE_X): self.stairmap.append([]) for y in range(LEVEL_SIZE_Y): block_type = self.level[x][y] self.stairmap[-1].append( STAIR_HEIGHT.get(block_type,0.0) ) ## Stores references to the tiles to draw. self.tilemap = [] for x in range(LEVEL_SIZE_X): self.tilemap.append([]) for y in range(LEVEL_SIZE_Y): self.tilemap[-1].append( TILE_DICT[ text[(LEVEL_SIZE_X*y)+x] ] ) def Draw(self): ## Background screen.blit(BACKGROUND,(0,0)) ## Tiles for y in range(LEVEL_SIZE_Y): for x in range(LEVEL_SIZE_X): tile = self.tilemap[x][y] if tile: screen.blit(tile,(TILE_SIZE_X*x,TILE_SIZE_Y*y)) ## Player pc = self.player.coords player_draw_coords = (TILE_SIZE_X*pc[0])+PLAYER_DRAW_OFFSET[0], (TILE_SIZE_Y*pc[1])+PLAYER_DRAW_OFFSET[1] screen.blit(PLAYER_SPRITE,player_draw_coords) pygame.display.update() def Logic(self): ## print "\n\nFrame: "+str(self.frames) ## What keys are currently held? keys_down = pygame.key.get_pressed() if keys_down[K_ESCAPE]: self.done = True self.Physics(keys_down) ## Pump events to clear input. pygame.event.pump() def CheckBlocked(self,x,y): """Is this block occupied?""" return self.level[ int(x) ][ int(y) ] in TERRAINS_SOLID def GetBlockType(self,x,y): return self.level[ int(x) ][ int(y) ] def GetStairHeight(self,x,y): return self.stairmap[ int(x) ][ int(y) ] def Physics(self,keys_down): """Move the player based on velocity and obstruction.""" ## Where will the player be if nothing interferes? new_x = self.player.coords[0]+self.player.speed_x new_y = self.player.coords[1]+self.player.speed_y ## If the following calculations don't raise this, player will fall. player_on_ground = False ## Check blocks to left and right of player. ## Left: left_x = new_x-self.player.half_x left_y1 = new_y - self.player.size_y left_y2 = new_y - self.player.half_y left_y3 = new_y - MAX_STAIRSTEP left_blocked = self.CheckBlocked(left_x,left_y1) or self.CheckBlocked(left_x,left_y2) or self.CheckBlocked(left_x,left_y3) ## Right: right_x = new_x+self.player.half_x right_y1 = new_y - self.player.size_y right_y2 = new_y - self.player.half_y right_y3 = new_y - MAX_STAIRSTEP right_blocked = self.CheckBlocked(right_x,right_y1) or self.CheckBlocked(right_x,right_y2) or self.CheckBlocked(right_x,right_y3) if left_blocked: self.player.speed_x = 0.0 new_x = float(int(left_x)) + 1.0 + self.player.half_x + .01 if right_blocked: self.player.speed_x = 0.0 new_x = float(int(right_x)) - self.player.half_x - .01 ## Check one or more points below the player. x = self.player.coords[0] ## Note: Current x. down_ys = [self.player.coords[1]-.1] drop_distance = self.player.speed_y DROP_CHECK_INCREMENT = .5 while drop_distance > DROP_CHECK_INCREMENT: down_ys.append(down_ys[-1]+DROP_CHECK_INCREMENT) drop_distance -= DROP_CHECK_INCREMENT down_ys.append(new_y) ## print "Y points we'll check: "+str(down_ys) for y in down_ys: ## print "Checking y: "+str(y) block_height = self.GetStairHeight(x,y) ## print "Block height of ("+str(x)[:5]+","+str(y)[:5]+"): "+str(block_height) if block_height == 1.0: ## print "This is a full block. We'll stop here." abs_height = float(int(y)) ## print "Player y vs. stair y: "+str(y)+", "+str(abs_height) if y > abs_height: ## print "Player is embedded in block. Adjust y & stop fall." self.player.speed_y = 0.0 new_y = abs_height player_on_ground = True break ## Done checking points elif abs(y-abs_height)<.05: ## print "Player is exactly atop block." self.player.speed_y = 0.0 new_y = abs_height player_on_ground = True break ## Done checking points else: ## print "Player is above block. That's okay." pass elif block_height > 0.0: ## print "This is a partial block (height "+str(block_height)+"). We'll stop here." abs_height = float(int(y)) + 1.0 - block_height ## print "Player y vs. stair y: "+str(y)+", "+str(abs_height) if y > abs_height: ## print "Player is embedded in partial block. Adjust y & stop fall." self.player.speed_y = 0.0 new_y = abs_height player_on_ground = True break ## Done checking points elif abs(y-abs_height)<.05: ## print "Player is exactly atop block." self.player.speed_y = 0.0 new_y = abs_height player_on_ground = True break ## Done checking points else: ## print "Player is above block. That's okay." pass ## print "Player on ground: "+str(player_on_ground) if not player_on_ground: ## Fall! self.player.speed_y = min(self.player.speed_y+FALL_SPEED,TERMINAL_VELOCITY) else: ground = "block" ##self.GetBlockType(new_x,new_y) ## Friction disabled ## Check block above player. top_x1 = new_x - self.player.half_x top_x2 = new_x top_x3 = new_x + self.player.half_x top_y = new_y-self.player.size_y top_blocked = self.CheckBlocked(top_x1,top_y) or self.CheckBlocked(top_x2,top_y) or self.CheckBlocked(top_x3,top_y) if top_blocked: self.player.speed_y = 0.0 new_y = float(int(new_y)) + 1.0 ## bottom_x_left = self.player.coords[0] - self.player.half_x ## bottom_x_mid = self.player.coords[0] ## bottom_x_right = self.player.coords[0] + self.player.half_x ## bottom_y = self.player.coords[1] ##+ .01 ## ## footing_left = self.GetBlockType(bottom_x_left,bottom_y) ## footing_mid = self.GetBlockType(bottom_x_mid,bottom_y) ## footing_right = self.GetBlockType(bottom_x_right,bottom_y) ## height_left = self.GetStairHeight(bottom_x_left,bottom_y) ## height_mid = self.GetStairHeight(bottom_x_mid,bottom_y) ## height_right = self.GetStairHeight(bottom_x_right,bottom_y) ## print footing_left, footing_mid, footing_right ## print "Base: "+str(int(bottom_y))+", l/m/r: "+str((height_left, height_mid, height_right)) ## #### if not height_left or height_mid or height_right: ## if not (height_left or height_mid or height_right): ## ## Not standing on anything. Fall. ## self.player.speed_y = min(self.player.speed_y+FALL_SPEED,TERMINAL_VELOCITY) ## else: ## player_on_ground = True ## ## Decide what terrain we're effectively on for friction &c. ## ground = footing_mid or footing_left or footing_right ## self.player.speed_y = 0.0 ## self.player.new_y = self.player.coords[1] ## print "Grounded: "+str(player_on_ground) ## print self.player.coords ## Controls m_left = False m_right = False m_up = False m_down = False m_jump = False m_grab = False ledge_hanging = False keys_down = pygame.key.get_pressed() if keys_down[K_ESCAPE]: self.done = True if keys_down[K_RIGHT]: m_right = True if keys_down[K_LEFT]: m_left = True if keys_down[K_UP]: m_up = True if keys_down[K_DOWN]: m_down = True self.player.dash = keys_down[K_DASH] if keys_down[K_SPACE]: m_jump = True if keys_down[K_GRAB]: m_grab = True ## Hang onto walls if m_grab and (left_blocked or right_blocked) and not player_on_ground: self.player.speed_y = 0.0 ledge_hanging = True if m_up: self.player.speed_y = -CLIMB_SPEED elif m_down: self.player.speed_y = CLIMB_SPEED if m_jump: if player_on_ground: self.player.speed_y = JUMP_SPEED ## self.player.jumping = True elif ledge_hanging and not self.player.jumping: if left_blocked: self.player.speed_y = JUMP_SPEED self.player.speed_x = WALL_JUMP_SPEED self.player.jumping = True self.player.air_control_disabled = WALL_JUMP_AIR_CONTROL_BLOCKED elif right_blocked: self.player.speed_y = JUMP_SPEED self.player.speed_x = -WALL_JUMP_SPEED self.player.jumping = True self.player.air_control_disabled = WALL_JUMP_AIR_CONTROL_BLOCKED if player_on_ground or not self.player.air_control_disabled: if m_left: self.player.speed_x = -RUN_SPEED if self.player.dash: self.player.speed_x *= 1.5 elif m_right: self.player.speed_x = RUN_SPEED if self.player.dash: self.player.speed_x *= 1.5 else: if player_on_ground: ## Friction self.player.speed_x *= TERRAINS[ground]["slipping"] if abs(self.player.speed_x) < MIN_SPEED: self.player.speed_x = 0.0 self.player.speed_x = 0.0 ## Count down air-control blockage. if self.player.air_control_disabled: self.player.air_control_disabled -= 1 ## Set the player's coordinates now. ## print "Old & new coords: "+str(self.player.coords)+", "+str((new_x,new_y)) ## print "Velocity Y: "+str(self.player.speed_y) self.player.coords = (new_x,new_y) def OldPhysics(self,keys_down): ## PHYSICS ## ## Movement new_x = self.player.coords[0]+self.player.speed_x new_y = self.player.coords[1]+self.player.speed_y ## Needed if terminal velocity is > 1 block, so that player can't fall through thin floors! ## fell_through_y = range(int(self.player.last_coords[1]),int(self.player.coords[1])) ## print fell_through_y ## New physics attempt: ## Check blocks surrounding player. player_on_ground = False ## Left: left_x = new_x-self.player.half_x left_y1 = new_y-self.player.size_y left_y2 = new_y-self.player.half_y left_y3 = new_y left_blocked = self.CheckBlocked(left_x,left_y1) or self.CheckBlocked(left_x,left_y2) or self.CheckBlocked(left_x,left_y3) ## print "Left: "+str(left_x)+", blocked: "+str(left_blocked) ## Right: right_x = new_x+self.player.half_x right_y1 = new_y-self.player.size_y right_y2 = new_y-self.player.half_y right_y3 = new_y right_blocked = self.CheckBlocked(right_x,right_y1) or self.CheckBlocked(right_x,right_y2) or self.CheckBlocked(right_x,right_y3) ## print "Right: "+str(right_x)+", blocked: "+str(right_blocked) if left_blocked: ## New and kludgy! left_tile = self.level[ int(left_x) ][ int(left_y2) ] if (left_tile in TERRAINS_STAIRS) and (False): pass else: self.player.speed_x = 0.0 new_x = self.player.coords[0] if right_blocked: print "blocked" ## New and kludgy! right_tile = self.level[ int(right_x) ][ int(right_y3) ] print right_tile, int(right_x), int(right_y3) if (right_tile in TERRAINS_STAIRS): print "stair test" margin = 1.0 - (right_y3 - int(right_y3)) sh = STAIR_HEIGHT[right_tile] print margin, sh if abs(margin-sh) < MAX_STAIRSTEP: print "good?" new_y -= sh - margin print "raised by: "+str(sh-margin) else: self.player.speed_x = 0.0 new_x = self.player.coords[0] else: self.player.speed_x = 0.0 new_x = self.player.coords[0] ## print "right blocked" ## Top: top_x1 = new_x - self.player.half_x top_x2 = new_x top_x3 = new_x + self.player.half_x top_y = new_y-self.player.size_y top_blocked = self.CheckBlocked(top_x1,top_y) or self.CheckBlocked(top_x2,top_y) or self.CheckBlocked(top_x3,top_y) ## if top_blocked: ## print "top blocked" ## print top_y, self.player.coords ## Bottom: bottom_x1 = new_x - self.player.half_x bottom_x2 = new_x bottom_x3 = new_x + self.player.half_x bottom_y = new_y + .01 bottom_blocked = self.CheckBlocked(bottom_x1, bottom_y) or self.CheckBlocked(bottom_x2, bottom_y) or self.CheckBlocked(bottom_x3, bottom_y) if bottom_blocked: ## print "Bottom is blocked. Coords: "+str((new_x,new_y))+". Setting..." player_on_ground = True self.player.jumping = False ## print "Player is on ground." ## What's the player standing on? ground = self.level[int(new_x)][int(bottom_y)] if ground == TERRAIN_EMPTY: ## Check other spots to allow for a toehold on nearby blocks. ground = self.level[int(bottom_x1)][int(bottom_y)] if ground == TERRAIN_EMPTY: ground = self.level[int(bottom_x3)][int(bottom_y)] if ground == TERRAIN_EMPTY: raise SystemError("Player seems to be standing solidly on nothing.") ## print "Ground: "+str(ground) ## self.player.coords[1] = self.player.last_coords[1] new_y = int(new_y+.01)-.01 ## print "...to new y: "+str(new_y) ## print "New Y: "+str(self.player.coords[1]) self.player.speed_y = 0.0 else: ## Gravity. self.player.speed_y = min(self.player.speed_y+FALL_SPEED,TERMINAL_VELOCITY) ## print "Fall: "+str(self.player.speed_y) if top_blocked: self.player.speed_y = 0.0 new_y = self.player.coords[1] ## ## Stuck in something? Undo. ## ## Assume player is 1.0 units wide. ## bounding_x1 = int(self.player.coords[0] - .5) ## bounding_x2 = int(self.player.coords[0] + .5) ## left_block = self.level[bounding_x1][int(self.player.coords[1])-1] ## if left_block in TERRAINS_SOLID: ## left_wall = True ## self.player.speed_x = 0.0 ## self.player.coords[0] = self.player.last_coords[0] ## else: ## right_block = self.level[bounding_x2][int(self.player.coords[1])-1] ## if right_block in TERRAINS_SOLID: ## right_wall = True ## self.player.speed_x = 0.0 ## self.player.coords[0] = self.player.last_coords[0] #### above_block = self.level[int(self.player.coords[0])][int(self.player.coords[1])-1] ## above_block = self.level[int(self.player.coords[0])][int(self.player.coords[1])-2] ## if above_block in TERRAINS_SOLID: ## self.player.speed_y = 0.0 ## self.player.coords[1] = self.player.last_coords[1] ## ## ## Falling ## pc = self.player.coords #### print pc ## ground = self.level[int(pc[0])][int(pc[1])] ## player_on_ground = ground in TERRAINS_SOLID ## Assumes never on bottom row #### print player_on_ground ## if player_on_ground: ## self.player.speed_y = 0.0 ## self.player.coords[1] = int(self.player.coords[1]) - .001 ## else: ## self.player.speed_y = min(self.player.speed_y+FALL_SPEED,TERMINAL_VELOCITY) ## CONTROLS ## m_left = False m_right = False m_up = False m_down = False m_jump = False keys_down = pygame.key.get_pressed() if keys_down[K_ESCAPE]: self.done = True if keys_down[K_RIGHT]: m_right = True self.player.dash = keys_down[K_DASH] if keys_down[K_LEFT]: m_left = True if keys_down[K_SPACE]: m_jump = True ## ## Test: Force input. ## m_right = True ## if player_on_ground and not self.jumped_already: ## m_jump = True ## self.jumped_already = True ## Act on input. if m_jump: if player_on_ground: self.player.speed_y = JUMP_SPEED self.player.jumping = True else: if left_blocked and not top_blocked and self.CheckBlocked(left_x,left_y1): self.player.speed_y = JUMP_SPEED self.player.speed_x = WALL_JUMP_SPEED self.player.jumping = True self.player.air_control_disabled = WALL_JUMP_AIR_CONTROL_BLOCKED elif right_blocked and not top_blocked and self.CheckBlocked(right_x,right_y1): self.player.speed_y = JUMP_SPEED self.player.speed_x = -WALL_JUMP_SPEED self.player.jumping = True self.player.air_control_disabled = WALL_JUMP_AIR_CONTROL_BLOCKED if player_on_ground or not self.player.air_control_disabled: if m_left: self.player.speed_x = -RUN_SPEED if self.player.dash: self.player.speed_x *= 1.5 elif m_right: self.player.speed_x = RUN_SPEED if self.player.dash: self.player.speed_x *= 1.5 else: if player_on_ground: ## Friction self.player.speed_x *= TERRAINS[ground]["slipping"] if abs(self.player.speed_x) < MIN_SPEED: self.player.speed_x = 0.0 ## Count down air-control blockage. if self.player.air_control_disabled: self.player.air_control_disabled -= 1 ## Set the player's coordinates now. ## print "Old & new coords: "+str(self.player.coords)+", "+str((new_x,new_y)) self.player.coords = (new_x,new_y) def Go(self): while not self.done: self.Logic() self.Draw() self.frames += 1 time_elapsed = time.time() - self.start_time print "FPS: "+str(self.frames / time_elapsed) ##### Other Functions ##### ##### Autorun ##### g = Game() g.Go()