在pygame中,当我与某物发生碰撞并使其停留时,如何将图像blit?

In pygame, how do I blit an image when I collide with something and make it stay?

所以我一直在使用第一个答案 here 中的 python 脚本的稍微修改的版本,我通过在此处添加一个名为 "SpeechBlock" 的 class :

    level = [
        "PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP",
        "P                                                             P",
        "P                                                             P",
        "P                                                             P",
        "P                                                             P",
        "P                                                             P",
        "P                                                             P",
        "P                                                             P",
        "PPPPPPPPSPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP",]
    # build the level
    for row in level:
        for col in row:
            if col == "P":
                p = Platform(x, y)
                platforms.append(p)
                entities.add(p)
            if col == "E":
                e = ExitBlock(x, y)
                platforms.append(e)
                entities.add(e)
            if col == "S":
                s = SpeechBlock(x, y)
                platforms.append(s)
                entities.add(s

并使其成为 class:

class SpeechBlock(Platform):
    def __init__(self, x, y):
        Platform.__init__(self, x, y)
        self.image.fill(Color("#0033FF"))
        self.x=x
        self.y=y

    def speak(self):
        self.events = [
            "test",
            ]
        for row in self.events:
            image=pygame.image.load(row+".png")
            screen.blit(image, (self.x,self.y))

脚本已经有一个碰撞方法,我在这里添加了最后 3 行:

ef collide(self, xvel, yvel, platforms):
        for p in platforms:
            if pygame.sprite.collide_rect(self, p):
                if isinstance(p, ExitBlock):
                    pygame.event.post(pygame.event.Event(QUIT))
                if xvel > 0:
                    self.rect.right = p.rect.left
                    print("collide right")
                if xvel < 0:
                    self.rect.left = p.rect.right
                    print ("collide left")
                if yvel > 0:
                    self.rect.bottom = p.rect.top
                    self.onGround = True
                    self.yvel = 0
                if yvel < 0:
                    self.rect.top = p.rect.bottom
                if isinstance(p, SpeechBlock):
                    SpeechBlock.speak(self)
                    pygame.display.update()

我想要并且仍然想要实现的是,当玩家站在 SpeechBlock 上方时,一个对话泡泡的图像被 blit 到屏幕上。取而代之的是,当玩家站在 SpeechBlock 的边缘时,图像会出现一小会儿,然后消失,然后再次出现,等等...... 我做错了什么?我是 pygame 的新手,所以我不太了解 blitting 和显示 updating/flipping 的工作原理。任何帮助将不胜感激。

你必须知道你的游戏是循环运行的。此循环的每次迭代称为一个帧。在每一帧中,您都可以清除屏幕上的所有内容、处理任何事件、更新您的游戏世界并重新绘制所有内容。

因此,当您调用 SpeechBlock.speak(self)pygame.display.update() 时,您将图像(来自 speak 方法)绘制到屏幕上,但它们将在下一帧中被擦除。

每帧调用 pygame.display.update() 的次数不应超过一次。你应该做的是引入一些新的状态来改变你触摸它时 SpeechBlock 的行为。

tl;dr 以下示例:

import pygame
from pygame import *
import sys

SCREEN_SIZE = pygame.Rect((0, 0, 800, 640))
TILE_SIZE = 32 
GRAVITY = pygame.Vector2((0, 0.3))

class CameraAwareLayeredUpdates(pygame.sprite.LayeredUpdates):
    def __init__(self, target, world_size):
        super().__init__()
        self.target = target
        self.cam = pygame.Vector2(0, 0)
        self.world_size = world_size
        if self.target:
            self.add(target)

    def update(self, *args):
        super().update(*args)
        if self.target:
            x = -self.target.rect.center[0] + SCREEN_SIZE.width/2
            y = -self.target.rect.center[1] + SCREEN_SIZE.height/2
            self.cam += (pygame.Vector2((x, y)) - self.cam) * 0.05
            self.cam.x = max(-(self.world_size.width-SCREEN_SIZE.width), min(0, self.cam.x))
            self.cam.y = max(-(self.world_size.height-SCREEN_SIZE.height), min(0, self.cam.y))

    def draw(self, surface):
        spritedict = self.spritedict
        surface_blit = surface.blit
        dirty = self.lostsprites
        self.lostsprites = []
        dirty_append = dirty.append
        init_rect = self._init_rect
        for spr in self.sprites():
            rec = spritedict[spr]
            newrect = surface_blit(spr.image, spr.rect.move(self.cam))
            if rec is init_rect:
                dirty_append(newrect)
            else:
                if newrect.colliderect(rec):
                    dirty_append(newrect.union(rec))
                else:
                    dirty_append(newrect)
                    dirty_append(rec)
            spritedict[spr] = newrect
        return dirty            

def main():
    pygame.init()
    screen = pygame.display.set_mode(SCREEN_SIZE.size)
    pygame.display.set_caption("Use arrows to move!")
    timer = pygame.time.Clock()

    level = [
        "PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP",
        "P                                          P",
        "P                                          P",
        "P                                          P",
        "P                    PPPPPPPPPPP           P",
        "P                                          P",
        "P                                          P",
        "P                                          P",
        "P    PPPPPPPP                              P",
        "P                                          P",
        "P                          PPPPPPP         P",
        "P                 PPPPPP                   P",
        "P                                          P",
        "P         PPPPPPP                          P",
        "P                                          P",
        "P                     PPPPPP               P",
        "P                                          P",
        "P   PPPPPPPPPPP                            P",
        "P                                          P",
        "P                 PPPPPPPPPPP              P",
        "P                                          P",
        "P                                          P",
        "P                                          P",
        "P                                          P",
        "PPPPPPPPPSPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP",]


    platforms = pygame.sprite.Group()
    player = Player(platforms, (TILE_SIZE, TILE_SIZE))
    level_width  = len(level[0])*TILE_SIZE
    level_height = len(level)*TILE_SIZE
    entities = CameraAwareLayeredUpdates(player, pygame.Rect(0, 0, level_width, level_height))

    # build the level
    x = y = 0
    for row in level:
        for col in row:
            if col == "P":
                Platform((x, y), entities, platforms)
            if col == "S":
                SpeechBlock((x, y), entities, platforms)
            x += TILE_SIZE
        y += TILE_SIZE
        x = 0

    dt = 0

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

        entities.update(dt, events)
        screen.fill((0, 0, 0))
        entities.draw(screen)
        pygame.display.update()
        dt = timer.tick(60)

class Entity(pygame.sprite.Sprite):
    def __init__(self, color, pos, *groups):
        super().__init__(*groups)
        self.image = Surface((TILE_SIZE, TILE_SIZE))
        self.image.fill(color)
        self.rect = self.image.get_rect(topleft=pos)

    def update(self, dt, events):
        pass

class Player(Entity):
    def __init__(self, platforms, pos, *groups):
        super().__init__(Color("#0000FF"), pos)
        self.vel = pygame.Vector2((0, 0))
        self.onGround = False
        self.platforms = platforms
        self.speed = 8
        self.jump_strength = 10

    def update(self, dt, events):
        pressed = pygame.key.get_pressed()
        up = pressed[K_UP]
        left = pressed[K_LEFT]
        right = pressed[K_RIGHT]
        running = pressed[K_SPACE]

        if up:
            # only jump if on the ground
            if self.onGround: self.vel.y = -self.jump_strength
        if left:
            self.vel.x = -self.speed
        if right:
            self.vel.x = self.speed
        if running:
            self.vel.x *= 1.5
        if not self.onGround:
            # only accelerate with gravity if in the air
            self.vel += GRAVITY
            # max falling speed
            if self.vel.y > 100: self.vel.y = 100

        if not(left or right):
            self.vel.x = 0
        # increment in x direction
        self.rect.left += self.vel.x * dt/10.
        # do x-axis collisions
        self.collide(self.vel.x, 0, self.platforms)
        # increment in y direction
        self.rect.top += self.vel.y * dt/10.
        # assuming we're in the air
        self.onGround = False;
        # do y-axis collisions
        self.collide(0, self.vel.y, self.platforms)

    def collide(self, xvel, yvel, platforms):
        for p in platforms:
            if pygame.sprite.collide_rect(self, p):
                if isinstance(p, SpeechBlock):
                    p.trigger()
                if xvel > 0:
                    self.rect.right = p.rect.left
                if xvel < 0:
                    self.rect.left = p.rect.right
                if yvel > 0:
                    self.rect.bottom = p.rect.top
                    self.onGround = True
                    self.yvel = 0
                if yvel < 0:
                    self.rect.top = p.rect.bottom

class Platform(Entity):
    def __init__(self, pos, *groups):
        super().__init__(Color("#DDDDDD"), pos, *groups)

class TextBlock(pygame.sprite.Sprite):
    def __init__(self, pos, text, *groups):
        super().__init__(*groups)
        self.image = Surface((200, 100))
        self.rect = self.image.get_rect(center=pos)
        self.image.fill(Color("white"))
        # todo: don't load font everytime
        self.image.blit(pygame.font.SysFont(None, 32).render(text, True, Color("Black")), (10, 10))

class SpeechBlock(Entity):
    def __init__(self, pos, *groups):
        super().__init__(Color("orange"), pos, *groups)
        self.cooldown = 0
        self.text = None

    def trigger(self):
        # do nothing if already triggered
        if self.cooldown: return

        self.cooldown = 2000
        # first group is the entity group
        self.text = TextBlock(self.rect.move(0, -150).center, 'Ouch!', self.groups()[0])

    def update(self, dt, events):

        if self.cooldown:
            self.cooldown -= dt
            if self.cooldown <= 0:
                self.text.kill()
                self.text = None
                self.cooldown = 0

if __name__ == "__main__":
    main()

在这里您看到我们创建了一个新的 Entity,当玩家踏入 SpeechBlock 时显示文本。两秒后,SpeechBlock 调用 kill 将其从游戏中移除。

我还稍微更新了代码以使用增量时间,这样我们就可以轻松检查是否已经过了 2 秒。