pythonpython-2.7pygame

How can I create a 2-d scrolling tiled map in pygame?


I am creating an RPG like game, however, I have absolutely no idea how to scroll through the map so the player is in the centre. Any other suggestions you may have would be greatly appreciated too! Thanks for all your help! Here's my code:

import pygame
from pytmx import load_pygame

pygame.init()

transparent = (0,0,0,0)
black = (0,0,0)
white = (255,255,255)
x,y = 0,0

moveSpeed = 5

#create window
screenSize = (800,600)
screen = pygame.display.set_mode(screenSize)
pygame.display.set_caption("Frozen")

gameMap = load_pygame("Frozen.tmx")

def getMap(layer):
    #creates list of single tiles
    images = []
    for y in range(50):
        for x in range(50):
            image = gameMap.get_tile_image(x,y,layer)
            images.append(image)

    #displays tiles in locations
    i = 0
    for y in range(50):
        for x in range(50):
            screen.blit(images[i],(x*32,y*32))
            i += 1

def getProperties():
    #gets properties of tiles in map            
    properties = []
    for y in range(50):
        for x in range(50):
            tileProperties = gameMap.get_tile_properties(x,y,3)
            properties.append(tileProperties)
    return properties

def createBoxes():
    #creates collision rectangles for unpassable tiles  
    noWalkLocs = []
    rects = []
    i = 0
    testSurf = pygame.Surface(screenSize).convert_alpha()
    testSurf.fill(transparent)
    for y in range(50):
        for x in range(50):
            tileProperties = properties[i]
            walkable = tileProperties.get("walkable")
            if walkable == "False":
                rect = pygame.draw.rect(testSurf,(0,0,0, 0),(x*32,y*32,32,32)) 
                rects.append(rect)
            i += 1
    return rects



def getCollisions():
    global x
    global y
    collisionLoc = player.rect.collidelist(rects)
    collision = rects[collisionLoc]
    if player.rect.collidepoint(collision.bottomleft):
        if x > 0 and y < 0:
            player.rect.topright = collision.bottomleft
            x,y = 0, 0
        elif x > 0:
            player.rect.right = collision.left
            x =0
        elif y < 0:
            player.rect.top = collision.bottom
            y = 0
    elif player.rect.collidepoint(collision.topleft):
        if x > 0 and y > 0:
            player.rect.bottomright = collision.topleft
            x,y = 0, 0
        elif x > 0:
            player.rect.right = collision.left
            x =0
        elif y > 0:
            player.rect.bottom = collision.top
            y = 0
    elif player.rect.collidepoint(collision.bottomright):
        if x < 0 and y <0:
            player.rect.topleft = collision.bottomright
            x,y = 0, 0
        elif x < 0:
            player.rect.left = collision.right
            x =0
        elif y < 0:
            player.rect.top = collision.bottom
            y = 0
    elif player.rect.collidepoint(collision.topright):
        if x < 0 and y > 0:
            player.rect.bottomleft = collision.topright
            x,y = 0, 0
        elif x < 0:
            player.rect.left = collision.right
            x =0
        elif y > 0:
            player.rect.bottom = collision.top
            y = 0
    elif player.rect.collidepoint(collision.midbottom):
        player.rect.top = collision.bottom
        y = 0
    elif player.rect.collidepoint(collision.midtop):
        player.rect.bottom = collision.top
        y = 0
    elif player.rect.collidepoint(collision.midright):
        player.rect.left = collision.right
        x = 0
    elif player.rect.collidepoint(collision.midleft):
            player.rect.right = collision.left
            x = 0
    return player.rect.x, player.rect.y, x, y

class sprite(pygame.sprite.Sprite):
    def __init__(self,x,y):
        pygame.sprite.Sprite.__init__(self)

        self.x,self.y = x,y

        self.image = standFront

        self.rect = self.image.get_rect()

        self.frontImages = [standFront,walkFront1,walkFront2,walkFront3]

        self.backImages = [standBack,walkBack1,walkBack2,walkBack3]

        self.leftImages = [standLeft,walkLeft1,walkLeft2,walkLeft3]

        self.rightImages = [standRight,walkRight1,walkRight2,walkRight3]

        self.index = 0

    def walkFront(self):
        self.index += 1
        if self.index >= len(self.frontImages):
            self.index=0
        self.image = self.frontImages[self.index]    

    def walkBack(self):
        self.index += 1
        if self.index >= len(self.backImages):
            self.index=0
        self.image = self.backImages[self.index]

    def walkLeft(self):
        self.index += 1
        if self.index >= len(self.leftImages):
            self.index=0
        self.image = self.leftImages[self.index]

    def walkRight(self):
        self.index += 1
        if self.index >= len(self.rightImages):
            self.index=0
        self.image = self.rightImages[self.index]

    def render(self):
        screen.blit(self.image,(self.x,self.y))
        self.rect.x, self.rect.y = self.x, self.y

#loads images
standFront = pygame.image.load("Images/Elsa Sprite/WalkFront/StandFront.png").convert_alpha()
walkFront1 = pygame.image.load("Images/Elsa Sprite/WalkFront/WalkFront1.png").convert_alpha()
walkFront2 = pygame.image.load("Images/Elsa Sprite/WalkFront/WalkFront2.png").convert_alpha()
walkFront3 = pygame.image.load("Images/Elsa Sprite/WalkFront/WalkFront3.png").convert_alpha()

standBack = pygame.image.load("Images/Elsa Sprite/WalkBack/StandBack.png").convert_alpha()
walkBack1 = pygame.image.load("Images/Elsa Sprite/WalkBack/WalkBack1.png").convert_alpha()
walkBack2 = pygame.image.load("Images/Elsa Sprite/WalkBack/WalkBack2.png").convert_alpha()
walkBack3 = pygame.image.load("Images/Elsa Sprite/WalkBack/WalkBack3.png").convert_alpha()

standLeft = pygame.image.load("Images/Elsa Sprite/WalkLeft/StandLeft.png").convert_alpha()
walkLeft1 = pygame.image.load("Images/Elsa Sprite/WalkLeft/WalkLeft1.png").convert_alpha()
walkLeft2 = pygame.image.load("Images/Elsa Sprite/WalkLeft/WalkLeft2.png").convert_alpha()
walkLeft3 = pygame.image.load("Images/Elsa Sprite/WalkLeft/WalkLeft3.png").convert_alpha()

standRight = pygame.image.load("Images/Elsa Sprite/WalkRight/StandRight.png").convert_alpha()
walkRight1 = pygame.image.load("Images/Elsa Sprite/WalkRight/WalkRight1.png").convert_alpha()
walkRight2 = pygame.image.load("Images/Elsa Sprite/WalkRight/WalkRight2.png").convert_alpha()
walkRight3 = pygame.image.load("Images/Elsa Sprite/WalkRight/WalkRight3.png").convert_alpha()


#creates tick
clock = pygame.time.Clock()

#creates player
player = sprite(250, 300)

properties = getProperties()
rects = createBoxes()

#main loop
running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

        #gets keypresses
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT:
                x, y = 0, 0
                x -= moveSpeed
            elif event.key == pygame.K_RIGHT:
                x, y = 0, 0
                x += moveSpeed
            elif event.key == pygame.K_UP:
                x, y = 0, 0
                y -= moveSpeed
            elif event.key == pygame.K_DOWN:
                x, y = 0, 0
                y += moveSpeed


        if event.type == pygame.KEYUP:
            if event.key == pygame.K_LEFT:
                x = 0
                player.image = player.leftImages[0]
            elif event.key == pygame.K_RIGHT:
                x = 0
                player.image = player.rightImages[0]
            elif event.key == pygame.K_UP:
                y = 0
                player.image = player.backImages[0]
            elif event.key == pygame.K_DOWN:
                y = 0
                player.image = player.frontImages[0]

    player.x += x
    player.y += y

    if y > 0:
        player.walkFront()
    if y < 0:
        player.walkBack()
    if x > 0:
        player.walkRight()
    if x < 0:
        player.walkLeft()

    getMap(0)
    getMap(1)
    getMap(2)
    player.render()
    pygame.display.flip()

    player.rect.x, player.rect.y, x, y = getCollisions()
    player.x, player.y = player.rect.x, player.rect.y

    clock.tick(10)
pygame.quit()

Solution

  • You could use Pygame's .scroll() method. Instead of changing the x and y coordinates of your player, call tieldMap.scroll() function and pass to it the new x and y (i.e. mx and my) values.

    To do this you need two surfaces, the main screen surface (you already have), and another which you can "scroll" (e.g. tieldMap).

    Add these lines somewhere after the pygame.init() function call:

    mapSize = (120,120) #change this values
    tieldMap = pygame.Surface(mapSize)
    
    mx, my = 0, 0
    

    and change the code in your getMap() function (where you blit the tiles onto the main screen) to:

    #displays tiles in locations
    i = 0
    for y in range(50):
        for x in range(50):
            tieldMap.blit(images[i],(x*32,y*32))
            i += 1
    

    Now you can "scroll" the tieldMap surface in the main game loop:

    1. Change these lines

      player.x += x
      player.y += y
      

      to

      mx += x
      my += y
      
    2. Add this two lines to create a copy called _tieldMap of your tieldMap surface (.copy) and call the .scroll function.

      _tieldMap = tieldMap.copy()
      _tieldMap.scroll(xm, ym)
      

    Finally blit in your main game loop the tieldMap surface onto the screen at (0,0) and update it:

    screen.blit(_tieldMap,(0,0))
    pygame.display.flip()
    

    Important: You must check if xm or ym are smaller or greater than the image height or width!

    I hope this helps! :)