sprites for dirtysprite collide python pygame collision-detection

python - dirtysprite - sprites for pygame



Añadir detección de colisión a un plattformer en pygame (2)

Estoy trabajando en un pequeño juego de plataformas en el que colocas bloques para hacer un nivel y luego lo juegas.

Obtuve gravedad, saltos y movimientos de izquierda y derecha ... pero no estoy seguro de cómo hacer que el jugador colisione con las paredes cuando se mueve hacia la izquierda o hacia la derecha.

La forma en que quiero que funcione es así-

if key[K_LEFT]:

if not block to the left:

move to the left

Cómo voy a hacer esto (en relación con esta fuente):

import pygame,random from pygame.locals import * import itertools pygame.init() screen=pygame.display.set_mode((640,480)) class Block(object): sprite = pygame.image.load("texture//dirt.png").convert_alpha() def __init__(self, x, y): self.rect = self.sprite.get_rect(centery=y, centerx=x) class Player(object): sprite = pygame.image.load("texture//playr.png").convert() sprite.set_colorkey((0,255,0)) def __init__(self, x, y): self.rect = self.sprite.get_rect(centery=y, centerx=x) blocklist = [] player = [] colliding = False while True: screen.fill((25,30,90)) mse = pygame.mouse.get_pos() key=pygame.key.get_pressed() if key[K_LEFT]: p.rect.left-=1 if key[K_RIGHT]: p.rect.left+=1 if key[K_UP]: p.rect.top-=10 for event in pygame.event.get(): if event.type == QUIT: exit() if key[K_LSHIFT]: if event.type==MOUSEMOTION: if not any(block.rect.collidepoint(mse) for block in blocklist): x=(int(mse[0]) / 32)*32 y=(int(mse[1]) / 32)*32 blocklist.append(Block(x+16,y+16)) else: if event.type == pygame.MOUSEBUTTONUP: if event.button == 1: to_remove = [b for b in blocklist if b.rect.collidepoint(mse)] for b in to_remove: blocklist.remove(b) if not to_remove: x=(int(mse[0]) / 32)*32 y=(int(mse[1]) / 32)*32 blocklist.append(Block(x+16,y+16)) elif event.button == 3: x=(int(mse[0]) / 32)*32 y=(int(mse[1]) / 32)*32 player=[] player.append(Player(x+16,y+16)) for b in blocklist: screen.blit(b.sprite, b.rect) for p in player: if any(p.rect.colliderect(block) for block in blocklist): #collide pass else: p.rect.top += 1 screen.blit(p.sprite, p.rect) pygame.display.flip()


Como estás usando pygame, puedes usar el rect.colliderect() de rect.colliderect() para ver si el sprite del jugador está colisionando con un bloque. A continuación, realice una función que devuelva el lado de un cierto rect en relación con el otro rect:

def rect_side(rect1, rect2): # Returns side of rect1 relative to rect2. if rect1.x > rect2.x: return "right" else: return "left" # If rect1.x == rect2.x the function will return "left".

rect_side() si rect_side() devuelve "right" , restringe el movimiento hacia la izquierda y viceversa.

PD: si quieres el mismo movimiento vertical, pero también puedes comparar rect1.y a rect2.y y lidiar con las salidas "up" y "down" . Puede hacer una tupla que represente direcciones horizontales y verticales.


Un enfoque común es separar el manejo de colisiones horizontal y vertical en dos pasos separados.

Si haces esto y también rastreas la velocidad de tu jugador, es fácil saber de qué lado ocurrió una colisión.

Antes que nada, demos al jugador algunos atributos para hacer un seguimiento de su velocidad:

class Player(object): ... def __init__(self, x, y): self.rect = self.sprite.get_rect(centery=y, centerx=x) # indicates that we are standing on the ground # and thus are "allowed" to jump self.on_ground = True self.xvel = 0 self.yvel = 0 self.jump_speed = 10 self.move_speed = 8

Ahora necesitamos un método para realmente verificar una colisión. Como ya dijimos, para facilitar las cosas, usamos nuestro xvel y yvel para saber si colisionamos con nuestro lado izquierdo o derecho, etc. Esto entra en la clase Player :

def collide(self, xvel, yvel, blocks): # all blocks that we collide with for block in [blocks[i] for i in self.rect.collidelistall(blocks)]: # if xvel is > 0, we know our right side bumped # into the left side of a block etc. if xvel > 0: self.rect.right = block.rect.left if xvel < 0: self.rect.left = block.rect.right # if yvel > 0, we are falling, so if a collision happpens # we know we hit the ground (remember, we seperated checking for # horizontal and vertical collision, so if yvel != 0, xvel is 0) if yvel > 0: self.rect.bottom = block.rect.top self.on_ground = True self.yvel = 0 # if yvel < 0 and a collision occurs, we bumped our head # on a block above us if yvel < 0: self.rect.top = block.rect.bottom

A continuación, movemos nuestro manejo de movimiento a la clase Player . Así que creemos en el objeto que realiza un seguimiento de la entrada. Aquí, uso una namedtuple , porque por qué no.

from collections import namedtuple ... max_gravity = 100 Move = namedtuple(''Move'', [''up'', ''left'', ''right'']) while True: screen.fill((25,30,90)) mse = pygame.mouse.get_pos() key = pygame.key.get_pressed() for event in pygame.event.get(): ... move = Move(key[K_UP], key[K_LEFT], key[K_RIGHT]) for p in player: p.update(move, blocklist) screen.blit(p.sprite, p.rect)

Pasamos la blocklist de blocklist al método de update del Player para que podamos verificar la colisión. Usando el objeto move , ahora sabemos dónde debe moverse el jugador, así que implementemos Player.update :

def update(self, move, blocks): # check if we can jump if move.up and self.on_ground: self.yvel -= self.jump_speed # simple left/right movement if move.left: self.xvel = -self.move_speed if move.right: self.xvel = self.move_speed # if in the air, fall down if not self.on_ground: self.yvel += 0.3 # but not too fast if self.yvel > max_gravity: self.yvel = max_gravity # if no left/right movement, x speed is 0, of course if not (move.left or move.right): self.xvel = 0 # move horizontal, and check for horizontal collisions self.rect.left += self.xvel self.collide(self.xvel, 0, blocks) # move vertically, and check for vertical collisions self.rect.top += self.yvel self.on_ground = False; self.collide(0, self.yvel, blocks)

Lo único que queda es usar un Clock para limitar la velocidad de fotogramas para que el juego funcione a una velocidad constante. Eso es.

Aquí está el código completo:

import pygame,random from pygame.locals import * from collections import namedtuple pygame.init() clock=pygame.time.Clock() screen=pygame.display.set_mode((640,480)) max_gravity = 100 class Block(object): sprite = pygame.image.load("dirt.png").convert_alpha() def __init__(self, x, y): self.rect = self.sprite.get_rect(centery=y, centerx=x) class Player(object): sprite = pygame.image.load("dirt.png").convert() sprite.set_colorkey((0,255,0)) def __init__(self, x, y): self.rect = self.sprite.get_rect(centery=y, centerx=x) # indicates that we are standing on the ground # and thus are "allowed" to jump self.on_ground = True self.xvel = 0 self.yvel = 0 self.jump_speed = 10 self.move_speed = 8 def update(self, move, blocks): # check if we can jump if move.up and self.on_ground: self.yvel -= self.jump_speed # simple left/right movement if move.left: self.xvel = -self.move_speed if move.right: self.xvel = self.move_speed # if in the air, fall down if not self.on_ground: self.yvel += 0.3 # but not too fast if self.yvel > max_gravity: self.yvel = max_gravity # if no left/right movement, x speed is 0, of course if not (move.left or move.right): self.xvel = 0 # move horizontal, and check for horizontal collisions self.rect.left += self.xvel self.collide(self.xvel, 0, blocks) # move vertically, and check for vertical collisions self.rect.top += self.yvel self.on_ground = False; self.collide(0, self.yvel, blocks) def collide(self, xvel, yvel, blocks): # all blocks that we collide with for block in [blocks[i] for i in self.rect.collidelistall(blocks)]: # if xvel is > 0, we know our right side bumped # into the left side of a block etc. if xvel > 0: self.rect.right = block.rect.left if xvel < 0: self.rect.left = block.rect.right # if yvel > 0, we are falling, so if a collision happpens # we know we hit the ground (remember, we seperated checking for # horizontal and vertical collision, so if yvel != 0, xvel is 0) if yvel > 0: self.rect.bottom = block.rect.top self.on_ground = True self.yvel = 0 # if yvel < 0 and a collision occurs, we bumped our head # on a block above us if yvel < 0: self.rect.top = block.rect.bottom blocklist = [] player = [] colliding = False Move = namedtuple(''Move'', [''up'', ''left'', ''right'']) while True: screen.fill((25,30,90)) mse = pygame.mouse.get_pos() key = pygame.key.get_pressed() for event in pygame.event.get(): if event.type == QUIT: exit() if key[K_LSHIFT]: if event.type==MOUSEMOTION: if not any(block.rect.collidepoint(mse) for block in blocklist): x=(int(mse[0]) / 32)*32 y=(int(mse[1]) / 32)*32 blocklist.append(Block(x+16,y+16)) else: if event.type == pygame.MOUSEBUTTONUP: if event.button == 1: to_remove = [b for b in blocklist if b.rect.collidepoint(mse)] for b in to_remove: blocklist.remove(b) if not to_remove: x=(int(mse[0]) / 32)*32 y=(int(mse[1]) / 32)*32 blocklist.append(Block(x+16,y+16)) elif event.button == 3: x=(int(mse[0]) / 32)*32 y=(int(mse[1]) / 32)*32 player=[] player.append(Player(x+16,y+16)) move = Move(key[K_UP], key[K_LEFT], key[K_RIGHT]) for b in blocklist: screen.blit(b.sprite, b.rect) for p in player: p.update(move, blocklist) screen.blit(p.sprite, p.rect) clock.tick(60) pygame.display.flip()

Tenga en cuenta que cambié los nombres de las imágenes, así que solo necesito un solo archivo de imagen para probar esto. Además, no sé por qué mantienes al jugador en una lista, pero aquí hay una buena animación de nuestro juego en acción: