Как увеличивать переменную скорости с течением времени, пока нажата клавиша?

#python #pygame

#python #pygame

Вопрос:

Я создаю игру в pygame. У меня есть персонаж, позиция которого изменяется следующим образом (также есть 3 других для L, R и D):

     if keys[pygame.K_UP]:
        y -= 10
  

Это перемещает символ на 10 пикселей каждые 60 мс. Я хотел бы написать функцию, которая увеличивает скорость, чем дольше нажата клавиша, а затем сбрасывает скорость после отпускания клавиши. Я пробовал это:

 def speed(velocity):
velocity *= 2
return velocity

if keys[pygame.K_UP]:
    y -= speed(10)
  

Это только увеличивает параметр на 2, но не увеличивает его по мере того, как дольше нажата клавиша, и я не мог понять, как вернуть переменную в исходное состояние после отпускания клавиши.
Как бы я это реализовал?
Спасибо.

Ответ №1:

Вот небольшой пример падающего спрайта в реальном времени. Нажатие Up уменьшает скорость и Down увеличивает ее. Возможно, я слишком усложнил пример, но если нажать Up достаточное количество раз, цветочный горшок упадет.

Я реализовал это как объект, который наследуется от класса sprite PyGame. Одна из приятных особенностей использования объекта — это возможность сохранить все атрибуты элемента (например, местоположение, растровое изображение, скорость и т.д.) Внутри объекта. Это означает, что ваша программа не усеяна глобальными переменными для этого и того. PyGame sprite также предоставляет множество полезных и элегантных функций. Конечно, вначале это немного дополнительная работа, но позже прогресс программирования будет намного плавнее и проще. (Если вы не хотите использовать объекты, по крайней мере, поместите все в словарь python или что-то подобное, чтобы сохранить все это вместе {"x":0, "y":0, "speed":3} и т.д.)

В любом случае … важной частью кода является FallingSprite.update() . Это занимает время между последним вызовом update() и now , используя его для вычисления того, на сколько пикселей должен переместиться объект, учитывая его скорость. Код использует простое вычисление постоянной скорости, а не правильное вычисление падения под действием силы тяжести, но формулу можно просто поменять местами. Разница вычисляется, затем применяется к x amp; y координатам спрайта rect (который задает положение на экране). Если объект выпадает из нижней части экрана (или верхней), он «оборачивается», чтобы начать все сначала.

 import pygame
import time
import sys

# Window size
WINDOW_WIDTH  = 400
WINDOW_HEIGHT = 400
# background colour
SKY_BLUE      = (161, 255, 254)


class FallingSprite( pygame.sprite.Sprite ):
    """ A falling flower-pot sprite.  Falls at a given velocity in real-time """
    def __init__( self ):
        pygame.sprite.Sprite.__init__( self )
        self.image       = pygame.image.load("flower_pot.png").convert_alpha()
        self.rect        = self.image.get_rect()
        self.rect.center = ( WINDOW_WIDTH//2, -50 )
        self.fall_speed  = 150 # pixels / second
        self.image_height= self.image.get_rect().height
        self.last_update = int( time.time() * 1000.0 )

    def update( self ):
        # There should have been movement since the last update
        # calculate the new position
        time_now    = int( time.time() * 1000.0 )
        time_change = time_now - self.last_update             # How long since last update?
        # If we're moving, and time has passed
        if ( self.fall_speed != 0 and time_change > 0 ):
            distance_moved   = time_change * abs( self.fall_speed ) / 1000
            now_x, now_y     = self.rect.center               # Where am I, right now
            # Fall down (or up)
            if ( self.fall_speed > 0 ):
                updated_y = now_y   distance_moved
            else:
                updated_y = now_y - distance_moved
            # Did we fall off the bottom of the screen?
            if ( updated_y > WINDOW_HEIGHT   self.image_height ):
                updated_y = -self.image.get_rect().height     # move to just above top of screen
            # ... or off the top?
            elif ( updated_y < 0 - self.image_height ):
                updated_y = WINDOW_HEIGHT   self.image_height # move to just below screen
            # Reposition the sprite
            self.rect.center = ( now_x, updated_y )
        self.last_update = time_now

    def adjustSpeed( self, amount ):
        self.fall_speed  = amount

    def stop( self ):
        self.fall_speed  = 0



### MAIN
pygame.init()
WINDOW    = pygame.display.set_mode( ( WINDOW_WIDTH, WINDOW_HEIGHT ), pygame.HWSURFACE|pygame.DOUBLEBUF|pygame.RESIZABLE )
pygame.display.set_caption("Flowerpot")

# Add some sprites
FALLERS   = pygame.sprite.Group()   # a group, for a single sprite
flower_pot_sprite = FallingSprite()
FALLERS.add( flower_pot_sprite )


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
        elif ( event.type == pygame.VIDEORESIZE ):
            WINDOW_WIDTH  = event.w
            WINDOW_HEIGHT = event.h
            WINDOW  = pygame.display.set_mode( ( WINDOW_WIDTH, WINDOW_HEIGHT ), pygame.HWSURFACE|pygame.DOUBLEBUF|pygame.RESIZABLE )

    # Movement keys
    keys = pygame.key.get_pressed()
    if ( keys[pygame.K_UP] ):
        flower_pot_sprite.adjustSpeed( -5 ) 
        print( "slower... ", end='' )
        sys.stdout.flush()

    elif ( keys[pygame.K_DOWN] ):
        flower_pot_sprite.adjustSpeed( 5 )  
        print( "faster! ", end='' )
        sys.stdout.flush()


    # Move the flower-pot, did it hit anything?
    FALLERS.update() # re-position the flower-pot

    # Re-draw the screen
    WINDOW.fill( SKY_BLUE )
    FALLERS.draw( WINDOW )    # draw the flower-pot
    pygame.display.flip()
    # Update the window, but not more than 60fps
    clock.tick_busy_loop( 60 )

pygame.quit()
  

flower_pot.png

Я понимаю, что технически это горшок с кактусом, пожалуйста, не записывайте.

Ответ №2:

Вы начали с правильного подхода, но проблема в том, что вы сохраняете скорость в локальной переменной внутри функции. Эта переменная будет сбрасываться при каждом вызове функции.

Вместо этого вы должны хранить скорость где-то в другом месте:

 # Somewhere earlier in the program, eg. before your main loop
# Store the initial speed in a variable
base_speed = 10
# Initialise current speed to initial speed
speed = base_speed
  

Затем при проверке того, нажата ли клавиша, вы могли бы сделать что-то вроде этого:

 if keys[pygame.K_UP]:
    y -= speed
    # Increase speed for the next round
    speed *= 2
else:
    # Did not press the key this time
    # Reset current speed to the initial speed (10, stored in the variable base_speed)
    speed = base_speed
  

Важной частью такого решения является

  1. это speed инициализируется перед любым циклом, в котором вы выполняете проверку, чтобы он не сбрасывался при каждом входе в цикл и

  2. что она инициализируется в той же функции, в которой вы ее используете, поэтому она доступна для функции

Комментарии:

1. Я понимаю, что вы имеете в виду. Единственная проблема заключается в использовании time.sleep() сейчас. Когда я пытаюсь увеличить переменную с течением времени, вся анимация переходит в режим ожидания на время ожидания в функции, по существу вызывая искусственную задержку. Есть ли способ использовать его на основе часов и не переводить программу в спящий режим?

2. Не используйте time.sleep() , вместо этого ограничьте частоту кадров, см. clock.tick(60) .