Pygame спрайт исчезает с LayeredUpdates по определенной координате Y - PullRequest
1 голос
/ 02 мая 2020

Я делаю эту игру с Pygame, и у меня есть player спрайт, нарисованный с помощью LayeredUpdates. _layer для игрока равно 1, и по какой-то причине, когда он достигает определенной точки по координате y, он просто исчезает. Спрайт ground имеет _layer из 2, поэтому спрайт player всегда должен быть сверху. Странно, что он просто исчезает с определенной координатой y.

main.py:

import pygame
from sprites import *
from settings import *
from os import path

class Game:
    def __init__(self):
        pygame.init()
        self.screen = pygame.display.set_mode((WIN_WIDTH, WIN_HEIGHT))
        pygame.display.set_caption(TITLE)
        pygame.key.set_repeat(500, 100)
        self.clock = pygame.time.Clock()
        self.load_data()

    def load_data(self):
        main_folder = path.dirname(__file__)
        img_dir = path.join(main_folder, 'img')
        self.map_data = []
        with open(path.join(main_folder, 'map.txt'), 'rt') as f:
            for line in f:
                self.map_data.append(line)
        self.spritesheet = Spritesheet(path.join(img_dir, SPRITESHEET))

    def new(self):
        self.all_sprites = pygame.sprite.LayeredUpdates()
        self.blocks = pygame.sprite.Group()
        self.grounds = pygame.sprite.Group()
        for row, tiles in enumerate(self.map_data):
            for col, tile in enumerate(tiles):
                Ground(self, col, row)
                if tile == 'a':
                    Block(self, col, row)
                if tile == 'P':
                    self.player = Player(self, col, row)


    def run(self):
        self.playing = True
        while self.playing:
            self.events()
            self.update()
            self.draw()

    def events(self):
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_w:
                    self.player.move(0,-1)
                if event.key == pygame.K_a:
                    self.player.move(-1,0)
                if event.key == pygame.K_s:
                    self.player.move(0,1)
                if event.key == pygame.K_d:
                    self.player.move(1,0)


    def update(self):
        self.screen.fill(BLACK)
        self.all_sprites.update()
        self.clock.tick(FPS)

    def draw(self):
        self.all_sprites.draw(self.screen)
        pygame.display.update()

g = Game()
while True:
    g.new()
    g.run()

sprites.py

import pygame
from settings import *

class Spritesheet:
    def __init__(self, filename):
        self.spritesheet = pygame.image.load(filename).convert()

    def get_image(self, x, y, width, height):
        image = pygame.Surface((width, height))
        image.blit(self.spritesheet, (0,0), (x, y, width, height))
        return image

class Player(pygame.sprite.Sprite):
    def __init__(self, game, x, y):
        self.groups = game.all_sprites
        pygame.sprite.Sprite.__init__(self, self.groups)
        self.game = game
        self.image = self.game.spritesheet.get_image(0,32,32,32)
        self.image.set_colorkey(BLACK)
        self.rect = self.image.get_rect()
        self.x = x
        self.y = y
        self.dir = 'UP'
        self._layer = PLAYER_LAYER

    def move(self, x_change, y_change):
        if x_change > 0:
            self.dir = 'RIGHT'
        if x_change < 0:
            self.dir = 'LEFT'
        if y_change > 0:
            self.dir = 'DOWN'
        if y_change < 0:
            self.dir = 'UP'

        if not self.collide(x_change, y_change):
            self.x += x_change
            self.y += y_change

    def collide(self, x_change, y_change):
        for block in self.game.blocks:
            if block.x == self.x + x_change and block.y == self.y + y_change and block.collidable:
                return True
        return False

    def update(self):
        self.rect.x = self.x * SCALE
        self.rect.y = self.y * SCALE

class Block(pygame.sprite.Sprite):
    def __init__(self, game, x, y, collidable=True, groups=None):
        pygame.sprite.Sprite.__init__(self, groups or (game.all_sprites, game.blocks))
        self.game = game
        self.image = pygame.Surface((SCALE, SCALE))
        self.rect = self.image.get_rect()
        self.x = x
        self.y = y
        self.collidable = collidable

    def update(self):
        self.rect.x = self.x * SCALE
        self.rect.y = self.y * SCALE

class Ground(Block):
    def __init__(self, game, x, y, collidable=False, groups=None):
        Block.__init__(self, game, x, y, collidable, groups or (game.all_sprites, game.grounds))
        self.image = self.game.spritesheet.get_image(0, 0, 32, 32)
        self._layer = GROUND_LAYER

settings.py:

WIN_WIDTH = 640
WIN_HEIGHT = 480

SPRITESHEET = 'sheet.png'

BLACK = (0,0,0)
WHITE = (255,255,255)
RED = (255,0,0)
GREEN = (0,255,0)
BLUE = (0,0,255)

TITLE = 'Pykemon'
SCALE = 32
FPS = 60

PLAYER_LAYER = 1
TREE_LAYER = 2
GROUND_LAYER = 3

map.txt:

aaaaaaaaaaaaaaaaaaaa
a..................a
a..................a
a..................a
a..................a
a..................a
a..................a
a..................a
a..................a
a....P.............a
a..................a
a..................a
a..................a
a..................a
aaaaaaaaaaaaaaaaaaaa

Спасибо.

Ответы [ 2 ]

1 голос
/ 06 мая 2020

Ваш GROUND_LAYER равен 3, а PLAYER_LAYER равен 1. Нижние пронумерованные слои находятся под слоями с более высокими номерами, поэтому игрок будет спрятан за землю.

Вы размещаете наземные плитки на основе данных из файла 'map.txt'. Я не знаю, что это такое, но я предполагаю, что это когда игрок подходит к тому, что вы намереваетесь пройти над одной из этих наземных плиток, он фактически проходит под ним. Или, может быть, это происходит под деревом.

Попробуйте поменять порядок слоев PLAYER, TREE и GROUND.

PLAYER_LAYER = 3
TREE_LAYER = 2
GROUND_LAYER = 1

@ Rabbid76 был прав в вопросе о порядке расположения слоев в своем ответе не об атрибуте _layer или способе работы _layer. @ Rabbid76 не предполагает никаких обид, документы были на самом деле неправильными, и я только что исправил это в документах ранее сегодня. документы теперь говорят: «Если добавляемый вами спрайт имеет атрибут _layer, то этот слой будет использоваться».

Также вы можете разместить спрайт на любом уровне, который вам нравится, просто назначив _layer атрибут для любого номера. На самом деле нет такой вещи, как создание слоя. LayeredUpdates просто проходит через спрайты в порядке, указанном атрибутом _layer. В моем коде я помещаю спрайты в слой и перемещаю спрайты вверх или вниз, просто назначая атрибут _layer. Это может выглядеть так, если вы используете неправильное имя атрибута, поскольку вам нужно использовать различные методы, которые управляют слоями, чтобы он работал без установки _layer.

1 голос
/ 05 мая 2020

Слои должны быть в порядке возрастания:

PLAYER_LAYER = 3
TREE_LAYER = 2
GROUND_LAYER = 1

Когда спрайт находится в слоистой группе (pygame.sprite.LayeredUpdates), тогда слой можно изменить на switch_layer():

class Player(pygame.sprite.Sprite):
    def __init__(self, game, x, y):
        self.groups = game.all_sprites
        pygame.sprite.Sprite.__init__(self, self.groups)
        # [...]        

        game.all_sprites.change_layer(self, PLAYER_LAYER)
class Ground(Block):
    def __init__(self, game, x, y, collidable=False, groups=None):
        Block.__init__(self, game, x, y, collidable, groups or (game.all_sprites, game.grounds))
        self.image = self.game.spritesheet.get_image(0, 0, 32, 32)

        game.all_sprites.change_layer(self, GROUND_LAYER)

Обратите внимание, что изменение атрибута _layer магическим образом не меняет слой спрайта. Атрибут _layer должен быть установлен до добавления спрайта в группу. Таким образом, вы должны установить атрибут слоя до вызовов super():

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

        self._layer = PLAYER_LAYER

        self.groups = game.all_sprites
        pygame.sprite.Sprite.__init__(self, self.groups)
class Ground(Block):
    def __init__(self, game, x, y, collidable=False, groups=None):

        self._layer = GROUND_LAYER

        Block.__init__(self, game, x, y, collidable, groups or (game.all_sprites, game.grounds))
        self.image = self.game.spritesheet.get_image(0, 0, 32, 32)

В качестве альтернативы создается слой в группе, если аргумент ключевого слова layer передано add(). Пока работает следующее (если self ранее не было game.all_sprites):

game.all_sprites.add(self, layer=PLAYER_LAYER)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...