Кажется, вы уже используете класс Sprite
, поэтому легко отслеживать время внутри ваших спрайтов.
Что вам нужно сделать, это использовать так называемое дельта-время. По сути, вы измеряете время, необходимое для визуализации кадра, и передаете это значение в каждую часть игры, которая «движется во времени». Поэтому, когда последний кадр занял X мс, спрайт, который движется со скоростью S, перемещает S * X пикселей. Если следующий кадр занимает Y мс, тот же спрайт будет перемещать S * Y пикселей.
В pygame вы обычно используете Clock
для ограничения частоты кадров, вызывая его метод tick
. Этот метод также возвращает количество миллисекунд, прошедших с момента предыдущего вызова. Таким образом, это значение является нашим дельта-временем, которое мы передаем методу update
наших спрайтов.
Так как теперь каждый спрайт знает каждый кадр, сколько прошло времени, мы можем сделать более медленную анимацию, создав переменную экземпляра. (давайте назовем его timer
), установите его значение равным количеству миллисекунд, которое мы хотим ждать, и вычтите время дельты (назовем его dt
) каждого кадра. Как только мы нажимаем <= 0
, мы сбрасываем timer
и меняем изображение спрайта.
Вот рабочий пример, который я взломал вместе. Стреляйте в красную ракету, нажимая любую клавишу, и попробуйте поразить синие цели. Смотрите удивительные взрывы!
import pygame
import random
TILESIZE = 32
# Since our sprites are very generic, we want to be able
# to set some attributes while creating them.
# Here we create a set of allowed options.
ACTOR_OPTIONS = set(['can_be_destroyed'])
# guess what
def random_color():
return random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)
# A generic Sprite class.
# It takes a position, a velocity, a color, a function that
# controls its behaviour, and keyworded arguments.
class Actor(pygame.sprite.Sprite):
def __init__(self, pos, vel, color, handler, all_sprites, **kwargs):
super().__init__(all_sprites)
# A timer and counter attribute to handle time based stuff.
self.timer = 0
self.counter = 0
# Update the sprite attributes with the keyworded arguments.
# We only allow the keys defined in ACTOR_OPTIONS.
self.__dict__.update(kwargs)
self.__dict__.update((key, False) for key in ACTOR_OPTIONS)
self.__dict__.update((key, value) for key, value in kwargs.items() if key in ACTOR_OPTIONS)
# We keep a reference to a sprite groups that contains all sprites
# so we can access them easily
self.all_sprites = all_sprites
# Simple non-colored image
self.image = pygame.Surface((TILESIZE, TILESIZE))
self.image.fill(pygame.Color(color))
self.rect = self.image.get_rect(center=pos)
# Position and velocity as Vectors for easier math
self.pos = pygame.Vector2(pos)
self.vel = pygame.Vector2(vel)
# The handler function that we're going to call each frame
self.handler = handler
def update(self, dt, events):
# Let the handler function to their thing
self.handler(self, dt, events)
# Move the sprite
self.pos += (self.vel * dt/10)
self.rect.center = [int(x) for x in self.pos]
# Our sprites are very generic, so here we create some functions
# that will control the behaviour of the sprites.
# We could have created new classes and implement the update-function instead,
# but this function-approach allows us to change the behaviour of a sprite
# at runtime by setting the handler attribute
def target_handler(actor, dt, events):
screen_rect = pygame.display.get_surface().get_rect()
# A target does nothing but changing its direction when hittin the screen edges.
# Note that this implementation does not handle edge cases like a huge delta time.
if not screen_rect.contains(actor.rect):
actor.vel *= -1
def rocket_handler(actor, dt, events):
# A rocket will disappear when leaving the screen
screen_rect = pygame.display.get_surface().get_rect()
if not screen_rect.colliderect(actor.rect):
actor.kill()
# but when it hits a sprite with the can_be_destroyed-flag...
collisions = pygame.sprite.spritecollide(actor, actor.all_sprites, False)
for c in collisions:
if c.can_be_destroyed:
# ...we remove both sprites
actor.kill()
c.kill()
# ...and create to explosion sprites
Actor(actor.pos, ( 0, 0), 'yellow', explosion_handler, actor.all_sprites, timer=100)
Actor(c.pos, ( 0, 0), 'orange', explosion_handler, actor.all_sprites, timer=50)
def explosion_handler(actor, dt, events):
# An explosion will change its color 15 times
# every 100 milliseconds.
if actor.timer:
actor.timer -= dt
if actor.timer <= 0:
actor.image.fill(random_color())
actor.timer = 100
actor.counter += 1
if actor.counter >= 15:
actor.kill()
# Standard pygame main loop
def main():
width, height = 640, 480
pygame.init()
screen = pygame.display.set_mode((width, height))
clock = pygame.time.Clock()
sprites = pygame.sprite.Group()
dt = 0
# We create three targets
Actor((20, 20), ( 2, 0), 'dodgerblue', target_handler, sprites, can_be_destroyed=True)
Actor((120, 120), ( 2, 0), 'dodgerblue', target_handler, sprites, can_be_destroyed=True)
Actor((320, 220), ( 2, 0), 'dodgerblue', target_handler, sprites, can_be_destroyed=True)
while True:
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
if e.type == pygame.KEYDOWN:
# Fire a rocket when we press a key
Actor((320, 479), ( 0, -4), 'red', rocket_handler, sprites)
sprites.update(dt, events)
screen.fill((20, 20, 20))
sprites.draw(screen)
pygame.display.flip()
dt = clock.tick(60)
if __name__ == '__main__':
main()
![enter image description here](https://i.stack.imgur.com/4JNqu.gif)