如何在 pygame 中的 class 中使用 while 循环?

How to use a while loop within a class in pygame?

我正在尝试创建一个实例 class(方块)在单击鼠标时生成并缓慢淡出,我希望能够生成无限数量的方块并淡出,前提是鼠标点击速度不够快。

为此,我想在第一次生成实例时启动 fade() 函数,并将刻度设置为 255,并将 alpha 设置为刻度。

但是,当使用 while 循环时,函数会自行完成而不更新显示,因为程序被限制在 fade() 函数中的 while 循环。

谁能帮我在每个实例中调用淡入淡出函数 255 次?

import pygame,sys,time,Blockm
from pygame.locals import *
black,white=(0,0,0),(255,255,255)
size=w,h=1400,800
screen=pygame.display.set_mode(size)
pygame.init()

class Block(object):

    sprite = None

    def __init__(self, x, y):
        if not Block.sprite:
            Block.sprite = pygame.image.load("Block.png").convert_alpha()
        self.rect = Block.sprite.get_rect(top=y, left=x)
        self.fade()

    def fade(self):
        ticks=255
        Block.sprite.set_alpha(ticks)
        while ticks!=0:
            ticks-=1
            print(ticks)



while True:
    blocks = []
    mmraw=pygame.mouse.get_pos()
    mmx,mmy=mmraw[0]-(pygame.image.load("Block.png").get_width())/2,mmraw[1]-(pygame.image.load("Block.png").get_width())/2
    for event in pygame.event.get():
        if event.type== pygame.QUIT:
            pygame.quit()
            sys.exit()
        if event.type == pygame.MOUSEBUTTONDOWN:
            print('click')
            blocks.append(Block(mmx,mmy))

    for block in blocks:
        screen.blit(block.sprite, block.rect)
        print('trying to blit')
        print(Block.sprite.get_offset())
    pygame.display.update()

您可能想使用 threads。这些允许您一次 运行 多个东西。对于您的代码,将其更改为:

from threading import Thread
import pygame, sys, time, Blockm
from pygame.locals import *
black = (0,) * 3
white = (255,) * 3
size = w, h = 1400, 800
screen = pygame.display.set_mode(size)
pygame.init()

class Block(object):

    sprite = None

    def __init__(self, x, y):
        if not Block.sprite:
            Block.sprite = pygame.image.load("Block.png").convert_alpha()
        self.rect = Block.sprite.get_rect(top=y, left=x)
        Thread(target=self.fade) # Made this a Thread so it runs separately.

    def fade(self):
        for tick in range(255, 0, -1):
            Block.sprite.set_alpha(tick)
            print(tick)


def game():
    while True:
        # The contents of that while True loop at the end


def main():
    Thread(target=Main)


if __name__ == "__main__":
    main()

这也为您的程序添加了一个入口点,这是缺少的。另外,Block.fade 实际上不会做你想做的,因为你只 set_alpha 一次。我改用 for 循环来解决这个问题。另外,请注意,您现在可以 return 打破整个游戏。

我有一个解决方案可以满足您的要求,但我承认它没有完全按照要求回答问题。

您可能遇到的第一个也是最棘手的问题是您需要使用 pygame.image.load("Block.png").convert() 而不是 pygame.image.load("Block").convert_alpha(),因为 .convert_alpha() 不适用于 .set_alpha(..) . 也有可能您没有注意到块在任何解决方案中何时消失,因为 screenupdate 之前没有被刷新。新的褪色块被绘制在 'brighter' 个块上,没有产生任何差异(尽管有重叠块)。我在下面的解决方案代码中添加了一个权宜之计,用蓝色填充屏幕,但我想您会想要一些不同的东西。它标有评论。

我建议让每个 Block 在从 for block in blocks: 块的主循环调用期间在本地处理它的 alpha,然后再 blit 它。这个版本的代码应该给你你想要的结果,尽管它只使用主循环,而不是像你问的那样并行循环...

import pygame,sys,time,Blockm
from pygame.locals import *
black,white=(0,0,0),(255,255,255)
size=w,h=1400,800
screen=pygame.display.set_mode(size)
pygame.init()

class Block(object):

    sprite = None

    def __init__(self, x, y):
        if not Block.sprite:
            Block.sprite = pygame.image.load("Block.png").convert()
            # ^  MODIFIED: .convert_alpha() won't recognize the effects of .set_alpha(), so use regular .convert() instead.
        self.rect = Block.sprite.get_rect(top=y, left=x)
        self.ticks = 255 # NEW: Set a local tick count.
        # REMOVED `self.fade()`: This runs on main now.

    def fade(self):
        ## REPURPOSED METHOD: Runs each frame that the Block is active. Called on main loop.
        # MODIFIED BLOCK: Update local tick count before setting the class's sprite alpha.
        self.ticks -= 1
        Block.sprite.set_alpha(self.ticks) # MOVED, MODIFIED: uses local tick count for alpha.
        print(self.ticks) # UPDATED: Using local ticks.


blocks = [] # MOVED: Make a list for the Blocks, but don't clear it on frame.
while True:    
    mmraw=pygame.mouse.get_pos()
    mmx,mmy=mmraw[0]-(pygame.image.load("Block.png").get_width())/2,mmraw[1]-(pygame.image.load("Block.png").get_width())/2
    # ^  There may be a tidier way of doing this. Described in solution body...
    for event in pygame.event.get():
        if event.type== pygame.QUIT:
            pygame.quit()
            sys.exit()
        if event.type == pygame.MOUSEBUTTONDOWN:
            print('click')
            blocks.append(Block(mmx,mmy))

    screen.fill((22,22,222))
    # ^  NEW: Fill the screen with some backdrop so that the erasure is obvious!
    #         If you don't do this, you'll be blitting faded images over brighter ones and there will be no real change!!
    for block in blocks:
        block.fade()    # NEW: Update this block.
        if block.ticks < 1: # NEW BLOCK: If the block has become invisible...
            blocks.remove(block) # NEW: Expunge this invisible block.
            continue # NEW: Immediately move on to the next block.
        screen.blit(Block.sprite, block.rect)
        print('trying to blit')
        print(Block.sprite.get_offset())
    pygame.display.update()

它有一个小问题,即消失的方块会触发其他剩余方块中的 'flicker'(或至少 blocks 中的下一个方块),我不确定为什么或怎么样。


在寻找的过程中,我发现了一些您可能需要考虑的其他事项:

...在 class Block::

-考虑使用 sprite = pygame.image.load("Block.png").convert() 而不是 sprite = None。这样,您可以使用 mmx,mmy = mmraw[0] - Block.sprite.get_rect.centerx, mmraw[1] - Block.sprite.get_rect().centery 之类的东西而不是暂时加载图像,只是为了知道它的大小。由于所有 Block 都使用相同的图形,因此应该没有什么区别,这样,如果您在运行时更改 Block.sprite,就不必重新获取偏移量!

-考虑将 Block 的精灵 copy 分配给每个实例,而不是使用 class 的表面。这将占用更多的处理能力,但如果您将其用作 return 只是暂时的。例如:

class Block(object):
      ...
    def fade(self):
        sprite = Block.sprite.copy()
        sprite.set_alpha(self.ticks)
        self.ticks -= 1
        return sprite

  ...

while True: # main loop
      ...
    for block in blocks:
        screen.blit(block.fade(), block.rect) # Although there are more Pythonic ways of writing this.

或者,您可以在 Block.fade() 中使用 screen.blit(sprite, self.rect) 而不是在 main 中使用 return 并完全放弃。因为alpha是本地设置的,所以不用每次fade运行的时候都resetBlock的unboundsprite可以留着新鲜!

无论如何,我希望这能解决您的问题,即使它没有(准确)回答您的问题!