Поворот спрайтов Pygame (python)
У меня есть спрайт на pygame и мне нужно его повернуть на какое то количество градусов, возможно ли это без дополнительных спрайтов?
Лучший ответ
Саша ЕфименкоЗнаток (337) 3 года назад
Спс, лол я час гуглил, а в инглишь документации как-то желание поискать не было)))
Остальные ответы
Спрайт на не равное 90 градусам одними только сифонами и си +++не повернешь без промежуточного создания копии нужного результата. следовательно какая разница — заранее ты его расчитаешь или внутри игры ?
Саша ЕфименкоЗнаток (337) 3 года назад
ну по сколько мне нужна плавная анимация поворота на 360, мне минимум нужно будет сделать 120 кадров (это каждый кадр на 3 градуса), это просто засер памяти, но если нет другого варианта то ок
Антонио Вивальди Оракул (72291) Саша Ефименко, Тебе надо сделать на 90 градусов поворот (это 90 кадров на градус или как у тя 30 кадров примерно) и командами допернуть на 90 градусов инверсией разрядов в спрайте. и получится анимация. влезет.
Как повернуть спрайт в pygame
pygame documentation
pygame.transform
pygame module to transform surfaces
A Surface transform is an operation that moves or resizes the pixels. All these functions take a Surface to operate on and return a new Surface with the results.
Some of the transforms are considered destructive. These means every time they are performed they lose pixel data. Common examples of this are resizing and rotating. For this reason, it is better to re-transform the original surface than to keep transforming an image multiple times. (For example, suppose you are animating a bouncing spring which expands and contracts. If you applied the size changes incrementally to the previous images, you would lose detail. Instead, always begin with the original image and scale to the desired size.)
Changed in pygame 2.0.2: transform functions now support keyword arguments.
pygame.transform. flip ( ) ¶
flip vertically and horizontally
flip(surface, flip_x, flip_y) -> Surface
This can flip a Surface either vertically, horizontally, or both. The arguments flip_x and flip_y are booleans that control whether to flip each axis. Flipping a Surface is non-destructive and returns a new Surface with the same dimensions.
resize to new resolution
scale(surface, size, dest_surface=None) -> Surface
Resizes the Surface to a new size, given as (width, height). This is a fast scale operation that does not sample the results.
An optional destination surface can be used, rather than have it create a new one. This is quicker if you want to repeatedly scale something. However the destination must be the same size as the size (width, height) passed in. Also the destination surface must be the same format.
resize to new resolution, using scalar(s)
scale_by(surface, factor, dest_surface=None) -> Surface
Experimental: feature still in development available for testing and feedback. It may change. Please leave scale_by feedback with authors
Same as scale() , but scales by some factor, rather than taking the new size explicitly. For example, transform.scale_by(surf, 3) will triple the size of the surface in both dimensions. Optionally, the scale factor can be a sequence of two numbers, controlling x and y scaling separately. For example, transform.scale_by(surf, (2, 1)) doubles the image width but keeps the height the same.
New in pygame 2.1.3.
pygame.transform. rotate ( ) ¶
rotate an image
rotate(surface, angle) -> Surface
Unfiltered counterclockwise rotation. The angle argument represents degrees and can be any floating point value. Negative angle amounts will rotate clockwise.
Unless rotating by 90 degree increments, the image will be padded larger to hold the new size. If the image has pixel alphas, the padded area will be transparent. Otherwise pygame will pick a color that matches the Surface colorkey or the topleft pixel value.
filtered scale and rotation
rotozoom(surface, angle, scale) -> Surface
This is a combined scale and rotation transform. The resulting Surface will be a filtered 32-bit Surface. The scale argument is a floating point value that will be multiplied by the current resolution. The angle argument is a floating point value that represents the counterclockwise degrees to rotate. A negative rotation angle will rotate clockwise.
specialized image doubler
scale2x(surface, dest_surface=None) -> Surface
This will return a new image that is double the size of the original. It uses the AdvanceMAME Scale2X algorithm which does a ‘jaggie-less’ scale of bitmap graphics.
This really only has an effect on simple images with solid colors. On photographic and antialiased images it will look like a regular unfiltered scale.
An optional destination surface can be used, rather than have it create a new one. This is quicker if you want to repeatedly scale something. However the destination must be twice the size of the source surface passed in. Also the destination surface must be the same format.
scale a surface to an arbitrary size smoothly
smoothscale(surface, size, dest_surface=None) -> Surface
Uses one of two different algorithms for scaling each dimension of the input surface as required. For shrinkage, the output pixels are area averages of the colors they cover. For expansion, a bilinear filter is used. For the x86-64 and i686 architectures, optimized MMX routines are included and will run much faster than other machine types. The size is a 2 number sequence for (width, height). This function only works for 24-bit or 32-bit surfaces. An exception will be thrown if the input surface bit depth is less than 24.
New in pygame 1.8.
pygame.transform. smoothscale_by ( ) ¶
resize to new resolution, using scalar(s)
smoothscale_by(surface, factor, dest_surface=None) -> Surface
Experimental: feature still in development available for testing and feedback. It may change. Please leave smoothscale_by feedback with authors
Same as smoothscale() , but scales by some factor, rather than taking the new size explicitly. For example, transform.smoothscale_by(surf, 3) will triple the size of the surface in both dimensions. Optionally, the scale factor can be a sequence of two numbers, controlling x and y scaling separately. For example, transform.smoothscale_by(surf, (2, 1)) doubles the image width but keeps the height the same.
New in pygame 2.1.3.
pygame.transform. get_smoothscale_backend ( ) ¶
return smoothscale filter version in use: ‘GENERIC’, ‘MMX’, or ‘SSE’
get_smoothscale_backend() -> string
Shows whether or not smoothscale is using MMX or SSE acceleration. If no acceleration is available then «GENERIC» is returned. For a x86 processor the level of acceleration to use is determined at runtime.
This function is provided for pygame testing and debugging.
set smoothscale filter version to one of: ‘GENERIC’, ‘MMX’, or ‘SSE’
set_smoothscale_backend(backend) -> None
Sets smoothscale acceleration. Takes a string argument. A value of ‘GENERIC’ turns off acceleration. ‘MMX’ uses MMX instructions only. ‘SSE’ allows SSE extensions as well. A value error is raised if type is not recognized or not supported by the current processor.
This function is provided for pygame testing and debugging. If smoothscale causes an invalid instruction error then it is a pygame/SDL bug that should be reported. Use this function as a temporary fix only.
gets a copy of an image with an interior area removed
chop(surface, rect) -> Surface
Extracts a portion of an image. All vertical and horizontal pixels surrounding the given rectangle area are removed. The corner areas (diagonal to the rect) are then brought together. (The original image is not altered by this operation.)
NOTE : If you want a «crop» that returns the part of an image within a rect, you can blit with a rect to a new surface or copy a subsurface.
find edges in a surface
laplacian(surface, dest_surface=None) -> Surface
Finds the edges in a surface using the laplacian algorithm.
New in pygame 1.8.
pygame.transform. average_surfaces ( ) ¶
find the average surface from many surfaces.
average_surfaces(surfaces, dest_surface=None, palette_colors=1) -> Surface
Takes a sequence of surfaces and returns a surface with average colors from each of the surfaces.
palette_colors — if true we average the colors in palette, otherwise we average the pixel values. This is useful if the surface is actually greyscale colors, and not palette colors.
Note, this function currently does not handle palette using surfaces correctly.
New in pygame 1.8.
New in pygame 1.9: palette_colors argument
pygame.transform. average_color ( ) ¶
finds the average color of a surface
average_color(surface, rect=None, consider_alpha=False) -> Color
Finds the average color of a Surface or a region of a surface specified by a Rect, and returns it as a Color. If consider_alpha is set to True, then alpha is taken into account (removing the black artifacts).
New in pygame 2.1.2: consider_alpha argument
pygame.transform. grayscale ( ) ¶
grayscale a surface
grayscale(surface, dest_surface=None) -> Surface
Returns a grayscaled version of the original surface using the luminosity formula which weights red, green and blue according to their wavelengths.
An optional destination surface can be passed which is faster than creating a new Surface. This destination surface must have the same dimensions (width, height) and depth as the source Surface.
finds which, and how many pixels in a surface are within a threshold of a ‘search_color’ or a ‘search_surf’.
threshold(dest_surface, surface, search_color, threshold=(0,0,0,0), set_color=(0,0,0,0), set_behavior=1, search_surf=None, inverse_set=False) -> num_threshold_pixels
This versatile function can be used for find colors in a ‘surf’ close to a ‘search_color’ or close to colors in a separate ‘search_surf’.
It can also be used to transfer pixels into a ‘dest_surf’ that match or don’t match.
By default it sets pixels in the ‘dest_surf’ where all of the pixels NOT within the threshold are changed to set_color. If inverse_set is optionally set to True, the pixels that ARE within the threshold are changed to set_color.
If the optional ‘search_surf’ surface is given, it is used to threshold against rather than the specified ‘set_color’. That is, it will find each pixel in the ‘surf’ that is within the ‘threshold’ of the pixel at the same coordinates of the ‘search_surf’.
- dest_surf (pygame.Surface pygame object for representing imagesorNone) — Surface we are changing. See ‘set_behavior’. Should be None if counting (set_behavior is 0).
- surf (pygame.Surface pygame object for representing images ) — Surface we are looking at.
- search_color (pygame.Color pygame object for color representations ) — Color we are searching for.
- threshold (pygame.Color pygame object for color representations ) — Within this distance from search_color (or search_surf). You can use a threshold of (r,g,b,a) where the r,g,b can have different thresholds. So you could use an r threshold of 40 and a blue threshold of 2 if you like.
- set_color (pygame.Color pygame object for color representationsorNone) — Color we set in dest_surf.
- set_behavior (int) —
- set_behavior=1 (default). Pixels in dest_surface will be changed to ‘set_color’.
- set_behavior=0 we do not change ‘dest_surf’, just count. Make dest_surf=None.
- set_behavior=2 pixels set in ‘dest_surf’ will be from ‘surf’.
- search_surf=None (default). Search against ‘search_color’ instead.
- search_surf=Surface. Look at the color in ‘search_surf’ rather than using ‘search_color’.
- False, default. Pixels outside of threshold are changed.
- True, Pixels within threshold are changed.
The number of pixels that are within the ‘threshold’ in ‘surf’ compared to either ‘search_color’ or search_surf .
def test_threshold_dest_surf_not_change(self): """the pixels within the threshold. All pixels not within threshold are changed to set_color. So there should be none changed in this test. """ (w, h) = size = (32, 32) threshold = (20, 20, 20, 20) original_color = (25, 25, 25, 25) original_dest_color = (65, 65, 65, 55) threshold_color = (10, 10, 10, 10) set_color = (255, 10, 10, 10) surf = pygame.Surface(size, pygame.SRCALPHA, 32) dest_surf = pygame.Surface(size, pygame.SRCALPHA, 32) search_surf = pygame.Surface(size, pygame.SRCALPHA, 32) surf.fill(original_color) search_surf.fill(threshold_color) dest_surf.fill(original_dest_color) # set_behavior=1, set dest_surface from set_color. # all within threshold of third_surface, so no color is set. THRESHOLD_BEHAVIOR_FROM_SEARCH_COLOR = 1 pixels_within_threshold = pygame.transform.threshold( dest_surface=dest_surf, surface=surf, search_color=None, threshold=threshold, set_color=set_color, set_behavior=THRESHOLD_BEHAVIOR_FROM_SEARCH_COLOR, search_surf=search_surf, ) # # Return, of pixels within threshold is correct self.assertEqual(w * h, pixels_within_threshold) # # Size of dest surface is correct dest_rect = dest_surf.get_rect() dest_size = dest_rect.size self.assertEqual(size, dest_size) # The color is not the change_color specified for every pixel As all # pixels are within threshold for pt in test_utils.rect_area_pts(dest_rect): self.assertNotEqual(dest_surf.get_at(pt), set_color) self.assertEqual(dest_surf.get_at(pt), original_dest_color)
New in pygame 1.8.
Changed in pygame 1.9.4: Fixed a lot of bugs and added keyword arguments. Test your code.
Модули pygame.image и pygame.transform
Функция load() модуля pygame.image загружает изображение и создает экземпляр Surface , на котором отображено это изображение. В load() передается имя файла. «Родным» форматом является BMP, однако если функция pygame.image.get_extended() возвращает истину, то можно загружать ряд других форматов: PNG, GIF, JPG и др.
import pygame as pg import sys W = 400 H = 300 sc = pg.display.set_mode((W, H)) sc.fill((100, 150, 200)) dog_surf = pg.image.load('dog.bmp') dog_rect = dog_surf.get_rect(bottomright=(W, H)) sc.blit(dog_surf, dog_rect) pg.display.update() while 1: for i in pg.event.get(): if i.type == pg.QUIT: sys.exit() pg.time.delay(20)
Если у изображения нет прозрачного слоя, но он необходим, то следует воспользоваться методом set_colorkey() класса Surface :
dog_surf = pg.image.load('dog.bmp') dog_surf.set_colorkey((255, 255, 255))
Все пиксели, цвет которых совпадает с переданным в set_colorkey() значением, станут прозрачными.
У формата PNG с альфа-каналом (когда для точек можно настраивать степень прозрачности; обычно устанавливается полностью прозрачный фон) таких проблем нет:
sun_surf = pg.image.load('sun.png') sun_rect = sun_surf.get_rect() sc.blit(sun_surf, sun_rect)
Ко всем экземплярам Surface рекомендуется применять метод convert() , который, если не передавать аргументы, переводит формат кодирования пикселей поверхности в формат кодирования пикселей главной поверхности. При выполнении игры это ускоряет отрисовку поверхностей.
Если поверхность была создана на базе изображения с альфа-каналом, то вместо convert() надо использовать метод convert_alpha() , так как первый удаляет прозрачные пиксели (вместо них будет черный цвет). Таким образом, код загрузки и обработки изображений разных форматов должен выглядеть примерно так:
dog_surf = pg.image.load('dog.bmp').convert() sun_surf = pg.image.load('sun.png').convert_alpha()
Что по смыслу равносильно:
. dog_surf = pg.image.load('dog.bmp') dog_surf = dog_surf.convert() .
Метод convert() возвращает новую, конвертированную, поверхность. Он не изменяет ту, к которой применяется.
В модуле pygame.image есть функция save() , которая позволяет сохранять переданную ей поверхность (не обязательно главную) в формат BMP, TGA, PNG, JPEG. Пример:
while 1: for i in pygame.event.get(): if i.type == pygame.QUIT: sys.exit() elif i.type == pygame.KEYUP \ and i.key == pygame.K_s: pygame.image.save(sc, 'day.png') pygame.time.delay(20)
Изменение поверхностей
Модуль pygame.transform содержит функции для изменения поверхностей. Некоторые трансформации (например, изменение размера) приводят к ухудшению изображения из-за потери части пикселей. В таких случаях надо сохранять исходную поверхность и выполнять трансформации от нее.
Функции модуля transform , которые изменяют поверхности, возвращают новые. Первым аргументом им передается исходный Surface . Ниже приведены примеры использования наиболее востребованных функций.
Функция flip() переворачивает Surface по горизонтали и вертикали, к потери качества не приводит. Указывается поверхность и булевыми значениями оси переворота.
import pygame import sys sc = pygame.display.set_mode((400, 300)) sc.fill((100, 150, 200)) dog_surf = pygame.image.load('dog.bmp').convert() dog_surf.set_colorkey((255, 255, 255)) dog_rect = dog_surf.get_rect(center=(200, 150)) sc.blit(dog_surf, dog_rect) pygame.display.update() while 1: for i in pygame.event.get(): if i.type == pygame.QUIT: sys.exit() # При отжатии (нажать и отпустить) клавиши f elif i.type == pygame.KEYUP and i.key == pygame.K_f: # собака перевернется слева направо flip = pygame.transform.flip(dog_surf, True, False) sc.fill((100, 150, 200)) sc.blit(flip, dog_rect) pygame.display.update(dog_rect) pygame.time.delay(20)
Поворот и изменение размера:
import pygame import sys sc = pygame.display.set_mode((400, 300)) sc.fill((100, 150, 200)) dog_surf = pygame.image.load('dog.bmp').convert() dog_surf.set_colorkey((255, 255, 255)) dog_rect = dog_surf.get_rect(center=(200, 150)) sc.blit(dog_surf, dog_rect) pygame.display.update() # ждем 1 секунду перед изменением pygame.time.wait(1000) sc.fill((100, 150, 200)) # уменьшаем в два раза scale = pygame.transform.scale( dog_surf, (dog_surf.get_width() // 2, dog_surf.get_height() // 2)) scale_rect = scale.get_rect(center=(200, 150)) sc.blit(scale, scale_rect) pygame.display.update(dog_rect) pygame.time.wait(1000) sc.fill((100, 150, 200)) # поворачиваем на 45 градусов rot = pygame.transform.rotate(dog_surf, 45) rot_rect = rot.get_rect(center=(200, 150)) sc.blit(rot, rot_rect) pygame.display.update() while 1: for i in pygame.event.get(): if i.type == pygame.QUIT: sys.exit() pygame.time.delay(20)
Практическая работа
Допустим, у вас есть такое изображение вида сверху машины:
Напишите программу управления ее движением с помощью стрелок клавиатуры (вверх, вниз, влево, вправо) так, чтобы объект всегда двигался головой вперед.
Курс с примерами решений практических работ:
pdf-версияX Скрыть Наверх
Pygame. Введение в разработку игр на Python
Стрелялка с Pygame №6: анимация спрайтов
Шестая часть проекта «Стрелялка с Pygame». Если пропустили, обязательно вернитесь и начните с первой части. В этом уроке астероиды будут выглядеть интереснее благодаря анимациям.
В этой серии уроков будет создана полноценная игра на языке Python с помощью библиотеки Pygame. Она будет особенно интересна начинающим программистам, которые уже знакомы с основами языка и хотят углубить знания, а также узнать, что лежит в основе создания игр.
Анимированные астероиды
Все астероиды выглядят одинаково, что не очень-то впечатляет:
Добавить разнообразия и привлекательности можно, заставив их вращаться. Благодаря этому будет создаваться впечатление, что они действительно летят в космосе. Это довольно легко сделать. Так же, как и с функцией pygame.transform.scale() , используемой для изменения размера спрайта Игрока, для вращения нужно задействовать pygame.transform.rotate() . Но чтобы сделать это правильно, нужно ознакомиться с парой нюансов.
В первую очередь необходимо добавить свойства спрайту Mob :
class Mob(pygame.sprite.Sprite): def __init__(self): pygame.sprite.Sprite.__init__(self) self.image = meteor_img self.image.set_colorkey(BLACK) self.rect = self.image.get_rect() self.radius = int(self.rect.width * .85 / 2) self.rect.x = random.randrange(WIDTH - self.rect.width) self.rect.y = random.randrange(-150, -100) self.speedy = random.randrange(1, 8) self.speedx = random.randrange(-3, 3) self.rot = 0 self.rot_speed = random.randrange(-8, 8) self.last_update = pygame.time.get_ticks()
Первое свойство rot (сокращенно от «rotation» (вращение)) будет измерять, на сколько градусов должен вращаться астероид. Начальное значение — 0 , но оно будет меняться со временем. rot_speed измеряет, на сколько градусов астероид будет поворачиваться каждый раз — чем больше число, тем быстрее будет происходить вращение. Подойдет любое значение: положительное задаст вращение по часовой стрелке, а отрицательное — против.
Последнее свойство необходимо для контроля скорости анимации. Для игры не нужно каждый раз менять изображение спрайта в каждом кадре. При анимации изображения спрайта нужно лишь определить время — как часто оно будет меняться.
В библиотеке есть объект pygame.time.Clock() , который называется clock (часы). Он позволяет контролировать FPS (количество кадров в секунду). Вызывая pygame.time.get_ticks() , можно узнать, сколько миллисекунд прошло с тех пор, как часы были запущены. Так можно будет сказать, прошло ли достаточно времени, что в очередной раз менять изображение спрайта.
Вращение изображения
Для этой операции потребуется еще несколько строк кода. Используем новый метод self.rotate() , который можно добавить в метод update() :
def update(self): self.rotate()
Благодаря этому можно будет добиться того, что в методе не будет лишней информации, а вращение можно убрать, закомментировав всего одну строку. Вот начало метода вращения:
def rotate(self): now = pygame.time.get_ticks() if now - self.last_update > 50: self.last_update = now # вращение спрайтов
Сначала проверяется текущее время, затем вычитается время последнего обновления. Если прошло более 50 миллисекунд, нужно обновлять изображение. Добавляем значение now в last_update и можно делать вращение. Кажется, что осталось лишь применить его к спрайту — как-то так:
self.image = pygame.transform.rotate(self.image, self.rot_speed)
Но в таком случае возникнет проблема:
Вращение разрушительно!
Это происходит, потому что изображение представляет собой сетку пикселей. При перемещении их в новое положение пиксели не выравниваются, и некоторая информация просто пропадает. Если повернуть изображение один раз, то ничего страшного не случится. Но при постоянном вращении астероид превращается в кашу.
Решение состоит в том, чтобы использовать переменную rot для отслеживания общей степени вращения (добавляя rot_speed с каждым обновлением) и вращать оригинальное изображение с таким шагом. Таким образом спрайт каждый раз будет представлять собой чистое изображение, которое повернется всего один раз.
Сначала нужно скопировать оригинальную картинку:
class Mob(pygame.sprite.Sprite): def __init__(self): pygame.sprite.Sprite.__init__(self) self.image_orig = random.choice(meteor_images) self.image_orig.set_colorkey(BLACK) self.image = self.image_orig.copy() self.rect = self.image.get_rect() self.radius = int(self.rect.width * .85 / 2) self.rect.x = random.randrange(WIDTH - self.rect.width) self.rect.y = random.randrange(-150, -100) self.speedy = random.randrange(1, 8) self.speedx = random.randrange(-3, 3) self.rot = 0 self.rot_speed = random.randrange(-8, 8) self.last_update = pygame.time.get_ticks()
Затем в методе rotate нужно обновить значение rot и применить вращение к исходному изображению:
def rotate(self): now = pygame.time.get_ticks() if now - self.last_update > 50: self.last_update = now self.rot = (self.rot + self.rot_speed) % 360 self.image = pygame.transform.rotate(self.image_orig, self.rot)
Стоит отметить, что был использован оператор остатка, % , чтобы значение rot не было больше 360.
Изображения уже выглядят хорошо, но все еще есть одна проблема:
Кажется, что астероиды прыгают, а не плавно вращаются.
Обновление прямоугольника ( rect )
После поворота изображения, размер rect может оказаться неправильным. В качестве примера стоит рассмотреть процесс вращения корабля:
Здесь видно, что при вращении rect остается одинаковым. Но важно каждый раз вычислять размеры прямоугольника при изменении изображения:
Можно увидеть, как меняется прямоугольник при повороте изображения. Для исправления «прыгающего» эффекта нужно убедиться, чтобы центр прямоугольника всегда находится в одном и том же месте, а не привязываться к верхнему левому углу:
Для этого необходимо указать положение центра прямоугольника, вычислить новый размер и сохранить координаты центра в обновленный прямоугольник:
def rotate(self): now = pygame.time.get_ticks() if now - self.last_update > 50: self.last_update = now self.rot = (self.rot + self.rot_speed) % 360 new_image = pygame.transform.rotate(self.image_orig, self.rot) old_center = self.rect.center self.image = new_image self.rect = self.image.get_rect() self.rect.center = old_center
Случайные размеры астероида
Последнее, что можно сделать, чтобы астероиды выглядели интереснее, — задавать их размеры случайным образом.
Сперва необходимо загрузить их и добавить в список:
meteor_images = [] meteor_list =['meteorBrown_big1.png','meteorBrown_med1.png', 'meteorBrown_med1.png','meteorBrown_med3.png', 'meteorBrown_small1.png','meteorBrown_small2.png', 'meteorBrown_tiny1.png'] for img in meteor_list: meteor_images.append(pygame.image.load(path.join(img_dir, img)).convert())
Далее нужно просто каждый раз выбирать случайный астероид для появления в кадре:
class Mob(pygame.sprite.Sprite): def __init__(self): pygame.sprite.Sprite.__init__(self) self.image_orig = random.choice(meteor_images) self.image_orig.set_colorkey(BLACK) self.image = self.image_orig.copy()
Так гораздо лучше!
Итого
Анимированные спрайты делают игру визуально более привлекательной, вне зависимости от того, что именно происходит на экране. Но чем больше анимаций, тем больше изображений нужно отслеживать. Поэтому важно организовывать их и использовать такие инструменты, как pygame.transform (помня об их ограничениях).
В следующий раз разберем, как вести счет, и узнаем, как выводить текст на экран.