I'm still having trouble with the scrolling/obstacle detection of my game. Currently my game scrolls exactly how I want it to (the player stays in the center of the screen unless he reaches one of the outer bounds of the map). My problem lies in the obstacle detection. Here are some things I have tried:
(1) I have tried shifting the walls based on my camera's x and y coordinates, but this always allowed me to go partially into the wall, or it restricted me from passing when I was far away from a wall.
(2) I tried creating a list that contained rectangles for each tile that was not walkable, but failed when I didn't know how to determine which rectangle to test for a collision. Also, the way I was doing this I was still using the same list to look up for the collision as (1), so I think even if I got this to work I would have had similar results as (1)
(3) I even tried defining each pixel as being walkable or not. This was my last resort, and was way too slow (obviously) to do anything with.
My current code allows scrolling and it leaves the walls in their original position on the screen, so if Im in the top left portion of the map my upper and left walls work, but everything else does not, and I know this is because I don't have any code to allow the walls to shift. Captain P has given me many great suggestions, but I am not fully understanding how to implement them with my code. So, I am asking for help once again. Thank You!!!
You can download the full code here: http://trovna.googlecode.com/files/Game%28stable%20scrolling%20%5Bno%20walls%5D%29%20.zip
But I think this is all you will need:
Game.py::::
#Change the version number and put your name after you edit#V Whatever#Whoever# KEEP THE CODE ORGANIZED# make changes in the right section# comments will help others (and YOU) understand# IDEA/ALTER model#I - Import and initializeimport pygameimport randomimport mathimport CharacterSpritesimport EnemySpritesimport ItemSpritesimport HUDsimport MapFunctionsimport ImagePathsimport Camerafrom Map import ImageCachefrom pygame.locals import *######initializing global variables###############initialize pygame#######pygame.init()#D - Display configurationresolution = (800,600)screen = pygame.display.set_mode(resolution)last_key_pressed = "DOWN"charImages = ImagePaths.CharImagePaths()fps = 30######main game loop#######def main():#E - Entities (things moving about on screen) pygame.display.set_caption("THE GAME") background = pygame.Surface(screen.get_size()) background.fill((50,255,50)) # what color the background is in RGB screen.blit(background, (0, 0)) last_key_pressed = "DOWN" # Assign sprite classes and other classes to variables char = CharacterSprites.Char(charImages, fps, resolution, last_key_pressed) info = HUDs.HUD() info.center = (100, 50) map1 = MapFunctions.Map(resolution, last_key_pressed) map1.load('Map/map.txt', resolution, char) boundingRectangle = Camera.BoundingRectangle(map1) camera = Camera.Camera(boundingRectangle, char) wiz = EnemySprites.Wiz(screen) wiz1 = EnemySprites.Wiz(screen) wiz2 = EnemySprites.Wiz(screen) gold = ItemSprites.Gold() gold1 = ItemSprites.Gold() gold2 = ItemSprites.Gold() cache = ImageCache.ImageCache() #A - Action (broken into ALTER steps) #A Assign values to key variables goodSprites = pygame.sprite.Group(char) itemSprites = pygame.sprite.Group(gold, gold1, gold2) badSprites = pygame.sprite.Group(wiz, wiz1, wiz2) infoSprites = pygame.sprite.Group(info) clock = pygame.time.Clock() #initializing the clock keepGoing = True #initialally we want to Keep Going # Main caption pygame.display.set_caption("THE GAME") #L - Set up main loop while keepGoing: #T - Timer to set frame rate clock.tick(fps) #setting frame rate number = fps #E - Event handling #saving the old position, before we change it, just incase we hit a wall. char.old_x = char.rect.centerx char.old_y = char.rect.centery # The above is for wall collisions for event in pygame.event.get(): if event.type == pygame.QUIT: keepGoing = False elif event.type == pygame.KEYDOWN: if event.key == pygame.K_LEFT: char.moveWest = True last_key_pressed = "LEFT" elif event.key == pygame.K_RIGHT: char.moveEast = True last_key_pressed = "RIGHT" elif event.key == pygame.K_UP: char.moveNorth = True last_key_pressed = "UP" elif event.key == pygame.K_DOWN: char.moveSouth = True last_key_pressed = "DOWN" elif event.key == pygame.K_ESCAPE: pygame.event.post(pygame.event.Event(pygame.QUIT, {})) elif event.type == pygame.KEYUP: if event.key == pygame.K_LEFT: char.moveWest = False char.image = char.charImages.character_images_left[0] elif event.key == pygame.K_RIGHT: char.moveEast = False char.image = char.charImages.character_images_right[0] elif event.key == pygame.K_UP: char.moveNorth = False char.image = char.charImages.character_images_back[0] elif event.key == pygame.K_DOWN: char.moveSouth = False char.image = char.charImages.character_images_front[0] ######Check for sprites colliding with other sprites###### hitItem = pygame.sprite.spritecollide(char, itemSprites, False) if hitItem: for theItem in hitItem: theItem.reset() #resets location info.gold += 1 #adds one to gold hitBad = pygame.sprite.spritecollide(char, badSprites, False) if hitBad: for theBad in hitBad: char.reset()#resets location info.life -= 1# ads one to kills if info.life <= 0: keepGoing = False #R - Refresh display "blit" means printing an image ###Clear old sprite groups goodSprites.clear(screen, background) itemSprites.clear(screen, background) badSprites.clear(screen, background) infoSprites.clear(screen, background) ###Update the sprite group positions, etc. goodSprites.update(last_key_pressed, pygame.time.get_ticks(), camera, boundingRectangle) itemSprites.update() badSprites.update() infoSprites.update() camera.update(char, boundingRectangle, map1) ###Draw new sprite groups map1.draw(screen, char, camera) goodSprites.draw(screen) itemSprites.draw(screen) badSprites.draw(screen) infoSprites.draw(screen) ####Switch the screen to the new information pygame.display.flip() # End main game loopif __name__ == "__main__": main()
MapFunctions.py::::
import pygame, mathfrom Map import ImageCacheclass Map(): def __init__(self, resolution, last_key_pressed): self.tiles = [] self.images = [] self.collision = [] self.shift_wall_x = 0 self.shift_wall_y = 0 cache = ImageCache.ImageCache() self.images.append(cache.getImage('Images/Scenery/ocean.png')) self.images.append(cache.getImage('Images/Scenery/pathway.png')) self.images.append(cache.getImage('Images/Scenery/brick.png')) self.tilewidth = self.images[0].get_width() self.tileheight = self.images[0].get_height() self.resolution = resolution self.last_key_pressed = last_key_pressed self.advanceVelocity_x = 0 self.advanceVelocity_y = 0 self.numXTiles = int(math.ceil(float(self.resolution[0]) / self.tilewidth)) + 1 self.numYTiles = int(math.ceil(float(self.resolution[1]) / self.tileheight)) + 1 self.tiledBG = pygame.Surface((self.numXTiles * self.tilewidth, self.numYTiles * self.tileheight)).convert() self.wallsBG = pygame.Surface((self.numXTiles * self.tilewidth, self.numYTiles * self.tileheight)).convert() def load(self, map_file, resolution, char): self.char = char self.resolution = resolution for line in open(map_file, 'r'): characters = line.strip().split(',') if len(characters) > 0: self.tiles.append([]) self.collision.append([]) # Run through all the tile characters for character in characters: if character == 'OCN': # [-1] means: the last element of # I'm pushing 0 here, because 'red' is the 0th element in self.images self.tiles[-1].append(0) self.collision[-1].append(True) elif character == 'PTH': self.tiles[-1].append(1) self.collision[-1].append(False) elif character == 'BRK': self.tiles[-1].append(2) self.collision[-1].append(True) else: # Right now this makes each row one tile shorter, because # we're not storing any data for this tile. print 'Wrong tile: [' + character + ']' #print "Max: (" +str(self.maxHorzScrollBounds)+ ", " +str(self.maxVertScrollBounds)+ ")" def draw(self, screen, char, camera): self.startXTile = int(math.floor(float(camera.x) / self.tilewidth)) self.startYTile = int(math.floor(float(camera.y) / self.tileheight)) for x in range(self.startXTile, self.startXTile + self.numXTiles): for y in range(self.startYTile, self.startYTile + self.numYTiles): #print map1.images[map1.tiles[y][x]] self.tiledBG.blit(self.images[self.tiles[y][x]], ((x - self.startXTile) * self.tilewidth, (y - self.startYTile) * self.tileheight)) screen.blit(self.tiledBG, (0, 0), (camera.x - (self.startXTile * self.tilewidth), (camera.y - (self.startYTile * self.tileheight))) + self.resolution) #Did we hit a wall???? ### The following if/else block checks the topleft, topright, bottomleft, and bottomright coordinates of the character's ### rectangle to see if they collide with any walls. If they collide the player is not allowed to advance, if there is no ### collision, then the char_old variables are reset to the current position of the charactcer. if self.collision[int(math.floor((char.rect.topleft[1])/self.tileheight))][int(math.ceil((char.rect.topleft[0])/self.tilewidth))]: #checking topleft coordinate for collision char.rect.center = (char.old_x, char.old_y) camera.x = camera.old_x camera.y = camera.old_y elif self.collision[int(math.floor((char.rect.topright[1])/self.tileheight))][int(math.ceil((char.rect.topright[0])/self.tilewidth))]: #checking topright coordinate for collision char.rect.center = (char.old_x, char.old_y) camera.x = camera.old_x camera.y = camera.old_y elif self.collision[int(math.floor((char.rect.bottomright[1])/self.tileheight))][int(math.ceil((char.rect.bottomright[0])/self.tilewidth))]: #checking bottomright coordinate for collision char.rect.center = (char.old_x, char.old_y) camera.x = camera.old_x camera.y = camera.old_y elif self.collision[int(math.floor((char.rect.bottomleft[1])/self.tileheight))][int(math.ceil((char.rect.bottomleft[0])/self.tilewidth))]: #checking bottomleft coordinate for collision char.rect.center = (char.old_x, char.old_y) camera.x = camera.old_x camera.y = camera.old_y else: #Didn't hit a wall char_old_x = char.rect.centerx char_old_y = char.rect.centery camera.old_x = camera.x camera.old_y = camera.y
Camera.py::::
import pygame, mathclass BoundingRectangle: def __init__(self, map1): self.max_right_bound = map1.tilewidth * len(map1.tiles[0]) - map1.resolution[0] - 1 self.max_left_bound = 0 self.max_top_bound = 0 self.max_bottom_bound = map1.tileheight * len(map1.tiles) - map1.resolution[1] - 1 def right(self): return self.max_right_bound def left(self): return self.max_left_bound def top(self): return self.max_top_bound def bottom(self): return self.max_bottom_boundclass Camera: def __init__(self, boundingRectangle, char): self.x = 0 self.y = 0 self.advanceVelocity_x = 0 self.advanceVelocity_y = 0 self.scrollVelocity = char.speed self.wall_shift_x = 0 self.wall_shift_y = 0 self.old_x = self.x self.old_y = self.y self.max_bound = False def update(self, char, boundingRectangle, map1): #Snap scrolling window to the Bounding Rectangle max_bound = False if self.x <= boundingRectangle.left(): self.x = boundingRectangle.left() self.max_bound = True if self.x >= boundingRectangle.right(): self.x = boundingRectangle.right() self.max_bound = True if self.y <= boundingRectangle.top(): self.y = boundingRectangle.top() self.max_bound = True if self.y >= boundingRectangle.bottom(): self.y = boundingRectangle.bottom() self.max_bound = True #shift wall positions (x,y) self.wall_shift_x = int(math.floor(self.x / map1.tilewidth)) self.wall_shift_y = int(math.floor(self.y / map1.tileheight)) #print "wall shifts: (" +str(self.wall_shift_x)+ ", " +str(self.wall_shift_y)+ ")" #print "Camera Coordinates: (" +str(self.x)+ ", " +str(self.y)+ ")"
CharacterSprites.py::::
import pygame, math, Map, ImagePathsfrom Map import ImageCacheclass Char(pygame.sprite.Sprite): def __init__(self, charImages, fps, resolution, last_key_pressed): pygame.sprite.Sprite.__init__(self) self.charImages = charImages self.start = pygame.time.get_ticks() self.delay = 1000 / fps self.last_update = 0 self.frame = 0 self.last_key_pressed = last_key_pressed self.image = self.charImages.character_images_front[self.frame] if self.last_key_pressed == "UP": self.image = self.charImages.character_images_back[self.frame] if self.last_key_pressed == "DOWN": self.image = self.charImages.character_images_front[self.frame] if self.last_key_pressed == "RIGHT": self.image = self.charImages.character_images_right[self.frame] if self.last_key_pressed == "LEFT": self.image = self.charImages.character_images_left[self.frame] self.resolution = resolution self.image = self.image.convert() self.rect = self.image.get_rect() self.rect.centerx = 400 self.rect.centery = 300 self.speed = 7 self.old_x = self.rect.centerx self.old_y = self.rect.centery self.start_x = self.rect.centerx self.start_y = self.rect.centery self.moveNorth = False self.moveSouth = False self.moveEast = False self.moveWest = False def reset(self): self.rect.centerx = self.start_x self.rect.centery = self.start_y def update(self, last_key_pressed, t, camera, boundingRectangle): # Put the player in the new spot self.last_key_pressed = last_key_pressed #if abs(self.start_x - self.rect.centerx) < self.speed: #self.rect.centerx = self.start_x if self.moveNorth: self.animate(self.charImages.character_images_back, t) if camera.y == boundingRectangle.top() or self.rect.centery > self.start_y: self.rect.centery -= self.speed if self.rect.centery == self.start_y: camera.y -= self.speed if self.moveSouth: self.animate(self.charImages.character_images_front, t) if camera.y == boundingRectangle.bottom() or self.rect.centery < self.start_y: self.rect.centery += self.speed if self.rect.centery == self.start_y: camera.y += self.speed if self.moveEast: self.animate(self.charImages.character_images_right, t) if camera.x == boundingRectangle.right() or self.rect.centerx < self.start_x: self.rect.centerx += self.speed if self.rect.centerx == self.start_x: camera.x += self.speed if self.moveWest: self.animate(self.charImages.character_images_left, t) if camera.x == boundingRectangle.left() or self.rect.centerx > self.start_x: self.rect.centerx -= self.speed if self.rect.centerx == self.start_x: camera.x -= self.speed if self.moveNorth == False and self.moveSouth == False and self.moveEast == False and self.moveWest == False: if self.last_key_pressed == "UP": self.image = self.charImages.character_images_back[0] if self.last_key_pressed == "DOWN": self.image = self.charImages.character_images_front[0] if self.last_key_pressed == "RIGHT": self.image = self.charImages.character_images_right[0] if self.last_key_pressed == "LEFT": self.image = self.charImages.character_images_left[0] self.frame = 0 #print "Frame: " + str(self.frame) #print "last_key_pressed: " + self.last_key_pressed #print self.image self.rect.center = (self.rect.centerx, self.rect.centery) def animate(self, image_array, t): if t - self.last_update > self.delay: self.frame += 1 if self.frame >= len(image_array): self.frame = 0 self.image = image_array[self.frame] self.colorkey = self.image.get_at((0, 0)) self.image.set_colorkey(self.colorkey) self.last_update = t #map1.vpCoordinate_x += map1.advanceVelocity_x #map1.vpCoordinate_y += map1.advanceVelocity_y #if map1.vpCoordinate_x < map1.minHorzScrollBounds: # map1.vpCoordinate_x = map1.minHorzScrollBounds #print "minHorz" #if map1.vpCoordinate_x > map1.maxHorzScrollBounds: # map1.vpCoordinate_x = map1.maxHorzScrollBounds #print "maxHorz" #if map1.vpCoordinate_y < map1.minVertScrollBounds: # map1.vpCoordinate_y = map1.minVertScrollBounds #print "minVert" #if map1.vpCoordinate_y > map1.maxVertScrollBounds: # map1.vpCoordinate_y = map1.maxVertScrollBounds #print "maxVert" # if last_key_pressed == "LEFT": # if map1.vpCoordinate_x == map1.minHorzScrollBounds: # char.rect.centerx -= char.speed # if map1.vpCoordinate_x == map1.maxHorzScrollBounds: # if char.rect.centerx > char.start_x: # char.rect.centerx -= char.speed #if last_key_pressed == "RIGHT": # if map1.vpCoordinate_x == map1.maxHorzScrollBounds: # char.rect.centerx += char.speed # if map1.vpCoordinate_x == map1.minHorzScrollBounds: # if char.rect.centerx < char.start_x: # char.rect.centerx += char.speed # if last_key_pressed == "UP": # if map1.vpCoordinate_y == map1.minVertScrollBounds: # char.rect.centery -= char.speed # if map1.vpCoordinate_y == map1.maxVertScrollBounds: # if char.start_y < char.rect.centery: # char.rect.centery -= char.speed #if last_key_pressed == "DOWN": # if map1.vpCoordinate_y == map1.maxVertScrollBounds: # char.rect.centery += char.speed # if map1.vpCoordinate_y == map1.minVertScrollBounds: # if char.start_y > char.rect.centery: # char.rect.centery += char.speed