События клавиатуры
Человек может управлять объектами в игре в основном с помощь клавиатуры, мыши, джойстика. Когда на «манипуляторах» что-то двигается или нажимается, то возникают события определенных типов. Обработкой событий занимается модуль pygame.event , который включает ряд функций, наиболее важная из которых уже ранее рассмотренная pygame.event.get() , которая забирает из очереди произошедшие события.
В pygame, когда фиксируется то или иное событие, создается соответствующий ему объект от класса Event . Уже с этими объектами работает программа. Экземпляры данного класса имеют только свойства, у них нет методов. У всех экземпляров есть свойство type . Набор остальных свойств события зависит от значения type .
События клавиатуры могут быть двух типов (иметь одно из двух значений type ) – клавиша была нажата, клавиша была отпущена. Если вы нажали клавишу и отпустили, то в очередь событий будут записаны оба. Какое из них обрабатывать, зависит от контекста игры. Если вы зажали клавишу и не отпускаете ее, то в очередь записывается только один вариант – клавиша нажата.
Событию типа «клавиша нажата» в поле type записывается числовое значение, совпадающее со значением константы pygame.KEYDOWN . Событию типа «клавиша отпущена» в поле type записывается значение, совпадающее со значением константы pygame.KEYUP .
У обоих типов событий клавиатуры есть атрибуты key и mod . В key записывается конкретная клавиша, которая была нажата или отжата. В mod – клавиши-модификаторы ( Shift , Ctrl и др.), которые были зажаты в момент нажатия или отжатия обычной клавиши. У событий KEYDOWN также есть поле unicode , куда записывается символ нажатой клавиши (тип данных str ).
Рассмотрим, как это работает. Пусть в центре окна имеется круг, который можно двигать по горизонтали клавишами стрелок клавиатуры:
import pygame import sys FPS = 60 W = 700 # ширина экрана H = 300 # высота экрана WHITE = (255, 255, 255) BLUE = (0, 70, 225) sc = pygame.display.set_mode((W, H)) clock = pygame.time.Clock() # координаты и радиус круга x = W // 2 y = H // 2 r = 50 while 1: for i in pygame.event.get(): if i.type == pygame.QUIT: sys.exit() elif i.type == pygame.KEYDOWN: if i.key == pygame.K_LEFT: x -= 3 elif i.key == pygame.K_RIGHT: x += 3 sc.fill(WHITE) pygame.draw.circle(sc, BLUE, (x, y), r) pygame.display.update() clock.tick(FPS)
В цикле обработки событий теперь проверяется не только событие выхода, но также нажатие клавиш. Сначала необходимо проверить тип, потому что не у всех событий есть атрибут key . Если сразу начать проверять key , то сгенерируется ошибка по той причине, что могло произойти множество событий. Например, движение мыши, у которого нет поля key . Соответственно, попытка взять значение из несуществующего поля ( i.key ) приведет к генерации исключения.
Часто проверку и типа и клавиши записывают в одно логическое выражение ( i.type == pygame.KEYDOWN and i.key == pygame.K_LEFT ). В Python так можно делать потому, что если первая часть сложного выражения возвращает ложь, то вторая часть уже не проверяется.
Если какая-либо клавиша была нажата, то проверяется, какая именно. В данном случае обрабатываются только две клавиши. В зависимости от этого меняется значение координаты x .
Проблема данного кода в том, что при выполнении программы, чтобы круг двигался, надо постоянно нажимать и отжимать клавиши. Если просто зажать их на длительный период, то объект не будет постоянно двигаться. Он сместиться только один раз на 3 пикселя.
Так происходит потому, что событие нажатия на клавишу происходит один раз, сколь долго бы ее не держали. Это событие было забрано из очереди функцией get() и обработано. Его больше нет. Поэтому приходится генерировать новое событие, еще раз нажимая на клавишу.
Как быть, если по логике вещей надо, чтобы шар двигался до тех пор, пока клавиша зажата? Когда же она отпускается, шар должен останавливаться. Первое, что надо сделать, – это перенести изменение координаты x в основную ветку главного цикла while . В таком случае на каждой его итерации координата будет меняться, а значит шар двигаться постоянно.
Во-вторых, в цикле обработки событий нам придется следить не только за нажатием клавиши, но и ее отжатием. Когда клавиша нажимается, какая-либо переменная, играющая роль флага, должна принимать одно значение, когда клавиша отпускается эта же переменная должна принимать другое значение.
В основном теле while надо проверять значение этой переменной и в зависимости от него менять или не менять значение координаты.
import pygame import sys FPS = 60 W = 700 # ширина экрана H = 300 # высота экрана WHITE = (255, 255, 255) BLUE = (0, 70, 225) RIGHT = "to the right" LEFT = "to the left" STOP = "stop" sc = pygame.display.set_mode((W, H)) clock = pygame.time.Clock() # координаты и радиус круга x = W // 2 y = H // 2 r = 50 motion = STOP while 1: for i in pygame.event.get(): if i.type == pygame.QUIT: sys.exit() elif i.type == pygame.KEYDOWN: if i.key == pygame.K_LEFT: motion = LEFT elif i.key == pygame.K_RIGHT: motion = RIGHT elif i.type == pygame.KEYUP: if i.key in [pygame.K_LEFT, pygame.K_RIGHT]: motion = STOP sc.fill(WHITE) pygame.draw.circle(sc, BLUE, (x, y), r) pygame.display.update() if motion == LEFT: x -= 3 elif motion == RIGHT: x += 3 clock.tick(FPS)
Использовать константы не обязательно, можно сразу присваивать строки или даже числа (например, motion = 1 обозначает движение вправо, -1 – влево, 0 – остановка). Однако константы позволяют легче понимать и обслуживать в дальнейшем код, делают его более информативным. Лучше привыкнуть к такому стилю.
Должно проверяться отжатие только двух клавиш. Если проверять исключительно KEYUP без последующей конкретизации, то отжатие любой клавиши приведет к остановке, даже если в это время будет по-прежнему зажиматься клавиша влево или вправо. Выражение i.key in [pygame.K_LEFT, pygame.K_RIGHT] обозначает, что если значение i.key совпадает с одним из значений в списке, то все выражение возвращает истину.
На самом деле существует способ по-проще. В библиотеке pygame с событиями работает не только модуль event . Так модуль pygame.key включает функции, связанные исключительно с клавиатурой. Здесь есть функция pygame.key.get_pressed() , которая возвращает кортеж двоичных значений. Индекс каждого значения соответствует своей клавиатурной константе. Само значение равно 1, если клавиша нажата, и 0 – если не нажата.
Эта функция подходит не для всех случаев обработки клавиатурных событий, но в нашем подойдет. Поэтому мы можем упростить код до такого варианта:
import pygame import sys FPS = 60 W = 700 # ширина экрана H = 300 # высота экрана WHITE = (255, 255, 255) BLUE = (0, 70, 225) sc = pygame.display.set_mode((W, H)) clock = pygame.time.Clock() # координаты и радиус круга x = W // 2 y = H // 2 r = 50 while 1: for i in pygame.event.get(): if i.type == pygame.QUIT: sys.exit() sc.fill(WHITE) pygame.draw.circle(sc, BLUE, (x, y), r) pygame.display.update() keys = pygame.key.get_pressed() if keys[pygame.K_LEFT]: x -= 3 elif keys[pygame.K_RIGHT]: x += 3 clock.tick(FPS)
Можно сказать, вызов get_pressed() снимает «маску» зажатых клавиш. Мы ее снимаем на каждой итерации главного цикла. Это даже не регистрация событий как таковых.
Выражение типа keys[pygame.K_LEFT] извлекает значение из кортежа по индексу, значение которого записано в константе K_LEFT . Если извлеченное значение True , то координата меняется.
Если необходимо, чтобы событие обрабатывалось при нажатии двух и более клавиш, то работает такое логическое выражение: keys[pygame.K_LEFT] and keys[pygame.K_a] (одновременное нажатие стрелки ‘влево’ и буквы ‘a’). Однако если нужно задействовать не обычные клавиши, а модификаторы, то данный номер не проходит.
В таком случае можно вернуться к первому варианту – перебирать события в цикле for :
. elif i.type == pygame.KEYDOWN: if i.key == pygame.K_LEFT and\ (i.mod & pygame.KMOD_SHIFT): motion = LEFT .
Здесь при if будет True , если перед нажатием стрелки был зажат левый Shift . Причем обратная последовательность: сначала зажать стрелку, потом Shift не сработает. Видимо модификаторы обрабатываются библиотекой pygame несколько отлично от обычных клавиш. Допустим, если при зажатии обычных клавиш генерируется только одно событие, то для модификаторов они генерируются постоянно или хранятся до отпускания в другой очереди.
Таким образом, если первым зажимается K_LEFT , то событие сразу обрабатывается. При этом в i.mod записывается отсутствие модификатора. Поэтому условие не срабатывает.
Если же первым зажимается модификатор, то это событие не теряется и позволяет условию при if выполнится в случае нажатия при этом обычной клавиши.
Весь перечень констант pygame, соответствующих клавишам клавиатуры, смотрите в документации: https://www.pygame.org/docs/ref/key.html
Практическая работа
Измените приведенную в уроке программу так, чтобы круг с той же скоростью, т. е. постепенно, возвращался назад в исходную точку, когда клавиша отпускается.
Курс с примерами решений практических работ:
pdf-версия
X Скрыть Наверх
Pygame. Введение в разработку игр на Python
Вращение 2D объекта по окружности
Ты скорее всего знаешь что во всех современных движках, таких как Unity вращение осуществляется с помощью функции, но нам интересно, как можно написать алгоритм
вращения своими руками.
Если проще то наш алгоритм работает так.
Мы представляем окружность, где:
- Центр это точка, вокруг которой мы вращаем объект.
- Радиус — расстояние от центра до вращаемого объекта.
Давайте взглянем на рисунок ниже:
Дано: длинна радиуса, координаты точки вращения, угол АОВ, АВ перпендикулярен оси ОХ.
Задача: Задать вращение точки А на 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 оборот. Это значит что нам нужна другая формула.

Давайте улучшим нашу формулу:
Дано:
треугольник ОАВ — прямоугольный
катет 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()
Примерно таком образом решается эта задача.
Надеюсь, ты смог извлечь полезную информацию из моего поста, желаю удачно применить полученные знания на практике
Python — Нужно сделать движение игрока в pygame
Мне вот на работу нужен человек, который будет делать за меня. Не хотите ли занять эту должность за спасибо от меня в виде слова? И это слово я уже сказал, авансом.
2 фев 2023 в 20:26
1 ответ 1
Сортировка: Сброс на вариант по умолчанию
import pygame import sys import random from time import sleep WHITE = (255, 255, 255) BLACK = (0, 0, 0) pygame.init() pygame.mouse.set_visible(False) screen = pygame.display.set_mode([900, 900]) pygame.display.set_caption('Gamee') clock = pygame.time.Clock() background_position = [0, 0] speed = 10 background_image = pygame.image.load("Фон1сл.png").convert_alpha() player_image = pygame.image.load("игрок.png").convert_alpha() player_image.set_colorkey(BLACK) vrag_image = pygame.image.load("vragn0.png").convert_alpha() done = False running = True speed = 5 x = 50 y = 50 while not done: keys = pygame.key.get_pressed() for event in pygame.event.get(): if event.type == pygame.QUIT: done = True running = True if event.type == pygame.KEYDOWN: if event.key == pygame.K_LEFT: x -= speed if event.key == pygame.K_LEFT: x += speed if event.key == pygame.pygame.K_UP: y += speed if event.key == pygame.pygame.K_DOWN: y -= speed screen.blit(background_image, background_position) playerx = 50 screen.blit(player_image, [x, y]) screen.blit(vrag_image, [10, 10]) pygame.display.flip() clock.tick(60) pygame.quit()
Отслеживать
ответ дан 3 фев 2023 в 6:48
37 6 6 бронзовых знаков
Danya Спасибо огромное!
3 фев 2023 в 16:43
- python
- pygame
-
Важное на Мете
Похожие
Подписаться на ленту
Лента вопроса
Для подписки на ленту скопируйте и вставьте эту ссылку в вашу программу для чтения RSS.
Дизайн сайта / логотип © 2024 Stack Exchange Inc; пользовательские материалы лицензированы в соответствии с CC BY-SA . rev 2024.4.29.8372
Chapter 8: Введение в анимацию

Посмотрите на рисунок 8.1. На нём показан зелёный ящик с несколькими объектами внутри него. Синий объект контролируется Python скриптом, который двигает его вокруг ящика, сталкивая с другими объектами. Скрипт, показаный ниже, содержит в себе много особенностей, которые есть и у 2D программ. Есть главный цикл, есть список x,y координат, есть переменные, контролирующие вектор.
Основной цикл программы контролируется самим Blender’ом. Нижеприведённый код python автоматически вызывается каждый раз в программном цикле. Поэтому код на Python не показывает главного программного цикла. Однако он всё-же существует.
У синего объекта есть местоположение, записанное в формате x,y,z. К нему можно получить доступ, а затем и изменить с помощью переменной blueobject.position . Массив location [0] содержит x, [1] содержит y, а [2] содержит z.
Вместо использования переменных change_x и change_y , как это было сделано 2D примерах этой главы, пример на Blender использует blueObject[«x_change»] и blueObject[«y_change»] .
Выражение if проверяет, достиг ли синий объект границ экрана и нужно ли изменить направление. В отличии от пикселей в 2D играх, местоположения объектов могут храниться в числах с плавающей точкой. Если хочется расположить объект между 5 и 6, вполне разрешено ставить его местоположение на 5.5.
Такое расширение позволяет лёгкое взаимодействие с джойстиком, которое будет показано далее в книге.
import bge # Получить ссылку на синий объект cont = bge.logic.getCurrentController() blueObject = cont.owner # Вывести x,y координаты синего объекта print (blueObject.position[0],blueObject.position[1] ) # Поменять x,y координаты в соответствии с x_change и # y_change. x_change и y_change - игровые свойства, # связанные с синим объектом. blueObject.position[0]+=blueObject["x_change"] blueObject.position[1]+=blueObject["y_change"] # Проверить, достиг ли объект границ. # Если да - поменять направление. Проделать это со всеми 4 краями. if blueObject.position[0] > 6 and blueObject["x_change"] > 0: blueObject["x_change"] *= -1 if blueObject.position[0] < -6 and blueObject["x_change"] < 0: blueObject["x_change"] *= -1 if blueObject.position[1] >6 and blueObject["y_change"] > 0: blueObject["y_change"] *= -1 if blueObject.position[1] < -6 and blueObject["y_change"] < 0: blueObject["y_change"] *= -1
Файл, продемонстрированный в видео данной главы, можно скачать здесь.
8.4 Повторение пройденного
You are not logged in. Log in here and track your progress.
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