Хорошо.Это действительно отличный вопрос о базовом программировании.
Как сделать так, чтобы прямоугольник не падал через платформы.[Простыми словами]
Позвольте мне сначала ответить «без усложнения кода [...] до сложного».
Когда вы пишете программы, они пишутся так, чтобынастолько просто, насколько это возможно.Если они «слишком продвинуты», это плохое программирование.Иногда должно быть много кода, потому что он покрывает множество «краевых условий», и это приводит к тому, что код выглядит сложным, но на самом деле это не так, его просто больше.
Итак: Как сделать так, чтобы прямоугольник не падал через платформы ... сначала давайте переформулируем объем работы ~
Как заставить прямоугольный объект падать с постоянной скоростью отверх экрана вниз.Он должен остановиться, когда нижние углы прямоугольника столкнутся с любым другим объектом, который находится на пути, по которому проходит прямоугольник.Если прямоугольный объект не попадает в объект, он исчезает в нижней части экрана.Если прямоугольный объект попадает в другой объект, он останавливается поверх него.
Хотя это описание более сложное, оно более полно описывает программу, которая должна быть написана, и фактически облегчает проектирование.
Одним из замечательных инструментов в библиотеке PyGame является класс спрайтов .Эта библиотека поддерживает прямоугольные объекты с простым обнаружением столкновений.Обычно люди используют это с растровым изображением, чтобы заполнить прямоугольник.Теперь спрайт PyGame - это питон class
.Класс - это object
, который является просто именем для набора данных и функций, которые имеют что-то общее.
Давайте создадим его сейчас:
class FallingSprite( pygame.sprite.Sprite ):
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.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 ( time_change > 0 ):
distance_moved = time_change * self.fall_speed / 1000
now_x, now_y = self.rect.center # Where am I, right now
updated_y = now_y + distance_moved
self.rect.center = ( now_x, updated_y )
self.last_update = time_now
# Did we fall off the bottom of the screen?
if ( updated_y > WINDOW_HEIGHT ):
self.kill()
def stop( self ):
self.fall_speed = 0
Итак, это классдля падающего спрайта.Функция __init__()
является особенной и вызывается при создании спрайта.Он загружает растровое изображение для представления прямоугольника и устанавливает несколько статистических данных об объекте - например, где он находится на экране, как быстро он падает.Интересно отметить, что мы используем системные часы для определения положения прямоугольника.Он начинается в верхней части экрана, но каждый раз, когда вызывается функция спрайта update()
(обычно каждый кадр), позиция Y
увеличивается (перемещение объекта вниз по экрану) в зависимости от реального времени вызова.Функция stop()
используется для отмены падения.
Итак, теперь у нас есть «падающий прямоугольник», теперь нужно несколько «платформ» для его блокировки (или нет).Мы снова используем объект спрайта PyGame, но, учитывая, что эта платформа не перемещается (или не изменяется вообще), она немного проще.Тем не менее, требуется координата position
в качестве параметра, когда она создается.Таким образом, мы можем сделать их несколько в разных позициях.
class PlatformSprite( pygame.sprite.Sprite ):
def __init__( self, position ):
pygame.sprite.Sprite.__init__( self )
self.image = pygame.image.load("brick_32.png").convert_alpha()
self.rect = self.image.get_rect()
self.rect.center = ( position )
Платформы не двигаются, поэтому функции update()
нет.
Следующий шаг - определить, когдападающий спрайт попадает в спрайт платформы.Еще одна полезная функция в библиотеке спрайтов - обнаружение коллизий.Фактически, возможно иметь группу спрайтов , которая может мгновенно определить, сталкивается ли спрайт с чем-либо в группе.Это то, что нам действительно нужно знать.Всякий раз, когда падающий спрайт перемещается, нам нужно знать, ударил ли он по любому из спрайтов платформы.
Поэтому мы создаем pygame.sprite.Group()
для хранения всех платформ.Мы также делаем один для одного падающего спрайта, но только потому, что это делает программу проще.
Чтобы узнать, попадет ли падающий спрайт на спрайты платформы, легко использовать функцию PyGame pygame.sprite.spritecollide()
.Это берет один спрайт и группу спрайтов и возвращает список пересекающихся спрайтов.Это делает обнаружение столкновений слишком простым:
FALLERS.update() # re-position the flower-pot
# Did it hit anything
if ( len( pygame.sprite.spritecollide( flower_pot_sprite, PLATFORMS, False ) ) > 0 ):
flower_pot_sprite.stop()
И это все.Код должен быть включен в некоторые другие вызовы для обработки пользовательского ввода и перерисовки каждого кадра экрана.Но это не много кода, и это не сложно.
import pygame
import random
import time
# 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 constant 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.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 ( self.fall_speed > 0 and time_change > 0 ):
distance_moved = time_change * self.fall_speed / 1000
now_x, now_y = self.rect.center # Where am I, right now
updated_y = now_y + distance_moved
self.rect.center = ( now_x, updated_y )
self.last_update = time_now
# Did we fall off the bottom of the screen?
if ( updated_y > WINDOW_HEIGHT ):
self.kill()
def stop( self ):
self.fall_speed = 0
class PlatformSprite( pygame.sprite.Sprite ):
""" A stationay sprite"""
def __init__( self, position ):
pygame.sprite.Sprite.__init__( self )
self.image = pygame.image.load("brick_32.png").convert_alpha()
self.rect = self.image.get_rect()
self.rect.center = ( position )
def udpate( self ):
# does not move
pass
### 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 )
PLATFORMS = pygame.sprite.Group() # a group for all the plaform sprites
for i in range(20):
# create a platform at a random position
new_platform = PlatformSprite( ( random.randrange( 0, WINDOW_WIDTH ), random.randrange( 0, WINDOW_HEIGHT ) ) )
PLATFORMS.add( new_platform )
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 )
# Move the flower-pot, did it hit anything?
FALLERS.update() # re-position the flower-pot
if ( len( pygame.sprite.spritecollide( flower_pot_sprite, PLATFORMS, False ) ) > 0 ):
flower_pot_sprite.stop()
# Re-draw the screen
WINDOW.fill( SKY_BLUE )
FALLERS.draw( WINDOW ) # draw the flower-pot
PLATFORMS.draw( WINDOW ) # draw the platforms
pygame.display.update()
pygame.display.flip()
# Update the window, but not more than 60fps
clock.tick_busy_loop( 60 )
pygame.quit()
Конечно, будучи такой простой программой, есть ошибки.Например: что происходит, когда падающий объект падает так быстро, чтобы он никогда не существовал в сталкивающейся позиции ?!В результате он может «провалиться» в кирпич - в один момент он был до кирпича, в следующий update()
он уже прошел кирпич.Ого.