如何在pygame中循环图像精灵

4 投票
3 回答
2806 浏览
提问于 2025-04-28 05:37

我想用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秒或每0.5秒切换一次呢?

暂无标签

3 个回答

-3

要在精灵图像之间切换,你只需要在循环中绘制对应的图像。一个好的方法是把所有的图像放在一张大图里,然后在程序启动时加载这张大图。接着,使用subsurface()函数来获取要绘制的图像,这样你就可以通过一个在循环中变化的变量来控制这张大图的“窗口”。另外,关于如何控制切换的频率,有几种方法,这里有一个简单的精灵动画示例。这个示例使用的是同一组精灵图像,有8种状态,分别以三种不同的帧率(FPS)循环:初始状态是12帧每秒(每帧暂停1/12秒),然后是初始帧率的1/3,即4帧每秒(每帧暂停3/12秒),最后是初始帧率的1/12,即1帧每秒(每帧暂停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

我们从头说起,写游戏的时候,把渲染循环和程序的主循环分开是非常重要的。主循环负责处理窗口事件,比如用户的点击和输入。如果渲染过程卡住了,窗口也不会变得无响应,这样用户就可以继续调整窗口大小或进行其他操作,而不会影响到图形的显示。

网上有很多不错的教程,虽然我手头没有针对Python的,但SFML的教程可以教你一些正确的概念,这些概念你可以在Python中使用。

说完这些基础知识,如果你的应用程序已经分成了多个线程,那么接下来就要考虑动画的问题了。一个非常简单的实现方式,虽然不是最好的,就是为每一帧图像创建一个单独的线程,代码大概是这样的:

def shake_it():
  while True:
    time.sleep(3)
    #do rotation here

虽然这样做能正常工作,但显然不是最好的解决方案,因为它缺乏灵活性。想象一下,如果你还想定期做其他事情,比如让你的NPC在城堡周围巡逻,这就需要另一个线程。这样线程的数量会迅速增加(虽然这本身并不是坏事,但也不是我们想要的效果)。

所以,一个更通用的解决方案就是使用事件处理器线程。这个类会控制所有需要在特定时间间隔内执行的功能。然后,在你用来动画的那个额外线程中,你可以运行事件处理器的实例,这样就可以处理像动画这样的动作了。

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

撰写回答