videojuegos sprites matrices juegos juego hacer games funciones fondo espaƱol con como arcade agregar python scroll pygame

python - sprites - Agrega desplazamiento a una plataforma en pygame



pygame matrices (3)

Ok, entonces incluí el código para mi proyecto a continuación, solo estoy haciendo algunos experimentos con pygame para hacer un juego de plataformas. Estoy tratando de descubrir cómo hacer un desplazamiento muy simple que sigue al jugador, por lo que el jugador es el centro de la cámara y rebota / lo sigue. ¿Alguien puede ayudarme?

import pygame from pygame import * WIN_WIDTH = 800 WIN_HEIGHT = 640 HALF_WIDTH = int(WIN_WIDTH / 2) HALF_HEIGHT = int(WIN_HEIGHT / 2) DISPLAY = (WIN_WIDTH, WIN_HEIGHT) DEPTH = 32 FLAGS = 0 CAMERA_SLACK = 30 def main(): global cameraX, cameraY pygame.init() screen = pygame.display.set_mode(DISPLAY, FLAGS, DEPTH) pygame.display.set_caption("Use arrows to move!") timer = pygame.time.Clock() up = down = left = right = running = False bg = Surface((32,32)) bg.convert() bg.fill(Color("#000000")) entities = pygame.sprite.Group() player = Player(32, 32) platforms = [] x = y = 0 level = [ "PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP", "P P", "P P", "P P", "P P", "P P", "P P", "P P", "P PPPPPPPPPPP P", "P P", "P P", "P P", "P P", "PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP",] # build the level for row in level: for col in row: if col == "P": p = Platform(x, y) platforms.append(p) entities.add(p) if col == "E": e = ExitBlock(x, y) platforms.append(e) entities.add(e) x += 32 y += 32 x = 0 entities.add(player) while 1: timer.tick(60) for e in pygame.event.get(): if e.type == QUIT: raise SystemExit, "QUIT" if e.type == KEYDOWN and e.key == K_ESCAPE: raise SystemExit, "ESCAPE" if e.type == KEYDOWN and e.key == K_UP: up = True if e.type == KEYDOWN and e.key == K_DOWN: down = True if e.type == KEYDOWN and e.key == K_LEFT: left = True if e.type == KEYDOWN and e.key == K_RIGHT: right = True if e.type == KEYDOWN and e.key == K_SPACE: running = True if e.type == KEYUP and e.key == K_UP: up = False if e.type == KEYUP and e.key == K_DOWN: down = False if e.type == KEYUP and e.key == K_RIGHT: right = False if e.type == KEYUP and e.key == K_LEFT: left = False if e.type == KEYUP and e.key == K_RIGHT: right = False # draw background for y in range(32): for x in range(32): screen.blit(bg, (x * 32, y * 32)) # update player, draw everything else player.update(up, down, left, right, running, platforms) entities.draw(screen) pygame.display.update() class Entity(pygame.sprite.Sprite): def __init__(self): pygame.sprite.Sprite.__init__(self) class Player(Entity): def __init__(self, x, y): Entity.__init__(self) self.xvel = 0 self.yvel = 0 self.onGround = False self.image = Surface((32,32)) self.image.fill(Color("#0000FF")) self.image.convert() self.rect = Rect(x, y, 32, 32) def update(self, up, down, left, right, running, platforms): if up: # only jump if on the ground if self.onGround: self.yvel -= 10 if down: pass if running: self.xvel = 12 if left: self.xvel = -8 if right: self.xvel = 8 if not self.onGround: # only accelerate with gravity if in the air self.yvel += 0.3 # max falling speed if self.yvel > 100: self.yvel = 100 if not(left or right): self.xvel = 0 # increment in x direction self.rect.left += self.xvel # do x-axis collisions self.collide(self.xvel, 0, platforms) # increment in y direction self.rect.top += self.yvel # assuming we''re in the air self.onGround = False; # do y-axis collisions self.collide(0, self.yvel, platforms) def collide(self, xvel, yvel, platforms): for p in platforms: if pygame.sprite.collide_rect(self, p): if isinstance(p, ExitBlock): pygame.event.post(pygame.event.Event(QUIT)) if xvel > 0: self.rect.right = p.rect.left print "collide right" if xvel < 0: self.rect.left = p.rect.right print "collide left" if yvel > 0: self.rect.bottom = p.rect.top self.onGround = True self.yvel = 0 if yvel < 0: self.rect.top = p.rect.bottom class Platform(Entity): def __init__(self, x, y): Entity.__init__(self) self.image = Surface((32, 32)) self.image.convert() self.image.fill(Color("#DDDDDD")) self.rect = Rect(x, y, 32, 32) def update(self): pass class ExitBlock(Platform): def __init__(self, x, y): Platform.__init__(self, x, y) self.image.fill(Color("#0033FF")) if __name__ == "__main__": main()


Como es correcto, tienes un fondo estático, y el jugador que controlas, está deslumbrado en la posición en la que está, tienes 2 opciones para mostrar siempre el personaje en el medio.

  1. Si el mapa es lo suficientemente pequeño, puede tener un gran img A, y derivar un rectángulo, basado en la posición del jugador que tendrá el tamaño de la pantalla. De esa forma, el jugador siempre estará en el medio. Un Rect.clamp (Rect) o Rect.clamp_ip (Rect) te ayudará en eso.

  2. Otro enfoque es tener una tupla diferente para la posición en la pantalla. El jugador tendrá un valor constante en el centro de la pantalla, mientras que la posición de los fondos será el negativo de la posición del jugador.


Debes aplicar un desplazamiento a la posición de tus entidades cuando las dibujas. Vamos a llamar a eso compensar una camera , ya que este es el efecto que queremos lograr con esto.

En primer lugar, no podemos (y no debemos) utilizar la función draw del grupo sprite, ya que los sprites no necesitan saber que su posición ( rect ) no es la posición en la que van a dibujarse en la pantalla. (Por supuesto, podríamos subclasificar la clase Group y volver a draw para tener en cuenta la cámara, pero por el mero hecho de aprender, seamos explícitos aquí).

Comencemos creando una clase de Camera para mantener el estado del desplazamiento que queremos aplicar a la posición de nuestras entidades:

class Camera(object): def __init__(self, camera_func, width, height): self.camera_func = camera_func self.state = Rect(0, 0, width, height) def apply(self, target): return target.rect.move(self.state.topleft) def update(self, target): self.state = self.camera_func(self.state, target.rect)

algunas cosas a tener en cuenta aquí:

Necesitamos almacenar la posición de la cámara y el ancho y alto del nivel en píxeles (ya que queremos dejar de desplazarnos por los bordes del nivel). Utilicé un Rect para almacenar toda esta información, pero podría simplemente usar algunos campos.

Usar Rect es útil en la función de apply . Aquí es donde volvemos a calcular la posición de una entidad en la pantalla para aplicar el desplazamiento.

Una vez por iteración del ciclo principal, necesitamos actualizar la posición de la cámara, por lo tanto, está la función de update . Simplemente altera el estado al llamar a la función camera_func , que hará todo el trabajo duro por nosotros. Lo implementamos más tarde.

Vamos a crear una instancia de la cámara:

for row in level: ... total_level_width = len(level[0])*32 # calculate size of level in pixels total_level_height = len(level)*32 # maybe make 32 an constant camera = Camera(*to_be_implemented*, total_level_width, total_level_height) entities.add(player) ...

y alterar nuestro ciclo principal:

# draw background for y in range(32): ... camera.update(player) # camera follows player. Note that we could also follow any other sprite # update player, draw everything else player.update(up, down, left, right, running, platforms) for e in entities: # apply the offset to each entity. # call this for everything that should scroll, # which is basically everything other than GUI/HUD/UI screen.blit(e.image, camera.apply(e)) pygame.display.update()

Nuestra clase de cámara ya es muy flexible y, sin embargo, muy simple. Puede usar diferentes tipos de desplazamiento (proporcionando diferentes funciones de camera_func ), y puede seguir cualquier sprite arbitario, no solo el jugador. Incluso puedes cambiar esto en tiempo de ejecución.

Ahora para la implementación de camera_func . Un enfoque simple es simplemente centrar al jugador (o cualquier entidad que deseemos seguir) en la pantalla, y la implementación es directa:

def simple_camera(camera, target_rect): l, t, _, _ = target_rect # l = left, t = top _, _, w, h = camera # w = width, h = height return Rect(-l+HALF_WIDTH, -t+HALF_HEIGHT, w, h)

Simplemente tomamos la posición de nuestro target y agregamos el tamaño de pantalla medio total. Puedes intentarlo creando tu cámara así:

camera = Camera(simple_camera, total_level_width, total_level_height)

Hasta aquí todo bien. Pero tal vez no queremos ver el fondo negro fuera del nivel? Qué tal si:

def complex_camera(camera, target_rect): l, t, _, _ = target_rect _, _, w, h = camera l, t, _, _ = -l+HALF_WIDTH, -t+HALF_HEIGHT, w, h # center player l = min(0, l) # stop scrolling at the left edge l = max(-(camera.width-WIN_WIDTH), l) # stop scrolling at the right edge t = max(-(camera.height-WIN_HEIGHT), t) # stop scrolling at the bottom t = min(0, t) # stop scrolling at the top return Rect(l, t, w, h)

Aquí simplemente utilizamos las funciones min / max para asegurarnos de que no nos desplazamos fuera del nivel.

Pruébalo creando tu cámara así:

camera = Camera(complex_camera, total_level_width, total_level_height)

Hay una pequeña animación de nuestro nuevo desplazamiento en acción:

Aquí está el código completo nuevamente (nota que cambié tu nivel un poco para ser más grande y tener algunas plataformas más):

#! /usr/bin/python import pygame from pygame import * WIN_WIDTH = 800 WIN_HEIGHT = 640 HALF_WIDTH = int(WIN_WIDTH / 2) HALF_HEIGHT = int(WIN_HEIGHT / 2) DISPLAY = (WIN_WIDTH, WIN_HEIGHT) DEPTH = 32 FLAGS = 0 CAMERA_SLACK = 30 def main(): global cameraX, cameraY pygame.init() screen = pygame.display.set_mode(DISPLAY, FLAGS, DEPTH) pygame.display.set_caption("Use arrows to move!") timer = pygame.time.Clock() up = down = left = right = running = False bg = Surface((32,32)) bg.convert() bg.fill(Color("#000000")) entities = pygame.sprite.Group() player = Player(32, 32) platforms = [] x = y = 0 level = [ "PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP", "P P", "P P", "P P", "P PPPPPPPPPPP P", "P P", "P P", "P P", "P PPPPPPPP P", "P P", "P PPPPPPP P", "P PPPPPP P", "P P", "P PPPPPPP P", "P P", "P PPPPPP P", "P P", "P PPPPPPPPPPP P", "P P", "P PPPPPPPPPPP P", "P P", "P P", "P P", "P P", "PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP",] # build the level for row in level: for col in row: if col == "P": p = Platform(x, y) platforms.append(p) entities.add(p) if col == "E": e = ExitBlock(x, y) platforms.append(e) entities.add(e) x += 32 y += 32 x = 0 total_level_width = len(level[0])*32 total_level_height = len(level)*32 camera = Camera(complex_camera, total_level_width, total_level_height) entities.add(player) while 1: timer.tick(60) for e in pygame.event.get(): if e.type == QUIT: raise SystemExit, "QUIT" if e.type == KEYDOWN and e.key == K_ESCAPE: raise SystemExit, "ESCAPE" if e.type == KEYDOWN and e.key == K_UP: up = True if e.type == KEYDOWN and e.key == K_DOWN: down = True if e.type == KEYDOWN and e.key == K_LEFT: left = True if e.type == KEYDOWN and e.key == K_RIGHT: right = True if e.type == KEYDOWN and e.key == K_SPACE: running = True if e.type == KEYUP and e.key == K_UP: up = False if e.type == KEYUP and e.key == K_DOWN: down = False if e.type == KEYUP and e.key == K_RIGHT: right = False if e.type == KEYUP and e.key == K_LEFT: left = False # draw background for y in range(32): for x in range(32): screen.blit(bg, (x * 32, y * 32)) camera.update(player) # update player, draw everything else player.update(up, down, left, right, running, platforms) for e in entities: screen.blit(e.image, camera.apply(e)) pygame.display.update() class Camera(object): def __init__(self, camera_func, width, height): self.camera_func = camera_func self.state = Rect(0, 0, width, height) def apply(self, target): return target.rect.move(self.state.topleft) def update(self, target): self.state = self.camera_func(self.state, target.rect) def simple_camera(camera, target_rect): l, t, _, _ = target_rect _, _, w, h = camera return 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-WIN_WIDTH), l) # stop scrolling at the right edge t = max(-(camera.height-WIN_HEIGHT), t) # stop scrolling at the bottom t = min(0, t) # stop scrolling at the top return Rect(l, t, w, h) class Entity(pygame.sprite.Sprite): def __init__(self): pygame.sprite.Sprite.__init__(self) class Player(Entity): def __init__(self, x, y): Entity.__init__(self) self.xvel = 0 self.yvel = 0 self.onGround = False self.image = Surface((32,32)) self.image.fill(Color("#0000FF")) self.image.convert() self.rect = Rect(x, y, 32, 32) def update(self, up, down, left, right, running, platforms): if up: # only jump if on the ground if self.onGround: self.yvel -= 10 if down: pass if running: self.xvel = 12 if left: self.xvel = -8 if right: self.xvel = 8 if not self.onGround: # only accelerate with gravity if in the air self.yvel += 0.3 # max falling speed if self.yvel > 100: self.yvel = 100 if not(left or right): self.xvel = 0 # increment in x direction self.rect.left += self.xvel # do x-axis collisions self.collide(self.xvel, 0, platforms) # increment in y direction self.rect.top += self.yvel # assuming we''re in the air self.onGround = False; # do y-axis collisions self.collide(0, self.yvel, platforms) def collide(self, xvel, yvel, platforms): for p in platforms: if pygame.sprite.collide_rect(self, p): if isinstance(p, ExitBlock): pygame.event.post(pygame.event.Event(QUIT)) if xvel > 0: self.rect.right = p.rect.left print "collide right" if xvel < 0: self.rect.left = p.rect.right print "collide left" if yvel > 0: self.rect.bottom = p.rect.top self.onGround = True self.yvel = 0 if yvel < 0: self.rect.top = p.rect.bottom class Platform(Entity): def __init__(self, x, y): Entity.__init__(self) self.image = Surface((32, 32)) self.image.convert() self.image.fill(Color("#DDDDDD")) self.rect = Rect(x, y, 32, 32) def update(self): pass class ExitBlock(Platform): def __init__(self, x, y): Platform.__init__(self, x, y) self.image.fill(Color("#0033FF")) if __name__ == "__main__": main()


La única forma de hacerlo es separar posiciones lógicas en el mapa, desde posiciones físicas en la pantalla.

Cualquier código relacionado con dibujar realmente su mapa en la pantalla, en su caso todos los atributos .rect de sus sprites, tiene que hacerlo en base a un desplazamiento de qué parte del mapa está realmente usando la pantalla.

Por ejemplo, su pantalla podría mostrar su mapa comenzando con la posición (10,10) en la esquina superior izquierda - todos los códigos relacionados con la visualización (que en el caso anterior son los atributos .rect ) deben restar el desplazamiento de la pantalla de la posición lógica actual - (supongamos que el carácter está en coords de mapa (12,15) - por lo tanto, debe dibujarse en (12,15) - (10, 10) -> (2, 5) * BLOCK_SIZE) En el ejemplo anterior, BLOCK_SIZE está codificado a 32,32, por lo que desea dibujarlo en la posición de píxel físico (2 * 32, 5 * 32) en la pantalla)

(sugerencia: evite codificar las cosas de esta manera, haga una declaración constante al comienzo de su código)