python - 在 pygame 中,当我与某物碰撞时如何位图传送图像并使其保持不变?

标签 python pygame collision blit

所以我一直在使用第一个答案 here 中的 python 脚本的稍微修改版本。我在此处添加了一个名为“SpeechBlock”的类:

    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 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 上时,语音气泡的图像会被传输到屏幕上。相反,当玩家站在 SpeechBlock 的边缘时,图像会出现一小会儿,然后消失,然后重新出现一会儿,依此类推…… 我做错了什么?我对 pygame 还很陌生,所以我不太了解位图传送和显示更新/翻转的工作原理。任何帮助将不胜感激。

最佳答案

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

因此,当您调用 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 秒是否已经过去。

enter image description here

关于python - 在 pygame 中,当我与某物碰撞时如何位图传送图像并使其保持不变?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57983559/

相关文章:

java - 我必须重构我的代码,但不知道将碰撞方法放在哪里

python - 在 Pandas 中,如何用数据帧的另一个子集替换数据帧的一个子集?

python - 找到图像的中心来定义碰撞箱

python - 使用PyGame音频时发生意外错误

python - pygame.mixer.music,我想在 while 循环运行时播放轨道

python - 当pygame中两个图像碰撞时如何使变量输出 'True'

python - Pygame - 检测矩形的哪一侧与另一侧的哪一侧碰撞

python - 国际化 - Python MySQLDb 和 ISO-8859-7

python - 当我尝试将 csv 数据帧中的列添加到在 pandas 上打开的现有数据帧时,为什么我不断收到 'Nan' 值?

python - 为什么 Python 列表乘法创建一个包含多个元素的列表?