python - 如何使两帧动画工作pygame
问题描述
我正在 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()
,但我不确定它是如何工作的,而且许多方法只是限制帧率,而不是设置它。
解决方案
将动画项目包装成一个对象,这样它就可以保持自己的内部时序,并绘制正确的位图。我会为此使用 PyGame 精灵,但你也可以自己制作。
这个想法是使用内部 PyGame 时钟来记录当前帧首次显示的时间。然后在某种每帧更新函数中,确定是否经过了足够的毫秒以保证移动到下一帧。您需要 13 FPS,所以这是 1000/13 -> 每 77 毫秒。
这给了我们一个粗略的功能:
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,因为它非常适合动画。
推荐阅读
- c++ - 为什么这行代码会产生错误?
- selenium - docker compose 中的 Selenium 独立 - 被操作系统杀死?
- java - 从多个 JLabel 获取鼠标点击输入的较小程序
- sql - 创建 SQL Server 数据库 - 但出现错误
- java - 如何使复选框/滑块持久化?
- jquery - 调整宽度
用于显示特定数量的行
- java - 我的 android studio 正在我的手机上安装两个应用程序/apk
- powershell - 尝试创建弹出窗口时方法调用失败
- ansible - 受管节点上特定 sudo 命令的 ansible 行为
- python - 如何将不同长度的字符串转换为不同精度的时间戳进行pandas插值?