Перейти к содержимому

Как повернуть спрайт в pygame

  • автор:

Как сделать чтобы Спрайт поворачивался к мышке?

программирую игру на pygame. Решил сделать так, чтобы спрайт все время поворачивался к курсору мыши. Вот только как это сделать не знаю.

Отслеживать
задан 7 янв 2019 в 6:41
Данил Березовский Данил Березовский
171 2 2 золотых знака 4 4 серебряных знака 10 10 бронзовых знаков

2 ответа 2

Сортировка: Сброс на вариант по умолчанию

Вам нужно посчитать вектор от вашего персонажа до позиции курсора. Это можно сделать через вычитание позиции игрока и мыши:

mouse_x, mouse_y = pygame.mouse.get_pos() rel_x, rel_y = mouse_x - self.x, mouse_y - self.y 

Затем можно посчитать угол:

angle = math.atan2(rel_y, rel_x) 

Это вернет величину угла в радианах, а функции pygame.transform.rotate(surface, angle) требуется величина в градусах. Она также вычислит угол в другом направлении(против часовой стрелки), поэтому вам нужно просто добавить/отнять 90 или 180 градусов и изображение станет правильным:

angle = (180 / math.pi) * -math.atan2(rel_y, rel_x) 

После поворота вам нужно изменить положение прямоугольника:

self.image = pygame.transform.rotate(self.original_image, int(angle)) self.rect = self.image.get_rect(center=self.position) 

Обратите внимание, что я вращаю атрибут с именем self.original_image. При применении поворота к изображению оно может слегка искажаться.

import math def rotate(self): mouse_x, mouse_y = pygame.mouse.get_pos() rel_x, rel_y = mouse_x - self.x, mouse_y - self.y angle = (180 / math.pi) * -math.atan2(rel_y, rel_x) self.image = pygame.transform.rotate(self.original_image, int(angle)) self.rect = self.image.get_rect(center=self.position) 

Модули 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)

Загрузка BMP-изображения

Если у изображения нет прозрачного слоя, но он необходим, то следует воспользоваться методом set_colorkey() класса Surface :

dog_surf = pg.image.load('dog.bmp') dog_surf.set_colorkey((255, 255, 255))

Все пиксели, цвет которых совпадает с переданным в set_colorkey() значением, станут прозрачными.

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

Программирование аркадных игр
и обучение информатике

Copyright © 2017
English version by Paul Vincent Craven
Spanish version by Antonio Rodríguez Verdugo
Russian version by Vladimir Slav
Turkish version by Güray Yildirim
Portuguese version by Armando Marques Sobrinho and Tati Carvalho
Dutch version by Frank Waegeman
Hungarian version by Nagy Attila
Finnish version by Jouko Järvenpää
French version by Franco Rossi
Korean version by Kim Zeung-Il
Chinese version by Kai Lin

Вращение 2D объекта по окружности

Ты скорее всего знаешь что во всех современных движках, таких как Unity вращение осуществляется с помощью функции, но нам интересно, как можно написать алгоритм
вращения своими руками.

Если проще то наш алгоритм работает так.

Мы представляем окружность, где:

  1. Центр это точка, вокруг которой мы вращаем объект.
  2. Радиус — расстояние от центра до вращаемого объекта.

Давайте взглянем на рисунок ниже:

image

Дано: длинна радиуса, координаты точки вращения, угол АОВ, АВ перпендикулярен оси ОХ.
Задача: Задать вращение точки А на 360 градусов с помощью формулы.
Рассмотрим: треугольник ОАВ:
угол ОВА — прямой => треугольник ОАВ — прямоугольный треугольник;
тогда:
гипотенуза = радиусу окружности с центром в точке О
угол поворота = от 1 до 360 градусов
тогда чтобы найти катеты ОВ и АВ нужно:
АВ = радиус * cos(угла поворота),
ОВ = $inline$радиус * sin(угла поворота)$inline$
Теперь когда мы знаем размеры катетов
мы составим формулу:
для координаты по x:$inline$позиция по x = радиус * cos(угла поворота) + начальная позиция по x$inline$,
и для координаты по y: $inline$позиция по y = радиус * sin(угла поворота) + начальная позиция по y$inline$

Давайте попробуем применить формулы:

import pygame, math #colors black = (0, 0, 0) size = (500, 500) screen = pygame.display.set_mode(size) a = b = 200 clock = pygame.time.Clock() FPS = 60 for i in range(1, 361, 3): clock.tick(FPS) angle = i * 3.14 / 180 a = 100 * math.cos(angle) + 300 b = 100 * math.sin(angle) + 300 screen.fill(black) pygame.draw.circle(screen, (122, 0, 0), (int (a), int (b)), 6) pygame.display.update() 

Не сложно догадаться что делает этот код, а именно вращает наш кружок по оси, но почему лишь один раз?

Давайте вспомним уроки геометрии в седьмом классе: градусная мера угла не может превышает 360 градусов.

А если попробуем обнулять угол вращения, когда тот будет больше 360 градусов, давайте посмотрим что из этого выйдет:

import pygame, math #colors red = (122 , 0, 0) green = (0, 122, 0) black = (0, 0, 0) size = (500, 500) screen = pygame.display.set_mode(size) a = b = 200 i = 0 clock = pygame.time.Clock() FPS = 60 while True: clock.tick(FPS) for event in pygame.event.get(): if event.type == pygame.QUIT: quit() screen.fill(black) pygame.draw.circle(screen, red, (int (a), int (b)), 6) if i 360 градусов нет, а кружок прошел свой путь 

Этот код позволяет вращать кружок по оси бесконечно.

Однако у этой формулы есть один недостаток: можно вращать объекты только на 1 оборот. Это значит что нам нужна другая формула.

image

Давайте улучшим нашу формулу:

Дано:
треугольник ОАВ — прямоугольный
катет 1 = x1 — начальная координата X
катет 2 = y1 — начальная координата y

Задача:
улучшить формулу так, что-бы можно было вращать точку А на неизвестный угол.

Решение:
Из предыдущей задачи мы знаем что:
$inline$$inline$xPos = (lineLong * cos(a)) + StartPosX$inline$$inline$. => $inline$cos(a) = xPos — startPosX: (lineLong )$inline$

И теперь мы можем вывести формулу по нахождению угла a
$inline$yPos = (lineLong * sin(a)) + StartPosY$inline$ => $$display$$a = arccos((xPos — startPosX): lineLong)$$display$$.

А сейчас обновим нашу формулу и получим: $$display$$xPos = (lineLong * cos(a +b)) + startPosX$$display$$

Теперь у нас есть формула которая позволяет бесконечно вращать точку по окружности: $$display$$xPos = (lineLong * cos(a +arccos((xPos — startPosX): lineLong))) + startPosX$$display$$

Давайте напишем код, который будет вращать наш объект по нажатию на клавишу.

import pygame, math #colors black = (0, 0, 0) green = (0, 122, 0) size = (600, 600) screen = pygame.display.set_mode(size) a = b = 500 i = 0 clock = pygame.time.Clock() FPS = 60 while True: clock.tick(FPS) for event in pygame.event.get(): if event.type == pygame.QUIT: quit() keys = pygame.key.get_pressed() if keys[pygame.K_RIGHT]: angle = i * 3.14 / 180 l = math.acos((a - 400) / math.sqrt(500 ** 2 - 400 **2)) int(l) a = (100 * math.cos(angle + l)) + 400 b = (100 * math.sin(angle + l)) + 400 i -= 3 elif keys[pygame.K_LEFT]: angle = i * 3.14 / 180 l = math.acos((a - 400) / math.sqrt(500 ** 2 - 400 **2)) int(l) a = (100 * math.cos(angle + l)) + 400 # 400 - начальная точка по х и по у b = (100 * math.sin(angle + l)) + 400 i += 3 screen.fill(black) pygame.draw.circle(screen, green, (int (a), int (b)), 6) pygame.display.update() 

Примерно таком образом решается эта задача.

Надеюсь, ты смог извлечь полезную информацию из моего поста, желаю удачно применить полученные знания на практике

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *