Столкновение спрайтов стены Pygame

Я делаю рогаликовую игру, но я новичок в программировании. У меня уже есть движущийся персонаж, мои спрайты на стенах и полу, но в моем коде есть некоторая ошибка, которая позволяет персонажу перемещаться через стены.

Я использовал block_path, чтобы выбрать между плиткой пола и стены, и затем попытался использовать его, чтобы распознать стену, но на самом деле это не сработало.

Далее вы можете увидеть мой код:

screenWidth = 800
screenHeight = 600
mapHeight = 30
mapWidth = 30
cellWidth = 32
cellHeight = 32

screen = pygame.display.set_mode((screenWidth, screenHeight))
walkRight = [pygame.image.load('model/r1.png'), pygame.image.load('model/r2.png'), pygame.image.load('model/r3.png'), pygame.image.load('model/r4.png'), pygame.image.load('model/r5.png'), pygame.image.load('model/r6.png')]
walkLeft = [pygame.image.load('model/l1.png'), pygame.image.load('model/l2.png'), pygame.image.load('model/l3.png'), pygame.image.load('model/l4.png'), pygame.image.load('model/l5.png'), pygame.image.load('model/l6.png')]
walkUp = [pygame.image.load('model/u1.png'), pygame.image.load('model/u2.png'), pygame.image.load('model/u3.png'), pygame.image.load('model/u4.png'), pygame.image.load('model/u5.png'), pygame.image.load('model/u6.png')]
Floor = pygame.image.load("map/floor.jpg")
wallRight = pygame.image.load("map/rightwall.png")

`

class struc_Tile():
    def __init__(self,block_path):
        self.block_path = block_path`

class player(object):
    def __init__(self,x,y,width,height):
        self.x = x
        self.y = y
        self.width = width
        self.height = height
        self.vel = 5
        self.left = False
        self.right = False
        self.up = False
        self.down = False
        self.walkCount = 0


    def draw(self,screen):
        if self.walkCount + 1 >= 18:
            self.walkCount = 0

        elif self.left:
            screen.blit(walkLeft[self.walkCount//3], (self.x,self.y))
            self.walkCount += 1
        elif self.right:
            screen.blit(walkRight[self.walkCount//3], (self.x,self.y))
            self.walkCount += 1
        elif self.up:
            screen.blit(walkUp[self.walkCount//3], (self.x,self.y))
            self.walkCount += 1
        elif self.down:
            screen.blit(walkDown[self.walkCount//3], (self.x,self.y))
            self.walkCount += 1
        else:
            screen.blit(Standing[self.walkCount//3], (self.x,self.y))
            self.walkCount = 0

    def move(self,dx,dy):
        if gamemap[self.x + dx][self.y + dy].block_path == False:
            self.x += dx
            self.y += dy

def createmap():
    newmap = [[struc_Tile(False) for y in range(0,mapHeight)] for x in range (0,mapWidth) ]
    newmap[10][10].block_path = True
    newmap[10][15].block_path = True

    return newmap
        def drawmap(maptodraw):

    for x in range(0,mapWidth):
        for y in range(0,mapHeight):
            if maptodraw[x][y].block_path == True:
                screen.blit(wallRight, (x*cellWidth, y*cellHeight))
            else:
                screen.blit(Floor, (x*cellWidth, y*cellHeight)
def redrawgamewindow():
screen.blit(bg, (0, 0))
drawmap(gamemap)
character.draw(screen)
pygame.display.update()

pygame.init()
gamemap = createmap()
clock = pygame.time.Clock()
character = player(0, 0, 32,32)

run = True
while run:
clock.tick(18)

for event in pygame.event.get():
    if event.type == pygame.QUIT:

        run = False

keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT] and character.x > character.vel:
    character.x -= character.vel
    character.left = True
    character.right = False
    character.up = False
    character.down = False
    character.standing = False

elif keys[pygame.K_RIGHT] and character.x < 800 -character.width - character.vel:
    character.x += character.vel
    character.left = False
    character.right = True
    character.up = False
    character.down = False
    character.standing = False

elif keys[pygame.K_UP] and character.y > character.vel:
    character.y -= character.vel
    character.left = False
    character.right = False
    character.up = True
    character.down = False
    character.standing = False
elif keys[pygame.K_DOWN] and character.y < 600 - character.height - character.vel:
    character.y += character.vel
    character.left = False
    character.right = False
    character.up = False
    character.down = True
    character.standing = False
else:
    character.right = False
    character.left = False
    character.up = False
    character.down = False
    character.standing = True

redrawgamewindow()

person Jose Vaz    schedule 17.02.2020    source источник
comment
В createmap() вы блокируете только 2 плитки, поэтому единственный раз, когда ваш путь может быть заблокирован, это если вы пытаетесь переместиться через одну из этих 2 плиток.   -  person Axe319    schedule 17.02.2020
comment
Также довольно сложно воспроизвести то, что происходит с вашим кодом, не увидев еще чего-то.   -  person Axe319    schedule 17.02.2020
comment
Я добавил остальную часть своего кода. Я попытался переместить эти 2 плитки, но они не были заблокированы.   -  person Jose Vaz    schedule 17.02.2020
comment
Что установлено mapHeight и mapWidth? У вас никогда не было таких примеров.   -  person Axe319    schedule 17.02.2020
comment
Мое намерение состоит в том, чтобы они отображали размер карты (количество плиток), например, моя карта 30x30 плиток.   -  person Jose Vaz    schedule 17.02.2020


Ответы (1)


Изменение вашей функции createmap() на что-то вроде этого создаст 5-пиксельный буфер на расточке вашей карты. Причина, по которой я сделал это 5 пикселей, заключается в том, что это движение вашего персонажа.

Я бы посоветовал добавить какое-то фактическое столкновение персонажей, чтобы проверить, выходим ли вы за пределы, независимо от скорости.

РЕДАКТИРОВАТЬ: Я включил полный код, поскольку внесенные мной изменения легче понять, если я их покажу.

Я изменил ваши списки изображений на список, потому что их так легче назвать. Сетка, которую вы просили, имеет размер 30x30. Поскольку вы показали ширину ячейки 32x32, я изменил карту на 960 x 960.

Я расширил ваш метод перемещения, чтобы проверить наличие столкновений, чтобы увидеть, может ли он двигаться раньше, чем это произойдет. Я также удалил вашу redraw() функцию, так как было легче просто переместить вашу перерисовку вниз. При желании вы можете добавить его обратно, но в этом примере я удалил его.

import pygame

# Based on the paths in your image
# This should work for your image paths
image = pygame.image.load('model/standing1.png')
Standing = pygame.transform.scale(image, (32, 32))
image = pygame.image.load('map/floor.jpg')
Floor = pygame.transform.scale(image, (32, 32))
image = pygame.image.load('map/rightwall.png')
wallRight = pygame.transform.scale(image, (32, 32))

walkLeft = []
walkRight = []
walkUp = []
walkDown = []

for i in range(1, 19):
    image = pygame.image.load('model/r' + str(i) + '.png')
    walkRight.append(pygame.transform.scale(image, (32, 32)))

    image = pygame.image.load('model/l' + str(i) + '.png')
    walkLeft.append(pygame.transform.scale(image, (32, 32)))

    image = pygame.image.load('model/u' + str(i) + '.png')
    walkUp.append(pygame.transform.scale(image, (32, 32)))

    image = pygame.image.load('model/d' + str(i) + '.png')
    walkDown.append(pygame.transform.scale(image, (32, 32)))

class struc_Tile():
    def __init__(self, block_path):
        self.block_path = block_path

class player(object):
    def __init__(self,x,y,width,height):
        self.x = x
        self.y = y
        self.width = width
        self.height = height
        self.vel = 5
        self.walkCount = 0

        # do something like this with your images
        # if you keep your lists in a dict you can avoid
        # all your boolean direction variables and 
        # the lengthy if else statement
        self.images = {}
        self.images['walkleft'] = walkLeft[:]
        self.images['walkright'] = walkRight[:]
        self.images['walkup'] = walkUp[:]
        self.images['walkdown'] = walkDown[:]


    def draw(self, screen, direction):
        if self.walkCount + 1 >= 18:
            self.walkCount = 0

        # since standing is not in your dict check for that first
        if direction == 'standing':
            screen.blit(Standing, (self.x,self.y))
            self.walkCount = 0
        else:
            screen.blit(self.images[direction][self.walkCount], (self.x,self.y))
            self.walkCount += 1


    def can_move(self, dx, dy):
        # with the buffer created around the border of the map
        # you shouldn't have issues with
        # index out of bounds exceptions
        # EDIT: added better collision
        new_x = self.x + dx
        new_y = self.y + dy
        if gamemap[new_x][new_y].block_path == False:
            if gamemap[new_x + cellWidth][new_y].block_path == False:
                if gamemap[new_x][new_y + cellHeight].block_path == False:
                    if gamemap[new_x + cellWidth][new_y + cellHeight].block_path == False:
                        self.x += dx
                        self.y += dy
                        return True

def createmap():
    newmap = [[struc_Tile(False) for y in range(0, mapHeight)] for x in range (0,mapWidth)]

    # give our upper/left borders a cell width buffer
    # and our bottom/right borders a 2 cell width buffer
    # since blit uses the upper left corner this should account
    # for the sprite width
    # EDIT: Fixed this to accommodate the better collision
    for x in range(0, mapWidth):
        for y in range (0, mapHeight):
            if y < 32 or y + cellWidth >= mapHeight:
                newmap[x][y].block_path = True
            elif x < 32 or x + cellWidth >= mapWidth:
                newmap[x][y].block_path = True


    return newmap

def drawmap(maptodraw):
    # only blit at cellwidth and height intervals
    for x in range(0, mapWidth, cellWidth):
        for y in range(0, mapHeight, cellHeight):
            if maptodraw[x][y].block_path == True:
                screen.blit(wallRight, (x, y))
            else:
                screen.blit(Floor, (x, y))

# Added this function which lets you block or unblock a cell
# simply call like gamemap = block_element(5, 10, gamemap)
# this will block the 6th cell on the x axis and 11th on the y axis
# to unblock a cell call it with block=False
def block_element(x, y, maptoblock, block=True):
    x_cells = int(mapWidth / cellWidth)
    y_cells = int(mapHeight / cellHeight)

    start_x = int(x * cellWidth)
    start_y = int(y * cellHeight)

    end_x = start_x + cellWidth
    end_y = start_y + cellHeight

    print(start_x, end_x)

    if x >= 0 and x < x_cells:
        if y >= 0 and y < y_cells:
            for x in range(start_x, end_x):
                for y in range(start_y, end_y):
                    maptoblock[x][y].block_path = block

    return maptoblock   

pygame.init()

mapHeight = 960
mapWidth = 960

cellWidth = 32
cellHeight = 32

screen = pygame.display.set_mode((mapWidth, mapHeight))

gamemap = createmap()
# blocking/unblocking example
gamemap = block_element(5, 10, gamemap)
gamemap = block_element(0, 8, gamemap, block=False)
gamemap = block_element(0, 9, gamemap, block=False)

clock = pygame.time.Clock()
character = player(64, 64, 32, 32)

run = True
while run:
    clock.tick(18)
    pygame.display.update()

    for event in pygame.event.get():
        if event.type == pygame.QUIT:

            run = False
    # I just used fill since I didn't have a bg image handy
    screen.fill((0, 0, 0))
    drawmap(gamemap)

    # handle the keypresses
    # first check if the character can move that much
    # then draw the movement
    keys = pygame.key.get_pressed()
    if keys[pygame.K_LEFT] and character.can_move(0 - character.vel, 0):
        character.draw(screen, 'walkleft')
    elif keys[pygame.K_RIGHT] and character.can_move(character.vel, 0):
        character.draw(screen, 'walkright')
    elif keys[pygame.K_UP] and character.can_move(0, 0 - character.vel):
        character.draw(screen, 'walkup')
    elif keys[pygame.K_DOWN] and character.can_move(0, character.vel):
        character.draw(screen, 'walkdown')
    else:
        character.draw(screen, 'standing')

person Axe319    schedule 17.02.2020
comment
gyazo.com/57291d59ebd8fa48a7b1a93303d42300 Это то, что я получил, когда менял границы, у меня никогда не было проблем моей карты, потому что character.x ‹800 -character.width - character.vel заботится об этом. Моя единственная проблема - заставить плитку на стене блокировать моего персонажа. Возможно, мне стоит попытаться найти другой способ создания моей карты. - person Jose Vaz; 17.02.2020
comment
Я потратил два часа, пытаясь использовать предоставленный вами код, но каким-то образом мне удалось не заставить его работать. Каждый раз, когда я нажимаю клавишу для перемещения, происходит сбой, например, если я нажимаю правую клавишу, он вызывает эти 2 строки character.draw (screen, 'walkright') и screen.blit (self.images [direction] [self.walkCount], ( self.x, self.y)) Вот как я вставил свои спрайты gyazo.com/cccd92e3c04973b3ff16d4de7a710 не трогай больше ничего, idk, если мне нужно коснуться словаря внутри class player. Последний вопрос, как я могу вставить стены по выбранной координате - person Jose Vaz; 18.02.2020
comment
Я вижу вашу проблему, но сейчас еду домой. Я займусь этим, когда вернусь домой. - person Axe319; 18.02.2020
comment
@JoseVaz Я обновил свой пример путями, которые вы указали для своих изображений. Если вы не можете заставить его работать за 15 минут или около того, дайте мне знать, и мы с этим справимся. - person Axe319; 18.02.2020
comment
Отлично, ты спас меня. Теперь мне просто нужно выяснить, как сделать стены в середине карты, но это не должно быть слишком сложно. Большое спасибо - person Jose Vaz; 18.02.2020
comment
Нет проблем @JoseVaz. Я добавил функцию, которая позволяет вам блокировать или разблокировать любую ячейку, которую вы выбираете в своей сетке 30x30. - person Axe319; 18.02.2020