Шуман Х. - Python для детей - 2019. # Startwerte festlegen Red (255,0,0)
Скачать 5.95 Mb.
|
Глава Игра с насекомыми 13 258 Figure.y -= 5 if event.key == pg.K_DOWN : Figure.y += 5 Теперь вызов метода blit изменяется следующим образом: Window.blit(Figure.image, (Figure.x, Figure.y)) К сожалению, по-прежнему есть, по крайней мере, два упу- щения, которые ты увидишь, если запустишь программу: жук перемещается на 5 пикселей в одном направлении, но тебе нужно отпускать и вновь нажимать клавишу для каждого последующего движения; спрайт оставляет след, как показано на рис. 13.7. Рис. 13.7.Во время движения жук клонируется Последнее упущение можно легко исправить, переместив показанную ниже строку: Window.fill(Green) Теперь жук может перемещаться свободно, но нам по-преж- нему нужен вариант повторения события при удержании клавиши. За это отвечает следующая инструкция: Управление персонажем 259 pg.key.set_repeat(20,20) Эта строка должна быть вверху, желательно прямо под ко- дом pg.init().set_repeat(). Она определяет продолжитель- ность повторения и интервал между двумя повторениями (поэкспериментируй с другими значениями!). Теперь ты можешь создать рабочую программу. Для этого напиши следующий исходный код (⇒ buggy1.py): # Pygame-графика import pygame as pg # Класс Player class Player(pg.sprite.Sprite) : def __init__(self, xPos=0, yPos=0) : super().__init__() self.image = pg.image.load("Bilder\Insekt1.png") self.x, self.y = xPos, yPos # Установка начальных значений Green = (0,255,0) # Запуск Pygame, создание игрового поля и персонажа pg.init() pg.key.set_repeat(20,20) Window = pg.display.set_mode((600, 400)) Figure = Player(250, 150) # Цикл событий running = True while running : for event in pg.event.get() : if event.type == pg.QUIT : running = False # Установка клавиш if event.type == pg.KEYDOWN : if event.key == pg.K_LEFT : Figure.x -= 5 if event.key == pg.K_RIGHT : Figure.x += 5 if event.key == pg.K_UP : Figure.y -= 5 if event.key == pg.K_DOWN : Figure.y += 5 # Положение спрайта в окне (новое) Window.fill(Green) Window.blit(Figure.image, (Figure.x, Figure.y)) Глава Игра с насекомыми 13 260 pg.display.update() # Завершение Pygame pg.quit() ¾ Запусти программу и нажимай клавиши со стрелками для перемещения жука (рис. 13.8). Рис. 13.8.Жук двигается в четырех направлениях Существует интересная альтернатива части кода, отвечающей за обработку нажатий клавиш (⇒ buggy1a.py): keys = pg.key.get_pressed() if keys[pg.K_LEFT]: Figure.x = 5 if keys[pg.K_RIGHT]: Figure.x += 5 if keys[pg.K_UP]: Figure.y = 5 if keys[pg.K_DOWN]: Figure.y += 5 В этом коде нажатые клавиши собираются в список и обрабаты- ваются, т. е. ты также можешь одновременно нажать две клавиши со стрелками для перемещения жука по диагонали. Поворот персонажа 261 Поворот персонажа Теперь ты можешь перемещать своего персонажа, жука, в горизонтальном и вертикальном направлениях. Что вполне естественно. Но меня беспокоит положение жука. Он продолжает смотреть вверх, независимо, в каком на- правлении движется. Это как-то неестественно, хотя, мо- жет, некоторые насекомые и бегают так. Чтобы решить эту проблему для нашего жука, нам нужно научиться поворачивать спрайт. За это отвечает функция rotate() , которая принимает в качестве параметра значе- ние угла (в градусах). Функция rotate() относится к группе transform. Существует не- сколько других полезных методов, показанных в табл. 13.3. Таблица 13.3. Методы трансформации transform.scale (файл, (ширина, высота)) Изменение размера изображения transform.flip (файл, xBool, yBool) Зеркальное отражение по горизон- тальной или вертикальной оси (true = да, false = нет) transform.rotate (файл, угол) Поворот изображения Давай поместим функцию вращения в новый метод для класса Player (⇒ buggy2.py): def rotate(self, degree) : self.Bild = pg.transform.rotate(self.image, degree) Я использовал новый атрибут, который принимает сущест- вующее изображение, а затем поворачивает его, сохраняя оригинал (рис. 13.9). Глава Игра с насекомыми 13 262 Direction = 0 (0°) Direction = 3 (270°) Direction = 1 (90°) Direction = 2 (180°) Рис. 13.9.Атрибут для поворота жука Вращение выполняется с шагом в 90°. Значение глобальной переменной Direction устанавливается равным 0 при запус- ке программы (рис. 13.10). Рис. 13.10.Исходное значение переменной Direction Затем она принимает значения от 0 до 3, если ты размес- тишь инструкции следующим образом (⇒ buggy2.py): if event.key == pg.K_LEFT : Direction = 1 Figure.x -= 5 if event.key == pg.K_RIGHT : Direction = 3 Figure.x += 5 if event.key == pg.K_UP : Direction = 0 Figure.y -= 5 Поворот персонажа 263 if event.key == pg.K_DOWN : Direction = 2 Figure.y += 5 Далее рисунок поворачивается соответствующим образом, чтобы жук теперь смотрел в ту сторону, куда он ползет: Figure.rotate(Direction*90) Чтобы все заработало, требуется внести еще несколько из- менений. Первое относится к классу Player (⇒ buggy2.py): def __init__(self, xPos=0, yPos=0) : super().__init__() self.image = pg.image.load("Bilder\Insekt1.png") self.x, self.y = xPos, yPos self.Bild = self.image После того как изображение было создано, оригинал оста- ется нетронутым, а с этого момента используется толь- ко копия. Поэтому вызов метода blit также изменяется (рис. 13.11): Window.blit(Figure.Bild, (Figure.x, Figure.y)) Рис. 13.11.Изменения в коде программы Глава Игра с насекомыми 13 264 Мы не вращаем оригинал по двум причинам. Во-первых, мы мо- жем каждый раз начинать вращение с одного и того же исходно- го угла. Во-вторых, любое вращение может привести к потере качества изображения. То же самое относится к масштабированию. Если потом использовать результат такого преобразования, качество изображения может ухудшаться все больше и больше. Это неже- лательно, поэтому лучше сохранить оригинал нетронутым. ¾ Запусти программу и поуправляй жуком (рис. 13.12). Правильно ли он поворачивается? Рис. 13.12.Теперь жук поворачивается «лицом» по направлению движения Отслеживание границ игрового поля Обратимся теперь к теме «покидание поля»: если ты хочешь, чтобы жук оставался в пределах видимости, нужны ограни- чения и соответствующие управляющие конструкции. Прежде всего нам нужны размеры игрового поля, которые я сейчас откорректирую: Отслеживание границ игрового поля 265 xMax, yMax = 600, 400 Конечно, ты можешь использовать другие значения. Эти переменные могут быть использованы при создании окна и персонажа: Window = pg.display.set_mode((xMax, yMax)) Figure = Player(xMax/2-50, yMax/2-50) Но и для наших границ мы можем использовать перемен- ные xMax и yMax. При этом мы проверяем позицию жука пос- ле каждого «шага», чтобы убедиться, что он все еще нахо- дится в игровом поле (⇒ buggy3.py): if event.key == pg.K_LEFT : Direction = 1 if Figure.x > 0 : Figure.x -= 5 if event.key == pg.K_RIGHT : Direction = 3 if Figure.x < xMax-100 : Figure.x += 5 if event.key == pg.K_UP : Direction = 0 if Figure.y > 0 : Figure.y -= 5 if event.key == pg.K_DOWN : Direction = 2 if Figure.y < yMax-100 : Figure.y += 5 ¾ Измени исходный код соответствующим образом, затем запусти программу и начни управлять жуком в пределах окна программы. Возможно, у тебя не получится использовать значение 100, поэто- му придется изменить его, чтобы соответствовать спрайту в про- грамме. У меня прямоугольник с изображением жука имеет тол- щину границы 100 пикселей. А если ты предпочитаешь, чтобы жук останавливался на некото- ром расстоянии от края окна программы, тогда также нужно ис- пользовать другое значение вместо 0. Глава Игра с насекомыми 13 266 Подведение итогов Проект еще не закончен, он станет настоящей игрой, но не сейчас. Уже в следующей главе мы продолжим. В этой главе ты научился управлять объектом с помощью клавиатуры и удерживать жука в пределах окна программы. Вот что ты узнал нового о Pygame: pygame Пакет для графики и игрового программирования init() Запуск pygame, инициализация всех модулей/коллекций quit() Выход из pygame display Группа методов для управления окном и экраном set_mode() Установка окна просмотра (в т. ч. и размера) update() Обновление окна Surface Класс для отображения изображений blit() «Перерисовка» изображения (и, таким образом, перекрытие исходного) fill() Заполнение области выбранным цветом draw Методы для рисования графических форм circle() Создание круга Ellipse() Создание эллипса line() Создание прямой линии rect() Создание прямоугольника sprite Набор методов для спрайтов Sprite Класс спрайта image.load() Загрузка файла изображения transform Методы трансформации, среди прочего, для поворота и масштабиро- вания объектов flip() Зеркальное отражение объекта rotate() Вращение объекта scale() Увеличение/уменьшение объекта key.get_pressed() Регистрация событий нажатия клавиш key.set_pressed() Повтор нажатий клавиш А также и в самом Python есть некоторые вещи, с которыми ты столкнулся впервые: super Вызов родительского метода event.type Тип события importas Импорт модуля под другим именем ...и задача 267 Несколько вопросов... 1. Почему нужно использовать изображения с прозрач- ным фоном для спрайтов? 2. Может ли event.type использоваться для обработки на- жатий клавиш с буквами? ...и задача 1. Сделай так, чтобы жук появлялся на противоположной стороне окна, а не просто останавливался на краю. 268 14 Как раздавить жука В предыдущей главе мы использовали клавиши со стрел- ками для перемещения жука по полю. В этой главе мы на- учимся применять для этого мышь, помимо клавиатуры. А еще мы расширим правила: мы разрешим насекомому свободно перемещаться без какого-либо контроля с нашей стороны. Итак, в этой главе ты узнаешь: как определить положение указателя мыши; кое-что о Пифагоре и арктангенсах; как перемещать фигуру под углом; как «ловить» объекты мышью; как создать еще один модуль Player. Разбираемся с мышью До сих пор жук управлялся нажатием одной из клавиш, но он также может ползти туда, где мы щелкнули мышью. В нашем следующем проекте нам не нужно управление с клавиатуры. Поэтому ты можешь создать новый документ и скопировать исходный код, но, если очень хочешь, так- же можешь добавить код, отвечающий за управление с по- мощью мыши, в предыдущий проект. Разбираемся с мышью 269 Давай возьмем код предыдущей программы и удалим все события, связанные с клавиатурой. Теперь речь пой- дет о щелчках мыши. Положение указателя мыши должно определяться: if event.type == pg.MOUSEBUTTONDOWN : (xPos, yPos) = pg.mouse.get_pos() Figure.x = xPos - 50 Figure.y = yPos - 50 Обязательное условие – модуль pygame импортируется сле- дующим образом: import pygame as pg Так как нажатие кнопки мыши – это событие (Mousebut tondown ), то пара значений определяется с помощью мето- да mouse.get_pos(). Так как параметры xPos и yPos указаны в круглых скобках, они образуют пару переменных, значе- ния которых с корректировкой присваиваются перемен- ным Figure.x и Figure.y, чтобы спрайт жука находился чуть ниже указателя мыши. Такое обозначение называется вектором – структурой, содержа- щей два (и более) числа. Это могут быть, например, координаты местоположения или длина и ширина прямоугольной области. Кроме того, группу из четырех значений, например, встречаю- щихся при обозначении параметров прямоугольников и эллип- сов, ты тоже можешь, по сути, называть вектором, но обычно ее называют типом Rect (от англ. rectangle – прямоугольник). ¾ Введи следующий код или измени старый. Затем запус- ти программу и щелкни где-нибудь в окне (⇒ buggy4.py): import pygame as pg # Класс Player class Player(pg.sprite.Sprite) : def __init__(self, xPos=0, yPos=0) : super().__init__() self.image = pg.image.load("Bilder\Insekt1.png") self.x, self.y = xPos, yPos self.Bild = self.image def rotate(self, degree) : self.Bild = pg.transform.rotate(self.image, degree) Глава_Как_раздавить_жука14_272distance_=_xDiff2_+_yDiff2yDiff_xDiff_Рис.'>Глава_Как_раздавить_жука14'>Глава Как раздавить жука 14 270 # Установка начальных значений Green = (0,255,0) xMax, yMax = 600, 400 # Запуск Pygame, создание игрового поля и персонажа pg.init() pg.key.set_repeat(20,20) Window = pg.display.set_mode((xMax, yMax)) Figure = Player(xMax/2-50, yMax/2-50) # Цикл событий running = True while running : for event in pg.event.get() : if event.type == pg.QUIT : running = False # Запрос мыши if event.type == pg.MOUSEBUTTONDOWN : (xPos, yPos) = pg.mouse.get_pos() Figure.x = xPos - 50 Figure.y = yPos - 50 # Положение спрайта в окне (новое) Window.fill(Green) Window.blit(Figure.Bild, (Figure.x, Figure.y)) pg.display.update() # Завершение Pygame pg.quit() Я оставил метод, отвечающий за направление жука, потому что он понадобится нам позже. Никуда без математики Пока все идет нормально. Или нет. Потому что бедное на- секомое не должно постоянно только прыгать. Мы хотим, чтобы жук приползал к месту назначения (и не торопился). Чтобы достичь этого, нам нужно определить путь, точнее: вычислить. Вот почему здесь понадобится математика. Сначала мы определяем расстояние от позиции жука до позиции щелчка мыши (рис. 14.1). Никуда без математики 271 Указатель мыши (xPos, yPos) Спрайт (Figure) xDiff yDiff Рис. 14.1.Траектория движения жука Однако это невозможно сделать сразу, сначала нам нужно выяснить разницу между координатами x и y фигуры и ука- зателем мыши: xDiff = xPos - Figure.x - 50 yDiff = yPos - Figure.y - 50 Ты можешь подумать, что значение переменной можно записать, скажем, так: Figure.x – xPos. И все равно значение будет то от- рицательным, то положительным. Это не важно, потому что впо- следствии нам понадобится возводить эти значения в квадрат, а в этом случае результат всегда положительный. Переменные xDiff и yDiff определяют количество пик- селей, на которое объект класса Player должен передви- гаться горизонтально (по оси x) и вертикально (по оси y). Реальный путь, которому он следует, определяется диаго- налью – самой длинной стороной в этом прямоугольном треугольнике. Глава Как раздавить жука 14 272 distance = xDiff 2 + yDiff 2 yDiff xDiff Рис. 14.2.Вычисление дистанции Чтобы вычислить дистанцию, мы используем формулу, вы- веденную греческим математиком Пифагором. Для этого нам сначала нужны квадраты расстояний xDiff и yDiff, ко- торые мы получаем следующим образом: xDiff*xDiff и yDiff*yDiff Эти два значения складываются. xDiff*xDiff + yDiff*yDiff И затем из суммы извлекается квадратный корень. Для этого мы используем функцию sqrt(): distance = math.sqrt(xDiff*xDiff + yDiff*yDiff) Поскольку эта математическая функция находится в от- дельном модуле, мы должны сначала импортировать его. Мы сделаем это, чтобы ты мог использовать указанное выше уравнение: from math import * Никуда без математики 273 Благодаря этому можно сократить вышеприведенное урав- нение так: distance = sqrt(xDiff*xDiff + yDiff*yDiff) Прежде чем отправить жука в путь, мы должны сначала вы- ровнять его. Поэтому нам нужно определить подходящий угол. Опять же, математическая функция, которую может предложить Python (в модуле Math), поможет нам: degree = atan2(-yDiff, xDiff) Слово atan – аббревиатура от arcus tangens, арктангенс, угловая функция. Сюда же относятся синусы, косинусы и тангенсы. А atan2() – специальная форма функции, кото- рая может принимать оба значения в качестве параметров. С помощью нее вычисляется угол. Важно, чтобы сначала передавалось значение по оси y, а затем x. Здесь нужно ука- зать значение по оси y с минусом перед ним. К сожалению, в результате получается угол не в градусах, а в так называемых радианах. Если тебе нужны подробности, радианы задействуют не градусы как единицу измерения, а значение «пи» (π) (примерно 3,14). Та- ким образом, 360° соответствуют 2π, 180° – π, 90° – π/2. С помощью показанной ниже функции мы преобразуем значение: degree = degrees(degree) - 90 В этой строке еще вычитаются 90°. Только тогда направле- ние будет правильным. Важно, чтобы обе переменные получили начальные значения при запуске, иначе программа не будет работать: distance = 0 degree = 0 Выравнивание жука выполняется следующим образом: Figure.rotate(degree) Глава Как раздавить жука 14 274 yDiff xDiff degree = ata n2( yDiff , xDiff ) degree = degrees (degree ) 90 Рис. 14.3.Вычисление траектории движения жука Теперь мы должны переместить нашего жука, чтобы он на- чал ползать. Для этого мы дополним класс Player методом перемещения (⇒ buggy5.py): def move(self, distance, xx, yy) : self.x += xx self.y += yy distance -= 1 return distance Параметры, которые принимает эта функция, – это шаги перемещения жука по горизонтали и вертикали (что при- водит к движениям по диагонали): self.x += xx self.y += yy Затем значение дистанции уменьшается на 1 и возвраща- ется: distance -= 1 return distance И вот как выглядит вызов функции: distance = Figure.move(distance, xDiff, yDiff) Но прежде должны быть определены параметры xDiff и yDiff: Собираем все вместе 275 xDiff /= distance yDiff /= distance xDiff yDiff xx yy Рис. 14.4.Траектория жука вычислена Таким образом у нас используются замечательные малень- кие шажочки, которые мы передаем методу, отвечающему за перемещение. Соответствующий вызов находится вне цикла for. И так будет происходить множество раз, пока персонаж не окажется достаточно близко от позиции щелч- ка мышью: if distance > 5 : distance = Figure.move(distance, xDiff, yDiff) pg.time.delay(5) Кроме того, я тут же определил задержку: метод time.de lay() создает (небольшую) паузу для отклика программы. Мне хватило 5 миллисекунд, но ты можешь попробовать другие значения. Собираем все вместе Чтобы ты мог проанализировать и внести изменения, я вновь привожу листинг целиком (⇒ BUGGY5.PY): import pygame as pg from math import * |