Шуман Х. - Python для детей - 2019. # Startwerte festlegen Red (255,0,0)
Скачать 5.95 Mb.
|
Глава Уклониться или проиграть 15 292 self.isHit = False В другой переменной хранится текущее положение фигу- ры. Номер также указывает номер отображаемого изобра- жения: self.Status = 0 Используются следующие значения: Status Действие Файл с изображением 0 Фигура стоит dostand.png 1 Фигура присела doduck.png 2 Фигура подпрыгнула dojump.png Стоять, приседать и подпрыгивать Затем нам нужен метод, который влияет на состояние пер- сонажа, то есть определяет, какое изображение должно отобра жаться, – в зависимости от того, перемещается ли игрок в настоящее время – приседает или подпрыгивает: def setState(self, Nr) : self.Status = Nr self.Bild = self.image[Nr] Давай пока отставим это в сторону. Лучше посмотрим, чем занимается наш персонаж на поле. Поэтому мы перейдем к основной программе. Ниже представлен ее полный лис- тинг (⇒ dodger1.py): # Ловкач import pygame as pg import random from dplayer import * # Установка начальных значений Yellow = (255,255,0) xMax, yMax = 800, 400 # Запуск Pygame, создание игрового поля и персонажа pg.init() pg.key.set_repeat(20,20) pg.display.set_caption("Моя игра") Window = pg.display.set_mode((xMax, yMax)) Стоять, приседать и подпрыгивать 293 Figure = Player(20,30) # Цикл событий 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_UP : Figure.setState(2) if event.key == pg.K_DOWN : Figure.setState(1) if event.key == pg.K_RETURN : Figure.setState(0) # Позиционирование спрайта в окне Window.fill(Yellow) Window.blit(Figure.Bild, (Figure.x, Figure.y)) pg.display.update() # Завершение Pygame pg.quit() ¾ Напиши все строки или используй исходный код из за- груженного файла ( dmkpress.com ). Запусти программу и нажимай клавиши ↑, ↓ и Enter. После нажатия соответствующей клавиши игрок приседа- ет, прыгает или просто стоит на месте. Все в твоих руках. Для позиции «стоять» я использовал дополнительную кла- вишу. Однако для нормальной игры персонаж должен ав- томатически возвращаться в стоячее положение, допуская нажатие только клавиши ↑ или ↓. К сожалению, это не так-то просто: автоматическое воз- вращение из прыжка или приседания должно происходить спустя некоторое время. if event.key == pg.K_UP : Figure.setState(2) getTime(True) if event.key == pg.K_DOWN : Figure.setState(1) getTime(True) Задержка во времени бесполезна, если ты совершаешь по- следующий вызов метода setState(0) без каких-либо усло- Глава Уклониться или проиграть 15 294 вий. Должно быть примерно так: когда прошло определен- ное время, нужно вернуться к позиции «стоять». Поэтому нам нужно что-то вроде таймера, который стар- тует после каждого прыжка или приседания, а затем сбра- сывается. Во-первых, нам нужна глобальная переменная, которая фиксирует время запуска. Мы присваиваем ей зна- чение 0 в начале программы: Start = 0 Я помещаю фактический таймер в функцию, которая вы- глядит следующим образом (⇒ dodger2.py): def getTime(Reset) : global Start if Reset : Start = pg.time.get_ticks() Diff = pg.time.get_ticks() - Start return Diff Reset – это переменная переключения. Если она имеет зна- чение False, тогда рассчитывается и возвращается разница между временем начала и текущим временем: Diff = pg.time.get_ticks() - Start return Diff Если Reset имеет значение True – текущее время в момент запуска (и, таким образом, таймер сбрасывается), в резуль- тате чего Diff, естественно, возвращает 0. Определение времени (в миллисекундах) выполняется методом time. get_ticks() (рис. 15.4). Стоять, приседать и подпрыгивать 295 Рис. 15.4.Создание таймера Новая функция теперь вставлена несколько раз в цикл while . Сначала обработаем две клавиши со стрелками (⇒ dodger2.py): if event.type == pg.KEYDOWN : if event.key == pg.K_UP : Figure.setState(2) getTime(True) if event.key == pg.K_DOWN : Figure.setState(1) getTime(True) Каждый раз, когда ты нажимаешь клавишу ↑ или ↓, таймер сбрасывается. Вне цикла for мы фиксируем время, прошедшее с момента последнего нажатия клавиши со стрелкой, используя воз- вращаемое значение функции getTime(): Time = getTime(False) if Time > 1000 : Figure.setState(0) И если прошло больше 1000 миллисекунд, тогда персонаж переходит в режим стояния, а также если ты отпустил соот- ветствующую клавишу со стрелкой. Вот одно из мест, где ты можешь впоследствии повлиять на то, насколько сложным будет маневр персонажа. Глава Уклониться или проиграть 15 296 ¾ Напиши исходный код переменной Start, определи функцию getTime и вызови ее. Затем запусти игру и по- пробуй, насколько хорошо персонаж выполняет упраж- нения по гимнастике. Класс Thing В конце концов, игрок уже знает, как он может справиться с предстоящими опасностями. Но до сих пор он приседа- ет и прыгает под принуждением. Поэтому нам нужно как- то заставить его уворачиваться. И для этого нужен новый класс. Я использую здесь мяч (точнее, круг, который ты можешь себе представить, это мяч или нечто еще опасное). Но это также может быть что-то угловатое, т. е. некая «вещь», по- этому я просто называю новый класс Thing. Что должен делать мяч? Он должен появляться в правой ча- сти поля и двигаться влево к игроку. Затем он должен сно- ва оказаться в правой части. И на этот раз или сверху, или снизу. Давай посмотрим, как такой класс может выглядеть (⇒ dthing.py): import pygame as pg import random # Объект class Thing(pg.sprite.Sprite) : def __init__(self, name, xPos=0, yPos=0) : super().__init__() self.image = pg.image.load(name) self.x, self.y = xPos, yPos self.Bild = self.image self.Width = self.Bild.get_width() self.Height = self.Bild.get_height() self.rect = self.Bild.get_rect() def setPosition(self, xPos, yPos, updown) : self.x = xPos if updown : yOyU = random.randint(0,1) self.y = yOyU * yPos + self.Height/2 else : self.y = yPos def move(self, xx, yy) : self.x += xx self.y += yy Класс Thing 297 def controlRestart(self, xx, yy) : if self.x < 0 : yOyU = random.randint(0,1) self.x = xx - self.Width/2 self.y = yOyU * yy + self.Height/2 return True else : return False ¾ Создай новый файл и вставь исходный код, затем со- храни файл. (Конечно, ты также можешь использовать другое имя, а не dthing.py.) Давай рассмотрим весь код построчно. Метод init прини- мает имя файла изображения, позволяя тебе выбрать, ка- кой объект будет перемещаться: def __init__(self, name, xPos=0, yPos=0) : Внутри метода сначала загружается изображение, затем определяется положение спрайта: self.image = pg.image.load(name) self.x, self.y = xPos, yPos После создания экземпляра изображения в Bild нам по-прежнему нужны ширина и высота спрайта – но об этом позже: self.Bild = self.image self.Width = self.Bild.get_width() self.Height = self.Bild.get_height() Другой метод гарантирует размещение объекта случайным образом выше или ниже средней линии. Метод setPosi tion() принимает сведения о расположении и переменную переключения: def setPosition(self, xPos, yPos, updown) : Сначала определяется горизонтальное положение (x): self.x = xPos Для оси y код выглядит немного иначе, так как значение зависит от переменной updown. Если ей присвоено значение False , то значение y оказывается равным x: Глава Уклониться или проиграть 15 298 self.y = yPos Если переменной updown присвоено значение True, то слу- чайное число 0 или 1 будет генерироваться так, чтобы мяч не всегда стартовал из одного и того же места: yOyU = random.randint(0,1) В этом случае центральная линия объекта должна быть пе- редана yPos. Тогда ты сможешь установить, будет ли мяч на- ходиться вверху или катиться внизу: self.y = yOyU * yPos + self.Height/2 Давай перейдем к методу, который перемещает объект: def move(self, xx, yy) : self.x += xx self.y += yy Я мало что скажу о нем, потому что ты уже знаешь несколь- ко способов перемещения. Остался последний метод – controlRestart(). Он принимает в качестве параметра позицию, в которую объект должен быть перемещен и из которой мяч должен быть выпущен в дальнейшем: def controlRestart(self, xx, yy) : Как только мяч оказывается в левой части игрового поля (x < 0), его следует вернуть обратно на правый край: self.x = xx - self.Width/2 Если ты укажешь xMax в качестве параметра для xx, полови- на ширины спрайта будет вычтена. Разумеется, высота прыжка мяча должна быть определена случайным образом: yOyU = random.randint(0,1) self.y = yOyU * yy + self.Height/2 Здесь тоже есть возвращаемое значение: Учим персонажа уклоняться 299 return True Если мяч находится в области движения, т. е. двигается справа налево, то это возвращаемое значение должно быть False : else : return False True означает, что мяч перезапускается, а False – что он в пути. Учим персонажа уклоняться Как насчет основной программы? Помимо того факта, что там должна быть добавлена инструкция импорта класса Thing , есть также следующие изменения. Сначала создаем объект мяча непосредственно после Player (⇒ dodger3.py): Ball = Thing("Bilder/ball1.png") Кроме того, мы ставим этот мяч на край поля (рис. 15.5). Ball.setPosition(xMax-50, yMax/2, True) Рис. 15.5.Размещение мяча Можно было указать позицию создания объекта мяча, но только метод setPosition() позволяет начать движение случайным об- разом вверху или внизу поля. Глава Уклониться или проиграть 15 300 В цикле while после инструкций, касающихся времени, указаны инструкции для управления перемещением мяча справа налево (рис. 15.6). Ball.move(-1, 0) Ball.controlRestart(xMax-50, yMax/2) И последнее, но не менее важное: ты также должен убе- диться, что летающий или катящийся мяч отображается (рис. 15.6). Итак, вот еще один вызов blit: Window.blit(Ball.Bild, (Ball.x, Ball.y)) Рис. 15.6.Добавленные инструкции ¾ Напиши показанный исходный код, затем запусти про- грамму. В зависимости от случайного значения мяч будет наполо- вину отображаться вверху или внизу в правой части окна, а затем двигаться к персонажу. ¾ Старайся избегать попадания мяча. Получается? Ка- жется слишком легким? Увеличь значение первого па- раметра в методе move. Независимо от того, уклонился ты или нет, мяч мчится мимо, а затем снова появляется справа и вновь катится. Для визуализации неудачного уклонения, чтобы был ка- кой-то эффект, нам нужно обнаруживать столкновения. Но как именно это должно выглядеть? Стоит ли персонаж на месте, приседает или вскакивает, всегда есть контакт между этими двумя объектами. Учим персонажа уклоняться 301 Давай взглянем в табл. 15.1, чтобы увидеть, какие варианты есть, когда объекты Ball и Player встречаются: Расположение | Состояние DoStand DoDuck DoJump Мяч вверху врезается пролетает мимо врезается Мяч внизу врезается врезается пролетает мимо Можно увидеть (только) два случая, когда уклонение срабо- тает (рис. 15.7): 1) когда мяч летит вверху, а игрок приседает; 2) когда мяч катится внизу, а игрок подпрыгивает. Рис. 15.7.Два варианта успешного уклонения В случае ошибки (столкновения) игра заканчивается: в иг- рока попал мяч, и тот проигрывает. Итак, нам нужны две функции управления: одна для столкновения и одна для уклонения. Давай воспользуемся методом dogde (⇒ dplay er. py): def dodge(self, yPos, yMiddle) : if (yPos < yMiddle and self.Status == 1) \ or (yPos > yMiddle and self.Status == 2) : return True else : return False Параметр yPos информирует о высоте приближаемого мяча (или подкатывающегося), а yMiddle определяет централь- ную линию: если мяч летит выше нее, то поможет только приседание (Status == 1), а если мяч катится ниже, тогда нуж- Глава Уклониться или проиграть 15 302 но присесть (Status == 2). Если уклонение прошло успешно, метод возвращает True, в противном случае – False. Вызов этого метода имеет смысл только при наличии кон- такта между прямоугольником фигуры и поверхностью мяча. Как насчет конструкции if в духе того, как мы сдела- ли с жуком в предыдущей главе? Здесь нам не требуется столько условий. Важно лишь со- прикосновение двух поверхностей. Нужно только прове- рить, находится ли мяч достаточно близко, чтобы пере- крыть персонажа: if (Ball.x < Figure.x+150) Я определил вертикальную ограничительную линию, кото- рую мяч должен пересечь, чтобы засчитать удар (рис. 15.8). (Ты должен поэкспериментировать со значением, которое добавляешь к переменной Figure.x.) Рис. 15.8.Невидимая вертикальная линия, достигнув которой мячом, можно засчитывать проигрыш Вот теперь в игру вступает атрибут isHit, который прини- мает значение False при создании персонажа, и True, если мяч коснулся линии ограничения, а персонаж не уклонил- ся. Получается двойная условная конструкция if: if (Ball.x < Figure.x+150) : if not Figure.dodge(Ball.y, yMax/2) : Figure.isHit = True Для того чтобы мяч остановился, необходим следующий код: Основная программа 303 if not Figure.isHit : Ball.move(-1, 0) Ball.controlRestart(xMax-50, yMax/2) Основная программа Чтобы ничего не потерялось, а именно все изменения и до- полнения в исходном коде, я вновь привожу его целиком (⇒ dodger4.py): import pygame as pg import random from dplayer import * from dthing import * # Установка начальных значений Yellow = (255,255,0) xMax, yMax = 800, 400 Start = 0 # Таймер def getTime(Reset) : global Start if Reset : Start = pg.time.get_ticks() Diff = pg.time.get_ticks() - Start return Diff # Запуск Pygame, создание игрового поля и персонажа pg.init() pg.key.set_repeat(20,20) pg.display.set_caption("Моя игра") Window = pg.display.set_mode((xMax, yMax)) Figure = Player(20,30) Ball = Thing("Bilder/ball1.png") Ball.setPosition(xMax-50, yMax/2, True) # Цикл событий 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_UP : Figure.setState(2) Глава Уклониться или проиграть 15 304 getTime(True) if event.key == pg.K_DOWN : Figure.setState(1) getTime(True) # Тестирование Time = getTime(False) if Time > 200 : Figure.setState(0) # Перемещение мяча, сброс при необходимости if not Figure.isHit : Ball.move(-1, 0) Ball.controlRestart(xMax-50, yMax/2) # Проверка, находится ли мяч в игровой зоне if (Ball.x < Figure.x+150) : # Если игрок проиграл, игра заканчивается if not Figure.dodge(Ball.y, yMax/2) : Figure.isHit = True # Позиционирование спрайта в окне Window.fill(Yellow) Window.blit(Figure.Bild, (Figure.x, Figure.y)) Window.blit(Ball.Bild, (Ball.x, Ball.y)) pg.display.update() # Завершение Pygame pg.quit() ¾ Набери исходный код и запусти программу. Если игра слишком проста для тебя, просто ускорь ее с помощью метода move. Подведение итогов Теперь у тебя есть еще одна игра. Такой проект никогда не завершится, ты всегда сможешь найти что-то, что можно улучшить. Здесь я использовал только один новый метод pygame , который ты узнал: time.get_ticks() Получение текущего времени (в миллисекундах) Нет вопросов... ...и одна задача 305 ...и одна задача 1. В папке с примерами для этой книги ( dmkpress.com ) есть файл с побитым персонажем (рис. 15.9). Убедись, что при завершении игры появляется соответствующее изображение. Рис. 15.9.Персонаж после попадания мяча 306 16 Пора развлекаться В последних нескольких главах ты запрограммировал две игры, в которые уже можешь играть. Но было бы намного интереснее, если бы ты мог набирать очки, когда убиваешь жука или уклоняешься от мяча. Вот почему мы должны за- няться этим в данной главе. Кроме того, играть с одним жуком не так сложно и интерес- но, как с целой кучей насекомых. Давай наплодим их в этой главе. Итак, в этой главе ты узнаешь: как отображать текст в окне программы; как создать новый класс (Game); как получать и набирать очки; как охотиться на более чем одного жука; как использовать таймер игрового времени. Игровой счет Чтобы отобразить текст в окне Pygame, тебе понадобится так называемая «вставка», на которой будет отображаться текст. В Pygame нужный класс называется Surface. Итак, мы создаем соответствующий объект: Игровой счет 307 Text = pg.Surface((300,50)) Text.fill(Yellow) В итоге получается не что иное, как прямоугольная область, которая на самом деле черная, но с помощью заливки мы делаем ее желтой, чтобы она (пока) не была видна на поле. Затем давай определим функцию для отображения текста (⇒ dodger5.py): def showMessage(text, Color) : global Text Font = pg.font.SysFont("arial", 48) Text = Font.render(text, True, Color) Сначала мы должны указать шрифт, лучше системный. Я выбрал шрифт Arial с размером 48. (Попробуй другие шрифты и размеры.) Теперь текст рендерится, и ты можешь его увидеть. Кроме того, ты можешь выбрать цвет текста. Рендерится? Что это? Визуализация (отображение на экране) гра- фического контента называется рендерингом. Это может быть, среди прочего, изображение или текст. Для многих объектов вы- зова blit() достаточно, чтобы сделать их видимыми, но в нашем случае текст, который должен отображаться, сначала должен быть «вставлен» в объект Surface. Таким образом, теперь мы можем отображать очки, кото- рые игрок получает, когда успешно уклоняется от мяча. Для этого нам нужна другая функция (⇒ dodger5.py): def setScore(num) : global Score Score += num showMessage("Счет: " + str(Score), Blue) Глобальной переменной Score, которая здесь используется, присваивается значение 0 в начале программы: Score = 0 Количество баллов, на которое должен увеличиться счет, принимается в качестве параметра. Затем функция show Message() используется для отображения результата синим |