2025, Oct 23 01:16

Как исправить исчезновение спрайта в Pygame из-за set_colorkey

Спрайт в Pygame исчезает? Разбираем причину: set_colorkey делает заливку прозрачной. Покажем минимальное исправление и как вернуть управление по оси Y.

Когда спрайт в Pygame загадочно пропадает, виноват обычно не рендерер и не цикл событий. В нашем случае всему виной одна строка, которая делает весь спрайт прозрачным. Разберёмся, что происходит, почему синий блок не видно и как аккуратно это исправить, не меняя поведение игры.

Как выглядит проблема

Ниже — фрагмент кода, при котором синий блок не отображается. Программа запускается, цикл работает, надпись рисуется, но спрайт так и не появляется.

import sys
import pygame
SCR_W, SCR_H = 800, 600
CLR_BLUE = (0, 0, 255)
CLR_WHITE = (255, 255, 255)
CLR_GREEN = (0, 255, 0)
window = pygame.display.set_mode((SCR_W, SCR_H))
pygame.display.set_caption("I wanna make a game")
tile_sz = 50
pos_x, pos_y = SCR_W // 2, SCR_H // 2
velocity = 5
class Block(pygame.sprite.Sprite):
    def __init__(self, shade, h, w):
        super().__init__()
        self.image = pygame.Surface([tile_sz, tile_sz])
        self.image.fill(CLR_BLUE)
        self.image.set_colorkey(CLR_BLUE)
        pygame.draw.rect(self.image, shade, pygame.Rect(0, 0, w, h))
        self.rect = self.image.get_rect()
    def shift_right(self, px):
        self.rect.x += px
    def shift_left(self, px):
        self.rect.x -= px
    def shift_up(self, spd):
        self.rect.y += spd * spd / 10
    def shift_down(self, spd):
        self.rect.y -= spd * spd / 10
pygame.init()
pygame.font.init()
hero = Block(CLR_BLUE, 30, 30)
sprite_group = pygame.sprite.Group()
sprite_group.add(hero)
hero.rect.x = 50
hero.rect.y = 50
alive = True
while alive:
    for evt in pygame.event.get():
        if evt.type == pygame.QUIT:
            alive = False
    label_font = pygame.font.SysFont('arial.ttf', 40)
    label_touch = label_font.render('Touch me', True, CLR_WHITE)
    keys = pygame.key.get_pressed()
    if keys[pygame.K_w]:
        hero.shift_up(10)
    if keys[pygame.K_s]:
        hero.shift_down(10)
    if keys[pygame.K_a]:
        hero.shift_left(10)
    if keys[pygame.K_d]:
        hero.shift_right(10)
    pos_x = max(0, min(SCR_W - tile_sz, pos_x))
    pos_y = max(0, min(SCR_H - tile_sz, pos_y))
    window.fill((0, 0, 0))
    window.blit(label_touch, (10, 300))
    sprite_group.update()
    sprite_group.draw(window)
    pygame.display.flip()
    pygame.time.Clock().tick(60)
pygame.quit()
sys.exit()

Почему спрайт невидим

Корневая причина — прозрачность по цветовому ключу. В Pygame Surface метод set_colorkey помечает указанный цвет как полностью прозрачный. Здесь поверхность залита синим, и этот же синий установлен как прозрачный. В результате весь спрайт становится прозрачным, и при отрисовке не остаётся ни одного видимого пикселя.

Просто удалите self.image.set_colorkey(BLUE)

Есть и вторая проблема: вертикальное движение инвертировано. Увеличение y сдвигает спрайт вниз по экрану. Текущие методы перемещения отправляют блок вниз при нажатии «вверх» и вверх при нажатии «вниз».

Минимальное исправление

Чтобы вернуть видимость спрайта, удалите цветовой ключ, совпадающий с цветом заливки. Чтобы управление было интуитивным, поменяйте направление по оси Y в обработчиках вверх/вниз: при нажатии вверх уменьшаем y, при нажатии вниз увеличиваем.

import pygame, sys
pygame.init()
pygame.font.init()
SCR_W, SCR_H = 800, 600
CLR_BLUE = (0, 0, 255)
CLR_WHITE = (255, 255, 255)
CLR_GREEN = (0, 255, 0)
window = pygame.display.set_mode((SCR_W, SCR_H))
pygame.display.set_caption("I wanna make a game")
tile_sz = 50
velocity = 5
class Block(pygame.sprite.Sprite):
    def __init__(self, shade, h, w):
        super().__init__()
        self.image = pygame.Surface([tile_sz, tile_sz])
        self.image.fill(shade)
        self.rect = self.image.get_rect()
    def shift_right(self, px):
        self.rect.x += px
    def shift_left(self, px):
        self.rect.x -= px
    def shift_up(self, spd):
        self.rect.y -= spd * spd / 10
    def shift_down(self, spd):
        self.rect.y += spd * spd / 10
hero = Block(CLR_BLUE, 30, 30)
sprite_group = pygame.sprite.Group()
sprite_group.add(hero)
hero.rect.x = 50
hero.rect.y = 50
alive = True
while alive:
    for evt in pygame.event.get():
        if evt.type == pygame.QUIT:
            alive = False
    label_font = pygame.font.SysFont('arial', 40)
    label_touch = label_font.render('Touch me', True, CLR_WHITE)
    keys = pygame.key.get_pressed()
    if keys[pygame.K_w]:
        hero.shift_up(5)
    if keys[pygame.K_s]:
        hero.shift_down(5)
    if keys[pygame.K_a]:
        hero.shift_left(5)
    if keys[pygame.K_d]:
        hero.shift_right(5)
    window.fill((0, 0, 0))
    window.blit(label_touch, (10, 300))
    sprite_group.update()
    sprite_group.draw(window)
    pygame.display.flip()
    pygame.time.Clock().tick(60)
pygame.quit()
sys.exit()

Почему это важно помнить

Ошибки с прозрачностью коварно просты, но отнимают массу времени на поиск. Одного set_colorkey на равномерно залитой поверхности достаточно, чтобы визуально «сломать» корректный пайплайн рендеринга. Точно так же перепутать вертикальное направление очень легко — и управление незаметно становится обратным ожидаемому. Понимание того, что цветовой ключ скрывает каждый пиксель этого цвета, а «вверх» в этой системе координат означает уменьшение y, помогает быстрее разбираться с проблемами рендеринга и ввода.

Итоги

Если спрайт в Pygame исчезает, проверьте, не совпадает ли цвет заливки поверхности с цветом, указанным в colorkey. Удаление этой строки вернёт спрайт на экран. Затем убедитесь, что направления по осям настроены верно и управление соответствует замыслу. Держите эти две проверки в голове — они быстро выявляют проблему, избегают путаницы и заметно ускоряют цикл итераций.

Статья основана на вопросе на StackOverflow от Dat Nguyen Tien и ответе от Uchenna Adubasim.