如何在Pygame中循环图像精灵

4

我想用pygame和python 2.7.6版本在游戏中创建角色。我有一个简单的Python脚本,可以让我创建一个窗口并将图像打印到屏幕上。虽然这很成功,但只是静态图像。

import pygame


class Game(object):
    def main(self, screen):

        #load first sprite to image for blit"
        image = pygame.image.load('sprites/sprite1.png')
        image2 = pygame.image.load('sprites/sprite2.png')
        image3 = pygame.image.load('sprites/sprite3.png')
        while 1:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                return
            if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
                return

        screen.fill((200, 200, 200))
        screen.blit(image, (320, 240))
        pygame.display.flip()

if __name__ == '__main__':
    pygame.init()
    screen = pygame.display.set_mode((640, 480))
    Game().main(screen)

我希望通过更改第一个精灵图片来创建更具动态性的图像,至少需要三个或更多的精灵(例如sprite1.pngsprite2.pngsprite3.png)。如何在经过给定的时间间隔后循环显示这些图片,例如:1秒钟、半秒钟...


一个单独的循环,使用线程轻松实现,在其中更改精灵并添加1秒的休眠作为暂停。 - Tymoteusz Paul
单独线程每个图像?我不确定在哪里使用线程。 - user2497792
3个回答

3
让我们从头开始,编写游戏时,将渲染循环与程序的主循环(处理窗口事件的循环)分离非常重要。这样即使在渲染过程中出现问题,窗口也不太可能变得无响应。此外,人们可以调整大小和操纵窗口,而不会中断图形线程,这是其他好处之一。 网络上有一些不错的教程,虽然我没有python的教程,但SFML教程可以教你正确的概念,然后你可以在python中使用。
完成这项公共服务后,当您已经将应用程序拆分为单独的线程时,我们面临的问题是动画。一个非常简单的实现方式,肯定不是最好的,就是为每个图像生成一个单独的线程,其中仅包含以下内容:
def shake_it():
  while True:
    time.sleep(3)
    #do rotation here

虽然这种方法可以很好地工作,但显然不是最佳解决方案,因为它非常不灵活。想象一下,现在您还想定期执行其他操作,例如让您的NPC巡逻堡垒。这将需要另一个线程,并且这将很快升级到相当高的线程数量(本身并不坏,但不是所需的效果)。
因此,拯救的是更通用的线程 - 事件处理程序。一个类将控制所有需要在特定时间间隔内执行的功能。然后,在用于动画图像的附加线程中,您将在循环中运行event_handler的实例,它将作为动画等操作的抽象。

0
稍微详细一些,根据@Puciek在评论中的建议,可以使用类似以下的循环:
import time

for img in [image, image1, image2]:
    screen.fill((200, 200, 200))
    screen.blit(img, (320, 240))
    time.sleep(1)    # sleep 1 second before continuing

你肯定希望将其放在单独的线程中,或者至少是一个独立的“渲染”线程,远离主循环。 - Tymoteusz Paul
是的,但那应该是线程内部基本结构。 - cm2
好的,我建议一种有效的方法来升级你的答案,使其更加有用! - Tymoteusz Paul
我同意,但不能透露所有细节!此外,我从未真正使用过Python中的“threading”模块 :-o。请随意提供您自己的更好答案 :)。 - cm2
太懒了,所以我只留在评论区。 - Tymoteusz Paul
你甚至提供了比我更详细的答案 :-p。 你的回答应该被接受。 - cm2

-3
要循环播放精灵图像,您只需在循环中绘制相应的图像即可。一个好的方法是将序列中的所有图像放在一个图像中,并在程序启动时加载它。然后使用subsurface()函数获取用于绘制的图像,因此您可以使用某个变量在循环中更改子表面的“窗口”,以将子表面的“窗口”绑定到整个图像上。另一个问题是如何控制频率,有几种方法,这里是一个简单的精灵动画示例。它是具有8个状态的相同精灵序列,在三个不同的FPS下循环:初始-12 fps(1/12秒暂停),然后是初始fps的1/3 = 4 fps(3/12秒暂停)和初始fps的1/12 = 1 fps(12/12 = 1秒暂停)。我发布整个脚本,所以您可以运行并查看它的工作原理,只需将其与图像放在同一位置并运行脚本。 请使用此图像与脚本:

enter image description here

源代码:

import pygame, os
from pygame.locals import *

def Cycle(n, N) :           # n - current state, N - max number of states
    value = n+1 
    case1 = (value >= N) 
    case2 = (value < N)
    result = case1 * 0 + case2 * value
    return result

w = 600         # window size
h = 400
cdir = os.path.curdir       # current directory in terminal
states = 8      # number of sprite states

pygame.init()
DISP = pygame.display.set_mode((w, h))
BG = (36, 0, 36)        # background color
DISP.fill(BG)
band_1 = pygame.image.load(os.path.join(cdir, "band1.png")).convert()
K = 4           # global scale factor
band_1 = pygame.transform.scale(band_1, (K * band_1.get_width(), K * band_1.get_height()))
band_1.set_colorkey((0,0,0))
sw = K*8            # sprite width
sh = K*8            # sprite height
r0 = (100, 100, sw, sh)     # sprite 0 rectangle
r1 = (150, 100, sw, sh)     # sprite 1 rectangle
r2 = (200, 100, sw, sh)     # sprite 2 rectangle
s0 = 0          # initial state of sprite 0
s1 = 0          # initial state of sprite 1
s2 = 0          # initial state of sprite 2
t0 = 1          # main ticker
t1 = 1          # ticker 1
t2 = 1          # ticker 2
Loop_1 = True
FPS = 12
clock = pygame.time.Clock( ) 
while Loop_1 :
    clock.tick(FPS)
    DISP.fill(BG, r0)
    sprite_0 = band_1.subsurface((s0*sw, 0, sw, sh))
    DISP.blit(sprite_0, r0)
    s0 = Cycle(s0, states)
    if t1 == 3 :
        DISP.fill(BG, r1)
        sprite_1 = band_1.subsurface((s1*sw, 0, sw, sh))
        DISP.blit(sprite_1, r1)
        s1 = Cycle(s1, states)
        t1 = 0
    if t2 == 12 :
        DISP.fill(BG, r2)
        sprite_2 = band_1.subsurface((s2*sw, 0, sw, sh))
        DISP.blit(sprite_2, r2)
        s2 = Cycle(s2, states)
        t2 = 0
    for event in pygame.event.get( ):
        if event.type == QUIT : Loop_1 = False
        if event.type == KEYDOWN:
            if event.key == K_ESCAPE : Loop_1 = False
#   t0 = t0 + 1 
    t1 = t1 + 1 
    t2 = t2 + 1 
    pygame.display.update()

pygame.quit( )

3
这是一堆代码,因为变量名而难以阅读。具有讽刺意味的是,在简单的变量赋值中您有很多注释,但其实它们不需要(只需要适当的名称),而在实际逻辑中则没有任何注释。 - Tymoteusz Paul
2
如果您想对我的答案发表评论,请随意在下面进行评论,因为这是评论它的地方。 - Tymoteusz Paul
好的,但是从您的评论中我看到您不会提供完整的示例,那我该评论什么呢?离题:我不知道是否是您在给我的回答投票反对,如果是,请考虑只有在示例错误或与问题无关时才这样做。这有助于过滤知识库,个人并不介意,只是在此处发布一个简单的可行示例。 - Mikhail V
哦,我漏掉了你帖子中的那部分!回答不必包含完整且可运行的解决方案,只需回答问题即可。有时候会涉及到工作代码,但这并非必需。 - Tymoteusz Paul
1
我们都曾经是新手,所以需要克服一些学习曲线;)另外,在去除不必要的部分之后,最大的问题是我提到的 - 所呈现的代码示例非常令人困惑。如果您对其进行重构,至少通过更改变量名称为有意义的内容,并在应该放置的位置包括注释(最好也将应用程序拆分成可管理的函数,而不是一个大而复杂的字符串),那么我很可能会把我的反对票变成支持票。您必须理解,您知道K是什么,我们无法分享您的大脑。 - Tymoteusz Paul
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接