Учим Python, делая крутые игры 2018. Invent your owncomputer gameswith python
Скачать 6.56 Mb.
|
pygame.time.Clock Объект Clock в модуле pygame.time пригодится для того, чтобы игры работали на нормальной скорости, а не на очень высокой. Объект Clock имеет метод tick(), которому может быть передано значение количества кадров в секунду (FPS, frames per second), с которым тре- буется запустить игру. Чем выше значение FPS, тем быстрее работает игра. Игра «Ловкач» с графикой и звуком 383 Пример запуска игры «Ловкач» Когда вы запустите эту программу, окно будет выглядеть как показано на рис. 21.1. Рис. 21.1. Снимок игры «Ловкач» Исходный код игры «Ловкач» В редакторе файлов создайте новый файл, выбрав команду меню File ⇒ New File (Файл ⇒ Новый файл). В открывшемся окне введите приведенный ниже ис- ходный код и сохраните файл под именем dodger.py. Поместите файлы изображений и звуков в папку с файлом dodger.py. Затем нажмите клавишу F5 и за- пустите программу. Если при выполнении програм- мы возникают ошибки, сравните код, который вы набрали, с оригинальным кодом с помощью онлайн- инструмента на сайте inventwithpython.com/diff/. Make sure you’re using Python 3, not Python 2! УБЕ ДИТЕСЬ, ЧТО ИСПО ЛЬЗУЕТЕ PY THON 3, А НЕ PY THON 2! 384 Глава 21 dodger.py 1. import pygame, random, sys 2. from pygame.locals import * 3. 4. WINDOWWIDTH = 600 5. WINDOWHEIGHT = 600 6. TEXTCOLOR = (0, 0, 0) 7. BACKGROUNDCOLOR = (255, 255, 255) 8. FPS = 60 9. BADDIEMINSIZE = 10 10. BADDIEMAXSIZE = 40 11. BADDIEMINSPEED = 1 12. BADDIEMAXSPEED = 8 13. ADDNEWBADDIERATE = 6 14. PLAYERMOVERATE = 5 15. 16. def terminate(): 17. pygame.quit() 18. sys.exit() 19. 20. def waitForPlayerToPressKey(): 21. while True: 22. for event in pygame.event.get(): 23. if event.type == QUIT: 24. terminate() 25. if event.type == KEYDOWN: 26. if event.key == K_ESCAPE: # Нажатие ESC осуществляет выход. 27. terminate() 28. return 29. 30. def playerHasHitBaddie(playerRect, baddies): 31. for b in baddies: 32. if playerRect.colliderect(b['rect']): 33. return True 34. return False 35. 36. def drawText(text, font, surface, x, y): 37. textobj = font.render(text, 1, TEXTCOLOR) 38. textrect = textobj.get_rect() 39. textrect.topleft = (x, y) 40. surface.blit(textobj, textrect) Игра «Ловкач» с графикой и звуком 385 41. 42. # Настройка pygame, окна и указателя мыши. 43. pygame.init() 44. mainClock = pygame.time.Clock() 45. windowSurface = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT)) 46. pygame.display.set_caption('Ловкач') 47. pygame.mouse.set_visible(False) 48. 49. # Настройка шрифтов. 50. font = pygame.font.SysFont(None, 35) 51. 52. # Настройка звуков. 53. gameOverSound = pygame.mixer.Sound('gameover.wav') 54. pygame.mixer.music.load('background.mid') 55. 56. # Настройка изображений. 57. playerImage = pygame.image.load('player.png') 58. playerRect = playerImage.get_rect() 59. baddieImage = pygame.image.load('baddie.png') 60. 61. # Вывод начального экрана. 62. windowSurface.fill(BACKGROUNDCOLOR) 63. drawText('Ловкач', font, windowSurface, (WINDOWWIDTH / 3), (WINDOWHEIGHT / 3)) 64. drawText('Нажмите клавишу для начала игры', font, windowSurface, (WINDOWWIDTH / 5) - 30, (WINDOWHEIGHT / 3) + 50) 65. pygame.display.update() 66. waitForPlayerToPressKey() 67. 68. topScore = 0 69. while True: 70. # Настройка начала игры. 71. baddies = [] 72. score = 0 73. playerRect.topleft = (WINDOWWIDTH / 2, WINDOWHEIGHT - 50) 74. moveLeft = moveRight = moveUp = moveDown = False 75. reverseCheat = slowCheat = False 76. baddieAddCounter = 0 77. pygame.mixer.music.play(-1, 0.0) 78. 79. while True: # Игровой цикл выполняется, пока игра работает. 80. score += 1 # Увеличение количества очков. 81. 82. for event in pygame.event.get(): 386 Глава 21 83. if event.type == QUIT: 84. terminate() 85. 86. if event.type == KEYDOWN: 87. if event.key == K_z: 88. reverseCheat = True 89. if event.key == K_x: 90. slowCheat = True 91. if event.key == K_LEFT or event.key == K_a: 92. moveRight = False 93. moveLeft = True 94. if event.key == K_RIGHT or event.key == K_d: 95. moveLeft = False 96. moveRight = True 97. if event.key == K_UP or event.key == K_w: 98. moveDown = False 99. moveUp = True 100. if event.key == K_DOWN or event.key == K_s: 101. moveUp = False 102. moveDown = True 103. 104. if event.type == KEYUP: 105. if event.key == K_z: 106. reverseCheat = False 107. score = 0 108. if event.key == K_x: 109. slowCheat = False 110. score = 0 111. if event.key == K_ESCAPE: 112. terminate() 113. 114. if event.key == K_LEFT or event.key == K_a: 115. moveLeft = False 116. if event.key == K_RIGHT or event.key == K_d: 117. moveRight = False 118. if event.key == K_UP or event.key == K_w: 119. moveUp = False 120. if event.key == K_DOWN or event.key == K_s: 121. moveDown = False 122. 123. if event.type == MOUSEMOTION: 124. # Если мышь движется, переместить игрока к указателю мыши. Игра «Ловкач» с графикой и звуком 387 125. playerRect.centerx = event.pos[0] 126. playerRect.centery = event.pos[1] 127. # Если необходимо, добавить новых злодеев в верхнюю часть экрана. 128. if not reverseCheat and not slowCheat: 129. baddieAddCounter += 1 130. if baddieAddCounter == ADDNEWBADDIERATE: 131. baddieAddCounter = 0 132. baddieSize = random.randint(BADDIEMINSIZE, BADDIEMAXSIZE) 133. newBaddie = {'rect': pygame.Rect(random.randint(0, WINDOWWIDTH - baddieSize), 0 - baddieSize, baddieSize, baddieSize), 134. 'speed': random.randint(BADDIEMINSPEED, BADDIEMAXSPEED), 135. 'surface':pygame.transform.scale(baddieImage, (baddieSize, baddieSize)), 136. } 137. 138. baddies.append(newBaddie) 139. 140. # Перемещение игрока по экрану. 141. if moveLeft and playerRect.left > 0: 142. playerRect.move_ip(-1 * PLAYERMOVERATE, 0) 143. if moveRight and playerRect.right < WINDOWWIDTH: 144. playerRect.move_ip(PLAYERMOVERATE, 0) 145. if moveUp and playerRect.top > 0: 146. playerRect.move_ip(0, -1 * PLAYERMOVERATE) 147. if moveDown and playerRect.bottom < WINDOWHEIGHT: 148. playerRect.move_ip(0, PLAYERMOVERATE) 149. 150. # Перемещение злодеев вниз. 151. for b in baddies: 152. if not reverseCheat and not slowCheat: 153. b['rect'].move_ip(0, b['speed']) 154. elif reverseCheat: 155. b['rect'].move_ip(0, -5) 156. elif slowCheat: 157. b['rect'].move_ip(0, 1) 158. 159. # Удаление злодеев, упавших за нижнюю границу экрана. 160. for b in baddies[:]: 161. if b['rect'].top > WINDOWHEIGHT: 162. baddies.remove(b) 163. 164. # Отображение в окне игрового мира. 165. windowSurface.fill(BACKGROUNDCOLOR) 166. 388 Глава 21 167. # Отображение количества очков и лучшего результата. 168. drawText('Счет: %s' % (score), font, windowSurface, 10, 0) 169. drawText('Рекорд: %s' % (topScore), font, windowSurface, 10, 40) 170. 171. # Отображение прямоугольника игрока. 172. windowSurface.blit(playerImage, playerRect) 173. 174. # Отображение каждого злодея. 175. for b in baddies: 176. windowSurface.blit(b['surface'], b['rect']) 177. 178. pygame.display.update() 179. 180. # Проверка, попал ли в игрока какой-либо из злодеев. 181. if playerHasHitBaddie(playerRect, baddies): 182. if score > topScore: 183. topScore = score # установка нового рекорда 184. break 185. 186. mainClock.tick(FPS) 187. 188. # Отображение игры и вывод надписи 'Игра окончена'. 189. pygame.mixer.music.stop() 190. gameOverSound.play() 191. 192. drawText('ИГРА ОКОНЧЕНА!', font, windowSurface, (WINDOWWIDTH / 3), (WINDOWHEIGHT / 3)) 193. drawText('Нажмите клавишу для начала новой игры', font, windowSurface, (WINDOWWIDTH / 3) - 120, (WINDOWHEIGHT / 3) + 50) 194. pygame.display.update() 195. waitForPlayerToPressKey() 196. 197. gameOverSound.stop() Импорт модулей Игра «Ловкач» импортирует те же модули, что и предыдущие pygame- программы — pygame, random, sys и pygame.locals. 1. import pygame, random, sys 2. from pygame.locals import * Игра «Ловкач» с графикой и звуком 389 Модуль pygame.locals содержит несколько констант, используемых pygame, таких как типы событий (QUIT, KEYDOWN и так далее) и клавиши клавиатуры (K_ESCAPE, K_LEFT и так далее). Используя синтаксис from pygame.locals import *, вы можете использовать в исходном коде просто QUIT вместо pygame.locals.QUIT. Создание констант Код в строках 4–7 устанавливают константы для размеров окна, цвета текста и цвета фона. 4. WINDOWWIDTH = 600 5. WINDOWHEIGHT = 600 6. TEXTCOLOR = (0, 0, 0) 7. BACKGROUNDCOLOR = (255, 255, 255) Мы используем константы, потому что это гораздо более наглядно, чем если бы мы набирали значения вручную. Например, строка кода windowSurface. fill(BACKGROUNDCOLOR) более понятна, чем windowSurface.fill((255, 255, 255)). Также это позволит вам легко модифицировать игру. Изменив значения переменной WINDOWWIDTH в строке 4, вы автоматически меняете код везде, где используется переменная WINDOWWIDTH. Если бы вместо этого вы использовали значение 600, вам пришлось бы изменить каждое вхождение 600 в коде. Про- ще один раз изменить значение константы. В строке 8 вы создаете константу для FPS, количества кадров в секунду, необходимого для запуска игры. 8. FPS = 60 Кадр — это экран, отображаемый при одном переборе игрового цикла. В строке 186 вы передаете значение переменной FPS методу mainClock.tick(), чтобы функция знала, на какое количество времени приостанавливать про- грамму. Здесь переменной FPS присвоено значение 60, но вы можете изменить значение переменной FPS в большую сторону, чтобы игра работала быстрее, или в меньшую, чтобы замедлить ее. Код в строках 9–13 создают еще несколько констант для падающих спрай- тов злодеев. 9. BADDIEMINSIZE = 10 10. BADDIEMAXSIZE = 40 11. BADDIEMINSPEED = 1 390 Глава 21 12. BADDIEMAXSPEED = 8 13. ADDNEWBADDIERATE = 6 Значения ширины и высоты спрайтов злодеев будут находиться в диапа- зоне между BADDIEMINSIZE и BADDIEMAXSIZE. Скорость, с которой спрайты зло- деев падают на экране, будет находиться в диапазоне между BADDIEMINSPEED и BADDIEMAXSPEED пикселей за одну итерацию игрового цикла. А новый спрайт злодея будет добавляться в верхней части окна каждые ADDNEWBADDIERATE ите- раций игрового цикла. Наконец, константа PLAYERMOVERATE хранит количество пикселей, на кото- рое спрайт игрока перемещается в окне при каждой итерации игрового цик- ла (если перемещается). 14. PLAYERMOVERATE = 5 Увеличивая это число, вы увеличиваете скорость перемещения персо нажа. Определение функций Для этой игры вы создадите несколько функций. Функции terminate() и waitForPlayerToPressKey() будут, соответственно, завершать и приостанав- ливать игру, функция playerHasHitBaddie() будет отслеживать столкновения спрайта игрока со спрайтами злодеев, а функция drawText() будет отображать на экране счет и другой текст. Завершение игры и добавление паузы Модуль pygame требует, чтобы для завершения игры вы вызывали как функцию pygame.quit(), так и sys.exit(). Строки 16–18 помещают их обеих в функцию terminate(). 16. def terminate(): 17. pygame.quit() 18. sys.exit() Теперь, вместо того, чтобы вызывать и pygame.quit(), и sys.exit(), вам нужно вызвать только функцию terminate(). В некоторых случаях вам понадобится приостановить выполнение про- граммы до тех пор, пока игрок не нажмет клавишу, — например, в самом начале игры, когда появится заголовок «Ловкач», или в конце, когда отобра- Игра «Ловкач» с графикой и звуком 391 жается надпись «Игра окончена». В строках 20–24 создается новая функция waitForPlayerToPressKey() 20. def waitForPlayerToPressKey(): 21. while True: 22. for event in pygame.event.get(): 23. if event.type == QUIT: 24. terminate() Внутри этой функции используется бесконечный цикл, который преры- вается только при получении события KEYDOWN или QUIT. В начале цикла pygame. event.get() возвращает список объектов Event для проверки. Если игрок закрыл окно во время ожидания программой, когда он на- жмет клавишу, pygame сгенерирует событие QUIT, наличие которого вы про- веряете в строке 23 с помощью event.type. Если игрок завершил игру, Python вызывает функцию terminate() в строке 24. Если игра получает событие KEYDOWN, сначала нужно проверить, была ли нажата клавиша Esc. 25. if event.type == KEYDOWN: 26. if event.key == K_ESCAPE: # Нажатие ESC осуществляет выход. 27. terminate() 28. return Если игрок нажал клавишу Esc, программа должна завершиться. Если дело было не в этом, то интерпретатор пропустит блок if в строке 27 и пе- рейдет прямо к инструкции return, что приведет к выходу из функции waitForPlayerToPressKey() Если события QUIT или KEYDOWN не генерируются, код продолжает цикл. Поскольку цикл ничего не выполняет, все будет выглядеть так, будто игра замерла, пока игрок не нажмет клавишу. Отслеживание столкновений со злодеями Функция playerHasHitBaddie() вернет True, если спрайт игрока столкнулся со спрайтом одного из злодеев. 30. def playerHasHitBaddie(playerRect, baddies): 31. for b in baddies: 32. if playerRect.colliderect(b['rect']): 392 Глава 21 33. return True 34. return False Параметр baddies представляет собой список структур данных словаря злодея. Каждый из этих словарей имеет ключ 'rect', а значение для этого клю- ча — объект Rect, который представляет размер и местоположение злодея. playerRect также является объектом Rect. Объекты Rect имеют метод с именем colliderect() , который возвращает True, если объект Rect столкнулся с тем объектом Rect, который был передан этому методу. В противном случае colliderect() возвращает False. Цикл for в строке 31 выполняет перебор каждого словаря злодея в списке baddies . Если любой из этих злодеев сталкивается с персонажем игрока, то playerHasHitBaddie() возвращает True. Если код перебрал всех злодеев в списке baddies , не обнаружив столкновения, playerHasHitBaddie() возвращает False. Отображение текста в окне Отображение текста в окне включает в себя несколько шагов, которые мы выполним с помощью функции drawText(). Таким образом, есть только одна функция, которую мы вызываем, когда хотим отобразить на экране количе- ство очков игрока или надпись 'Игра окончена'. 36. def drawText(text, font, surface, x, y): 37. textobj = font.render(text, 1, TEXTCOLOR) 38. textrect = textobj.get_rect() 39. textrect.topleft = (x, y) 40. surface.blit(textobj, textrect) Сначала вызов метода render() в строке 37 создает объект Surface, кото- рый форматирует текст определенным шрифтом. Затем вам нужно узнать размер и местоположение объекта Surface. Можно получить объект Rect с этой информацией, используя метод Surface get_rect(). Объект Rect, возвращаемый из функции get_rect() в строке 38, содержит копию информации о ширине и высоте объекта Surface. Код в строке 39 из- меняет местоположение объекта Rect, устанавливая новый кортеж для его атрибута topleft. Наконец, код в строке 40 отображает объект Surface с отформатирован- ным текстом на объекте Surface, который был передан функции drawText(). Отображение текста в pygame занимает на несколько шагов больше, чем про- Игра «Ловкач» с графикой и звуком 393 сто при вызове функции print(). Но если вы поместите этот код в единую функцию с именем drawText(), тогда для отображения текста на экране вам нужно будет вызвать лишь эту функцию. Инициализация pygame и настройка окна Теперь, когда с константами и функциями покончено, начнем вызывать функции pygame, которые настраивают окно и скорость работы. 42. # Настройка pygame, окна и указателя мыши. 43. pygame.init() 44. mainClock = pygame.time.Clock() Код в строке 43 устанавливает модуль pygame, вызывая функцию pygame. init() . Код в строке 44 создает объект pygame.time.Clock() и сохраняет его в переменной mainClock. Этот объект поможет нам обеспечить выполнение программы с надлежащей скоростью. Код в строке 45 создает новый объект Surface, который используется для отображения окна. 45. windowSurface = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT)) Обратите внимание, что функции pygame.display.set_mode() передается только один аргумент — кортеж. Аргументы pygame.display.set_mode() — не два целых числа, а один кортеж из двух целых чисел. Вы можете указать ши- рину и высоту этого объекта Surface (и окна), передав кортеж с константами WINDOWWIDTH и WINDOWHEIGHT. Функция pygame.display.set_mode() имеет второй необязательный пара- метр. Вы можете передать константу pygame.FULLSCREEN, чтобы окно заполнило весь экран. Взгляните на строку 45 с данной модификацией. 45. windowSurface = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT), pygame.FULLSCREEN) Для обозначения ширины и высоты окна по-прежнему передаются па- раметры WINDOWWIDTH и WINDOWHEIGHT, но изображение будет растянуто больше, чтобы соответствовать размеру экрана. Попробуйте запустить программу в полноэкранном режиме и в оконном. 394 Глава 21 Код в строке 46 создает заголовок окна со строкой 'Ловкач': 46. pygame.display.set_caption('Ловкач') Эта надпись отображается в строке заголовка в верхней части окна. В игре «Ловкач» указатель мыши должен быть невиден. Вам нужно, чтобы мышь перемещала персонажа игрока по экрану, но при этом указатель мыши помешал бы изображению персонажа. Мы можем сделать мышь невидимой с помощью всего одной строки кода. 47. pygame.mouse.set_visible(False) Вызов pygame.mouse.set_visible(False) сообщает pygame, что указатель мыши нужно сделать невидимым. |