背景图像更改故障

Background image change glitches

我试着用 Pygame 制作一个简单的关卡,它会在玩家移动到特定位置时改变背景(一个简单的 level/scene 改变)。

启动时,我的第一个背景绘制正确。当我更改背景(移动到左上角)时,第二个背景被正确绘制。但是当我移动到屏幕上的特定位置时,在透明精灵(使用 set_keycolor 设置透明)后面绘制的不是我的第二个(正确的)背景,而是最初的第一个背景。

我需要明确清除屏幕对象的背景吗?我必须删除屏幕对象,然后创建一个全新的吗?

program.py

#!/usr/bin/env python
import pygame

from player import Character

# see if we can load more than standard BMP
if not pygame.image.get_extended():
    raise SystemExit("Sorry, extended image module required")

# globals
id = 1

# main
def main(winstyle=0):
    # Initialize pygame
    pygame.init()

    SCREENRECT = pygame.Rect(0, 0, 630, 450)
    winstyle = 0  # |FULLSCREEN
    bestdepth = pygame.display.mode_ok(SCREENRECT.size, winstyle, 32)
    screen = pygame.display.set_mode(SCREENRECT.size, winstyle, bestdepth)

    BG_TILE_SIZE = 90

    # decorate the game window
    pygame.display.set_caption("Pygame TEST")
    pygame.mouse.set_visible(0)

    # create the background, tile the bgd image
    background = pygame.Surface(SCREENRECT.size)
    background.fill((0,0,255))
    screen.blit(background, (0, 0))
    pygame.display.flip()

    # Initialize Game Groups
    all = pygame.sprite.RenderUpdates()

    # assign default groups to each sprite class
    Character.containers = all

    # Create Some Starting Values
    clock = pygame.time.Clock()

    # initialize our starting sprites
    player = Character(SCREENRECT)

    # Run our main loop whilst the player is alive.
    while player.alive():

        # get input
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                return
            if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
                return

        # update all the sprites
        all.update()

        # handle player input
        player.move(pygame.key.get_pressed(), SCREENRECT)

        # change background
        indexX, indexY = player.rect.center
        if checkPlayerPosition(indexY, screen, background):
            player.rect = player.image.get_rect(center=SCREENRECT.center)

        # clear/erase the last drawn sprites
        all.clear(screen, background)

        # draw the scene
        dirty = all.draw(screen)
        pygame.display.update(dirty)

        # cap the framerate at 60fps. Also called 60HZ or 60 times per second.
        clock.tick(60)

    pygame.time.wait(1000)

# change the background
def checkPlayerPosition(indexY, screen, background):
    global id
    if id == 1:
        if indexY < 90:
            screen.fill((0,0,0))
            pygame.display.flip()
            background.fill((0,0,255))
            id = -1
            return True
    else:
        if indexY > 360:
            screen.fill((0,0,255))
            pygame.display.flip()
            background.fill((0,0,0))
            id = 1
            return True
    return False

# call the "main" function if running this script
if __name__ == "__main__":
    main()
    pygame.quit()

这是我的精灵的代码。

player.py:

import os
import pygame

class Character(pygame.sprite.Sprite):
    # fields
    speed = 4
    run = 1
    images = []

    # constructor
    def __init__(self, SCREENRECT):
        pygame.sprite.Sprite.__init__(self, self.containers)
        self.setCharImages()
        self.image = self.images[0]
        self.rect = self.image.get_rect(center=SCREENRECT.center)
        self.reloading = 0
        self.origtop = self.rect.top
        self.facing = -1

    # sets the character image array
    def setCharImages(self):
        img = self.load_image("char.png")
        img.set_colorkey((0,0,0))
        self.images = [img, 
                       pygame.transform.flip(img, 1, 1),
                       pygame.transform.rotate(img, 270),
                       pygame.transform.rotate(img, 90)]

    # loads an image from disk
    def load_image(self, file):
        try:
            MAIN_DIR = os.path.split(os.path.abspath(__file__))[0]
            surface = pygame.image.load(os.path.join(MAIN_DIR, "img", file))
        except pygame.error:
            raise SystemExit('Could not load image "%s" %s' % (file, pygame.get_error()))
        return surface.convert()

    # move operation and animation
    def move(self, keystate, SCREENRECT):
        if keystate[pygame.K_LSHIFT]:
            self.run = 2
        else:
            self.run = 1

        movX = (keystate[pygame.K_RIGHT] - keystate[pygame.K_LEFT]) * self.speed * self.run
        movY = (keystate[pygame.K_DOWN] - keystate[pygame.K_UP]) * self.speed * self.run

        self.rect.move_ip(movX, movY)
        self.rect = self.rect.clamp(SCREENRECT)

        if movX < 0:
            self.image = self.images[0]
        elif movX > 0:
            self.image = self.images[1]
        if movY < 0:
            self.image = self.images[2]
        elif movY > 0:
            self.image = self.images[3]

这是开始游戏时的第一关。

这是有问题的版本,在我更改级别后背景变黑。如您所见,精灵的透明背景被替换为第一个添加的背景层,而不是第二个。

所以基本上我会说,当移动我的精灵时,错误的背景会在它离开它的位置时重新绘制。这也只发生在第二个屏幕上!如果我回到第一个,我就不会遇到这个故障。

所以基本上我上面的简短代码示例中的两行实际上是在这里调用这个函数。也许这会导致问题?!

您必须更改用于清除屏幕的 Surface 的颜色:

all.clear(screen, background)

您必须 fill 使用新颜色 background 并使用相同颜色清屏:

def checkPlayerPosition(self, indexX, indexY, screen):
        if self.id == 1:
            if indexX <= 2 and indexY == 0:
                backgrund.fill((0, 0, 0)) 
                screen.blit(backgrund, (0, 0))
                pygame.display.flip()
                return True
        else:
            if indexX < 3 and indexY == 4:
                backgrund.fill((0, 0, 255)) 
                screen.blit(backgrund, (0, 0))
                pygame.display.flip()
                return True
        return False

此外,在checkPlayerPosition之后调用all.clear(screen, background)

while player.alive():

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            return
        if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
            return

    all.update()
    player.move(pygame.key.get_pressed(), map.matrix)

    indexX, indexY = player.mapPosition()
    if map.checkPlayerPosition(indexX, indexY, screen):
        player.rect = player.image.get_rect(center=SCREENRECT.center)

    # draw the scene
    all.clear(screen, background)
    dirty = all.draw(screen)
    pygame.display.update(dirty)

    # cap the framerate at 60fps. Also called 60HZ or 60 times per second.
    clock.tick(60)

我终于找到了问题,你的代码很有帮助,谢谢!

当我测试答案时一切正常,但后来我尝试实现更多逻辑但又失败了。我更改了行:

background.fill((0,0,255))
screen.blit(background, (0, 0))

以下内容:

bgGreen = load_image("background_green.png")
background = pygame.Surface(SCREENRECT.size) # <- this was wrong
for height in range(0, 450, 90):
   for width in range(0, 630, 90):
      background.blit(bgGreen, (width, height))
screen.blit(background, (0, 0))

然后我发现,当我删除第二行(Surface 构造函数调用)时,“故障”消失了。

所以我想说我完全误解了 Python 中参数的传递。背景是按值而不是按引用传递给函数的?因此,我在我的背景上创建了一个新的 Surface,但是当该函数返回到我的主游戏循环时,它继续使用原始背景对象。

我希望我的解释是正确的,这对我自己来说似乎是一个非常愚蠢的错误。但是我设法改变了我的初始程序,现在一切正常,正如我所期望的那样。

@Rabbid76:感谢您的耐心等待。我是否也应该将您的答案标记为 correct/helpful,因为它帮助我正确安排了游戏循环?当然,它对我找到自己的错误有很大帮助...