我正在pygame上做游戏,我想对如何制作一个简单的两帧动画进行一些建议,并在按下某个键时退出。这是代码,并附有完整文件。
screen.blit(win_screen1, [0, 0])
pygame.display.update()
clock.tick(13)
screen.blit(win_screen2, [0, 0])
clock.tick(13)
pygame.display.update()
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
playing = True
win = False
reset()
我本以为这会使动画以13 FPS的速度运行,并在按下某个键时停止播放,但是它的播放过程确实不一致且缓慢,并且按任何键都无济于事。我在另一篇文章中看到有人说他们使用了pygame.time.Clock()
,但我不确定它的工作原理,而且许多方法仅限制帧速率,而不设置帧速率。
答案 0 :(得分:1)
将动画项目包装到一个对象中,这样它可以保持自己的内部时序,并绘制正确的位图。我会为此使用PyGame精灵,但是您也可以自己制作。
该想法是使用内部PyGame时钟记录首次显示当前帧的时间。然后,在某种类型的每帧更新功能中,确定是否经过了足够的毫秒数以保证移动到下一帧。您希望获得13 FPS,因此每77毫秒为1000/13->。
这给了我们一个粗略的功能:
drawAnimation()
What is the time now?
Has 77 milliseconds elapsed since the last update?
Advance to the next image in the list
Draw the image
在循环播放帧和保持时间戳方面有一些内务处理,但这并不比上面的伪代码复杂得多。诀窍是将所有包含在可以传递的单个数据对象中。这样可以使其他帧定时(例如窗口FPS)保持很好的分离。就像我说的那样,PyGame Sprite是保存所有这些数据的理想选择,但是即使是简单的python列表也可以正常使用。
动画来自:https://rvros.itch.io/animated-pixel-hero(在许可许可下使用)。缩放200%
这是一个基于Sprite的实现。如有疑问,请添加评论。
class MultiFrameSprite(pygame.sprite.Sprite):
def __init__( self, fps, list_of_frames ):
pygame.sprite.Sprite.__init__( self )
# Load all the specified animation frames
self.frames = []
for filename in list_of_frames:
self.frames.append( pygame.image.load( filename ).convert_alpha() )
# Start the animation at the first frame
self.image = self.frames[0]
self.rect = self.image.get_rect()
# Frame handling - index frame, and time used
self.millisec_rate = 1000 // fps # inter-frame delay in milliseconds
self.current_frame = 0
self.last_frame_at = 0
# we need to be somewhere on-screen
self.rect.center = ( WINDOW_WIDTH//2, WINDOW_HEIGHT//2 )
def update(self):
# Compare the time of the last frame to time-now
# and determine if it's time to show the next frame
time_now = pygame.time.get_ticks()
if ( time_now > self.last_frame_at + self.millisec_rate ):
# new frame needed!
self.last_frame_at = time_now
# Advance to the next frame
self.current_frame += 1
if ( self.current_frame == len( self.frames ) ):
self.current_frame = 0 # wrap frame loop index
# Set the new image
self.image = self.frames[ self.current_frame ]
self.rect = self.image.get_rect()
# TODO: handle any x/y changes needed by differing animation sizes
请注意,我们绝不会只制作2帧,3帧或10帧的动画。这只是 N 个图像的列表。当我们到达列表的末尾(无论时间长短)时,它就可以回到开始。这样,可以将相同的代码用于2
或22
帧动画。
参考代码:
import pygame
import random
# Window size
WINDOW_WIDTH = 300
WINDOW_HEIGHT = 300
WINDOW_SURFACE = pygame.HWSURFACE|pygame.DOUBLEBUF|pygame.RESIZABLE
DARK_BLUE = ( 3, 5, 54)
class MultiFrameSprite(pygame.sprite.Sprite):
def __init__( self, fps, list_of_frames ):
pygame.sprite.Sprite.__init__( self )
# Load all the specified animation rames
self.frames = []
for filename in list_of_frames:
self.frames.append( pygame.image.load( filename ).convert_alpha() )
# Start the animation at the first frame
self.image = self.frames[0]
self.rect = self.image.get_rect()
# Frame handling
self.millisec_rate = 1000 // fps # inter-frame delay in milliseconds
self.current_frame = 0
self.last_frame_at = 0
# we need to be somewhere on-screen
self.rect.center = ( WINDOW_WIDTH//2, WINDOW_HEIGHT//2 )
def update(self):
# Compare the time of the last frame to time-now
# and determine if it's time to show the next frame
time_now = pygame.time.get_ticks()
if ( time_now > self.last_frame_at + self.millisec_rate ):
# new frame needed!
self.last_frame_at = time_now
# preserve the centroid in case the frames are differing sizes
x,y = self.rect.center
# Advance to the next frame
self.current_frame += 1
if ( self.current_frame == len( self.frames ) ):
self.current_frame = 0 # wrap frame loop index
# Set the new image
self.image = self.frames[ self.current_frame ]
self.rect = self.image.get_rect()
self.rect.center = (x,y) # restore the centroid co-ordinate
### initialisation
pygame.init()
pygame.mixer.init()
window = pygame.display.set_mode( ( WINDOW_WIDTH, WINDOW_HEIGHT ), WINDOW_SURFACE )
pygame.display.set_caption( "Multi-Frame Sprite" )
### Create Sprites
all_sprites = pygame.sprite.Group()
adventurer = MultiFrameSprite( 5, [ 'adventurer-idle-00.png',
'adventurer-idle-01.png',
'adventurer-idle-02.png',
'adventurer-idle-03.png' ] )
all_sprites.add( adventurer )
### Main Loop
clock = pygame.time.Clock()
done = False
while not done:
# Handle user-input
for event in pygame.event.get():
if ( event.type == pygame.QUIT ):
done = True
# Update the window, but not more than 60fps
all_sprites.update()
window.fill( DARK_BLUE )
all_sprites.draw( window ) # Paint ALL the sprites
pygame.display.flip()
# Clamp FPS
clock.tick_busy_loop(60)
pygame.quit()
注意:上面的代码中我使用了5 FPS,因为它非常适合动画。