Using scrolling camera in pygame

2019-07-23 23:02发布

问题:

I have tried to implement a camera function in my 2d game made with pygame but the only I obtain is my player set in the middle of the window. I see how its x and y cordenates changes but the player does not move.I know I must use the camera's apply method to the map but I don't know how to do it since is my first approach to pygame. Here is my code:

import pygame, sys
from pygame.locals import *
import random

"""
dirt  0
grass 1
water 2
coal  3
"""

DIRT = 0
GRASS = 1
WATER = 2
COAL = 3
DIAMOND = 4
CLOUD = 5

cloudx = -200
cloudy = 0

WHITE = (255, 255, 255)
BLACK = (0,   0,   0  )
BROWN = (153, 76,  0  )
GREEN = (0,   255, 0  )
BLUE  = (0,   0,   255)

TILESIZE  = 40
MAPWIDTH  = 140
MAPHEIGHT = 120

WIN_WIDTH = 800
WIN_HEIGHT = 600

HALF_WIDTH = int(WIN_WIDTH / 2)
HALF_HEIGHT = int(WIN_HEIGHT / 2)

fpsClock = pygame.time.Clock()

colours = {
            DIRT: BROWN,
            GRASS: GREEN,
            WATER: BLUE,
            COAL: BLACK
          }

"""tilemap = [
            [GRASS, COAL,  DIRT ],
            [WATER, WATER, GRASS],
            [COAL,  WATER, GRASS],
            [DIRT,  WATER, COAL ],
            [GRASS, WATER, DIRT]
          ]"""

resources = [WATER, GRASS, COAL, DIRT, DIAMOND]
tilemap = [[random.choice(resources) for w in range(MAPWIDTH)] for h in range(MAPHEIGHT)]



textures = {
            DIRT: pygame.image.load("dirt.gif"),
            GRASS: pygame.image.load("grass.gif"),
            COAL: pygame.image.load("coal.gif"),
            WATER: pygame.image.load("water.gif"),
            DIAMOND: pygame.image.load("diamond.gif"),
            CLOUD: pygame.image.load("nube.gif")
           }

inventory = {
                DIRT    :0,
                WATER   :0,
                GRASS   :0,
                COAL    :0,
                DIAMOND :0

            }

playerPos = [50,50]
move = 0
vel_x = 0
vel_y = 0
speed = 1

class Camera(object):
    def __init__(self, camera_func, width, height):
        self.camera_func = camera_func
        self.state = pygame.Rect(0, 0, width, height)

    def apply(self, rect):
        return rect.move(self.state.topleft)

    def update(self, target_rect):
        self.state = self.camera_func(self.state, target_rect)

def simple_camera(camera, target_rect):
    l, t, _, _ = target_rect
    _, _, w, h = camera

    return pygame.Rect(-l+HALF_WIDTH, -t+HALF_HEIGHT, w, h)

def complex_camera(camera, target_rect):
    l, t, _, _ = target_rect
    _, _, w, h = camera
    l, t, _, _ = -l+HALF_WIDTH, -t+HALF_HEIGHT, w, h

    l = min(0, l)                           # stop scrolling at the left edge
    l = max(-(camera.width-SCREEN_WIDTH), l)   # stop scrolling at the right edge
    t = max(-(camera.height-SCREEN_HEIGHT), t) # stop scrolling at the bottom
    t = min(0, t)                           # stop scrolling at the top
    return pygame.Rect(l, t, w, h)

global cameraX, cameraY
total_level_width = len(tilemap[0]) * TILESIZE
total_level_height = len(tilemap)*TILESIZE
camera = Camera(simple_camera ,total_level_width, total_level_height)

pygame.init()
DISPLAYSURF = pygame.display.set_mode((WIN_WIDTH, WIN_HEIGHT))
pygame.display.set_caption("My first game :)")
pygame.display.set_icon(pygame.image.load("player.gif"))

PLAYER = pygame.image.load("player.gif")
playerrect = PLAYER.get_rect()


for rw in range(MAPHEIGHT):
    for cl in range(MAPWIDTH):
        randomNumber = random.randint(0,50)

        if randomNumber <= 10:
            tile = COAL
        elif randomNumber > 11 and randomNumber <= 20:
            tile = WATER
        elif randomNumber > 21 and randomNumber <= 45:
            tile = GRASS
        elif randomNumber > 46 and randomNumber <= 49:
            tile = DIRT
        else:
            tile = DIAMOND

        tilemap[rw][cl] = tile

INVFONT = pygame.font.Font("freeSansBold.ttf", 18)

while True:
    for event in pygame.event.get():
        if event.type == QUIT or event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
            pygame.quit()
            sys.exit()
        elif event.type == KEYDOWN:
            #movement
            if event.key == K_RIGHT and playerPos[0] < MAPWIDTH - 1:
                playerPos[0] += move
            elif event.key == K_LEFT and playerPos[0] > 1/TILESIZE:
                playerPos[0] -= move
            elif event.key == K_DOWN and playerPos[1] < MAPHEIGHT - 1:
                playerPos[1] += move
            elif event.key == K_UP and playerPos[1] > 1/TILESIZE:
                playerPos[1] -= move

            #pick up resource
            elif event.key == K_SPACE:
                currentTile = tilemap[playerPos[1]][playerPos[0]]
                inventory[currentTile] += 1
                tilemap[playerPos[1]][playerPos[0]] = DIRT

            #place resources
            elif event.key == K_1:
                currentTile = tilemap[playerPos[1]][playerPos[0]]
                if inventory[WATER] > 0:
                    inventory[WATER] -= 1
                    tilemap[playerPos[1]][playerPos[0]] = WATER
                    inventory[currentTile] += 1
            elif event.key == K_2:
                currentTile = tilemap[playerPos[1]][playerPos[0]]
                if inventory[GRASS] > 0:
                    inventory[GRASS] -= 1
                    tilemap[playerPos[1]][playerPos[0]] = GRASS
                    inventory[currentTile] += 1
            elif event.key == K_3:
                currentTile = tilemap[playerPos[1]][playerPos[0]]
                if inventory[COAL] > 0:
                    inventory[COAL] -= 1
                    tilemap[playerPos[1]][playerPos[0]] = COAL
                    inventory[currentTile] += 1
            elif event.key == K_4:
                currentTile = tilemap[playerPos[1]][playerPos[0]]
                if inventory[DIRT] > 0:
                    inventory[DIRT] -= 1
                    tilemap[playerPos[1]][playerPos[0]] = DIRT
                    inventory[currentTile] += 1
            elif event.key == K_5:
                currentTile = tilemap[playerPos[1]][playerPos[0]]
                if inventory[DIAMOND] > 0:
                    inventory[DIAMOND] -= 1
                    tilemap[playerPos[1]][playerPos[0]] = DIAMOND
                    inventory[currentTile] += 1
    keys = pygame.key.get_pressed()
    if keys[K_LEFT] and playerPos[0] > 1/TILESIZE:
        playerPos[0] -= speed
    if keys[K_RIGHT] and playerPos[0] < MAPWIDTH - 1:
        playerPos[0] += speed
    if keys[K_UP] and playerPos[1] > 1/TILESIZE:
        playerPos[1] -= speed
    if keys[K_DOWN] and playerPos[1] < MAPHEIGHT - 1:
        playerPos[1] += speed

    for row in range(MAPHEIGHT):
        for column in range(MAPWIDTH):
            DISPLAYSURF.blit(textures[tilemap[row][column]], (column*TILESIZE, row*TILESIZE))
            #DISPLAYSURF.blit(PLAYER, (playerPos[0]*TILESIZE, playerPos[1]*TILESIZE))


    DISPLAYSURF.blit(PLAYER, camera.apply(pygame.Rect(playerPos[0],playerPos[1],42,42)))
    camera.update(PLAYER.get_rect().move((playerPos[0],playerPos[1])))

    pygame.display.update()
    fpsClock.tick(10)
    print playerPos

回答1:

First, remove this block of code:

        if event.key == K_RIGHT and playerPos[0] < MAPWIDTH - 1:
            playerPos[0] += move
        elif event.key == K_LEFT and playerPos[0] > 1/TILESIZE:
            playerPos[0] -= move
        elif event.key == K_DOWN and playerPos[1] < MAPHEIGHT - 1:
            playerPos[1] += move
        elif event.key == K_UP and playerPos[1] > 1/TILESIZE:
            playerPos[1] -= move

You already use pygame.key.get_pressed() for movement, so there's not need to check for a key press (Don't forget to change the following elif to if).

I know I must use the camera's apply method to the map

Yes, you're on the right track.

You already create a variable playerrect, so let's use it to store the position of the player. Also, let's create a Rect to store the size of the map. That will come in handy soon:

playerrect = PLAYER.get_rect(top=50, left=50)
mapsize = pygame.rect.Rect(0, 0, MAPWIDTH*TILESIZE, MAPHEIGHT*TILESIZE)

Now, when keys are hold down to move the player, we can simply use

keys = pygame.key.get_pressed()

if keys[K_LEFT]:  playerrect.move_ip(-speed, 0)
if keys[K_RIGHT]: playerrect.move_ip(speed, 0)
if keys[K_UP]:    playerrect.move_ip(0, -speed)
if keys[K_DOWN]:  playerrect.move_ip(0, speed)

playerrect.clamp_ip(mapsize)

to change the position of the player, and playerrect.clamp_ip(mapsize) makes sure that the player never leaves the map.

As for drawing, you should clear the screen first. To draw the tiles, you have to create a Rect for each tile (maybe later you could create them once and store/reuse them).

DISPLAYSURF.fill(pygame.color.Color('Black'))
for row in range(MAPHEIGHT):
    for column in range(MAPWIDTH):
        DISPLAYSURF.blit(textures[tilemap[row][column]], camera.apply(pygame.rect.Rect((column*TILESIZE, row*TILESIZE), (TILESIZE, TILESIZE))))

DISPLAYSURF.blit(PLAYER, camera.apply(playerrect))
camera.update(playerrect)


标签: python pygame