Учим Python, делая крутые игры 2018. Invent your owncomputer gameswith python
Скачать 6.56 Mb.
|
349 Управление отскакиванием блока После того как код в строках 44–57 переместил блок, нужно проверить, за- шел ли он за границу окна. Если так и есть, нужно заставить блок отскочить. В контексте кода это значит, что цикл for присвоит новое значение ключу бло- ка 'dir'. Блок будет двигаться в новом направлении при следующей итерации игрового цикла. Благодаря этому кажется, что блок отскочил от границы окна. В инструкции if в строке 60 мы определяем то, что блок переместился за верхнюю границу окна, если верхний атрибут объекта Rect блока меньше 0. 59. # Проверка, переместился ли блок за пределы окна. 60. if b['rect'].top < 0: 61. # Прохождение блока через верхнюю границу. 62. if b['dir'] == UPLEFT: 63. b['dir'] = DOWNLEFT 64. if b['dir'] == UPRIGHT: 65. b['dir'] = DOWNRIGHT В таком случае направление будет изменено в зависимости от направле- ния движения блока. Если блок перемещался в направлении UPLEFT (вле- во вверх), то теперь он будет перемещаться DOWNLEFT (влево вниз); если он перемещался UPRIGHT (вправо вверх), теперь он будет перемещаться DOWNRIGHT (вправо вниз). Код в строках 66–71 занимается ситуацией, в которой блок зашел за ниж- нюю границу окна. 66. if b['rect'].bottom > WINDOWHEIGHT: 67. # Прохождение блока через нижнюю границу. 68. if b['dir'] == DOWNLEFT: 69. b['dir'] = UPLEFT 70. if b['dir'] == DOWNRIGHT: 71. b['dir'] = UPRIGHT Эти строки проверяют, является ли значение атрибута bottom (не атрибута top ) больше значения переменной WINDOWHEIGHT. Помните, что координаты y начинаются с 0 в верхней части окна и увеличиваются до значения перемен- ной WINDOWHEIGHT в самом низу. Код в строках 72–83 управляют поведением блоков, когда те отскакивают от границ. 350 Глава 18 72. if b['rect'].left < 0: 73. # Прохождение блока через левую границу. 74. if b['dir'] == DOWNLEFT: 75. b['dir'] = DOWNRIGHT 76. if b['dir'] == UPLEFT: 77. b['dir'] = UPRIGHT 78. if b['rect'].right > WINDOWWIDTH: 79. # Прохождение блока через правую границу. 80. if b['dir'] == DOWNRIGHT: 81. b['dir'] = DOWNLEFT 82. if b['dir'] == UPRIGHT: 83. b['dir'] = UPLEFT Код в строках 78–83 аналогичен коду в строках 72–77, но проверьте, за- шла ли правая сторона блока за правую границу окна. Помните, что коорди- наты x начинаются с 0 на левой границе окна и увеличиваются до значения переменной WINDOWWIDTH на правой границе. Отображение в окне блоков в новых положениях При каждом перемещении блоков мы должны отображать их в новых по- ложениях на windowSurface, вызывая функцию pygame.draw.rect(). 85. # Создание блока на поверхности. 86. pygame.draw.rect(windowSurface, b['color'], b['rect']) Вам нужно передать объект windowSurface функции, потому что прямоу- гольник отображается на объекте Surface. Передайте функции b['color'], по- скольку это цвет прямоугольника. Наконец, передайте b['rect'], так как это объект Rect, содержащий положение и размер отображаемого прямоуголь- ника. Строка 86 — последняя в цикле for. Отображение окна на экране После завершения цикла for, каждый блок в списке boxes будет обри- сован. Теперь нам нужно вызвать функцию pygame.display.update(), чтобы ото- бразить на экране windowSurface. 88. # Вывод окна на экран. 89. pygame.display.update() 90. time.sleep(0.02) Компьютер может перемещать и отображать блоки, и заставлять их от- скакивать настолько быстро, что если бы программа работала на полной ско- рости, все блоки слились бы в пятно. Чтобы замедлить выполнение програм- мы до скорости, на которой можно различать блоки, нужно добавить код time.sleep(0.02) . Вы можете закомментировать строку кода time.sleep(0.02) и запустить программу, чтобы увидеть, как она работает без нее. Вызов функ- ции time.sleep() приостанавливает программу на 0,02 с (то есть 20 мс) между каждым перемещением блоков. После этой строки интерпретатор возвращается к началу игрового цикла и снова начинает весь процесс. Таким образом, блоки постоянно перемеща- ются на небольшие расстояния, отскакивают от границ окна и отображаются на экране в новых положениях. Заключение В этой главе был представлен совершенно новый способ создания ком- пьютерных программ. Программы из предыдущих глав останавливались и дожидались, пока игрок введет текст. Однако в случае с нашей «анимационной» программой происходит постоянное обновление структуры данных без ожидания ввода от игрока. Помните, в играх «Виселица» и «Крестики-нолики» у нас были структуры данных, которые представляли состояние игрового поля. Те структуры дан- ных передавались функции drawBoard() и отображались на экране, что схо- же с программой из этой главы. Переменная boxes содержит список структур данных, представляющих блоки, которые отображаются внутри игрового цикла. Но как при отсутствии вызовов input() получить данные ввода от игрока? В главе 19 мы рассмотрим принцип, благодаря которому программы узнают, когда игрок нажимает клавиши на клавиатуре. Мы также изучим новую кон- цепцию, называемую обнаружением столкновения. 352 Глава 19 19 ОБНАРУЖЕНИЕ СТОЛКНОВЕНИЙ Обнаружение столкновений подразуме- вает вычисление момента касания (то есть столкновения) двух объектов друг с другом. Это часто может потребо- ваться в играх. Например, если игрок ка- сается врага, он может терять здоровье, а касаясь монеты, должен автоматически ее подбирать. Обнаружение столкновений помогает определить, стоит ли персонаж в игре на твердой почве, или же под ним пу- стое пространство, и ему следует упасть. В наших играх обнаружение столкновений будет использоваться для опре- деления, накладываются ли два прямоугольника друг на друга. Такой базовый метод будет охватывать пример программы из данной главы. Мы также рас- смотрим, как наши программы, использующие модуль pygame, могут прини- мать от игрока входные данные посредством клавиатуры и мыши. Это немно- го сложнее, чем вызов функции input(), который мы осуществляли в наших текстовых программах. Но использование клавиатуры является гораздо более интерактивным процессом в программах с графическим интерфейсом (GUI), а применение мыши в ранее описанных текстовых играх вообще невозможно. Эти две концепции сделают ваши игры более захватывающими! В ЭТОЙ ГЛАВЕ РАССМАТРИВАЮТСЯ СЛЕДУЮЩИЕ ТЕМЫ: • Объекты clock • Ввод с клавиатуры в pygame • Ввод мышью в pygame • Обнаружение столкновений • Перебор элементов списка без его изменения Обнаружение столкновений 353 Пример работы программы В этой программе игрок использует клавиши ←, ↑, ↓ и →наклавиату- ре, чтобы перемещать по экрану черный блок. На экране также появляют- ся маленькие зеленые блоки, представляющие собой «еду», которые блок «съедает», когда касается их. Чтобы создать новые блоки «еды», игрок может щелкнуть мышью в любом месте окна. Кроме того, клавиша Esc завершает работу программы, а клавиша X перемещает игрока в случайное место на экране. На рис. 19.1 показано, как будет выглядеть запущенная программа. Рис. 19.1. Снимок pygame-программы с функцией обнаружения столкновений Исходный код программы В редакторе файлов создайте новый файл, вы- брав команду меню File ⇒ New File (Файл ⇒ Новый файл). В открывшемся окне введите приведенный ниже исходный код и сохраните файл под именем collisionDetection.py. Затем нажмите клавишу F5 и запустите программу. Если при выполнении про- граммы возникают ошибки, сравните код, который вы набрали, с оригинальным кодом с помощью онлайн-инструмента на сайте inventwithpython. com/diff/. Make sure you’re using Python 3, not Python 2! УБЕ ДИТЕСЬ, ЧТО ИСПО ЛЬЗУЕТЕ PY THON 3, А НЕ PY THON 2! 354 Глава 19 collisionDetection.py 1. import pygame, sys, random 2. from pygame.locals import * 3. 4. # Установка pygame. 5. pygame.init() 6. mainClock = pygame.time.Clock() 7. 8. # Настройка окна. 9. WINDOWWIDTH = 400 10. WINDOWHEIGHT = 400 11. windowSurface = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT), 0, 32) 12. pygame.display.set_caption('Обнаружение столкновений') 13. 14. # Настройка цветов. 15. BLACK = (0, 0, 0) 16. GREEN = (0, 255, 0) 17. WHITE = (255, 255, 255) 18. 19. # Создание структур данных игрока и "еды". 20. foodCounter = 0 21. NEWFOOD = 40 22. FOODSIZE = 20 23. player = pygame.Rect(300, 100, 50, 50) 24. foods = [] 25. for i in range(20): 26. foods.append(pygame.Rect(random.randint(0, WINDOWWIDTH - FOODSIZE), random.randint(0, WINDOWHEIGHT - FOODSIZE), FOODSIZE, FOODSIZE)) 27. 28. # Создание переменных перемещения. 29. moveLeft = False 30. moveRight = False 31. moveUp = False 32. moveDown = False 33. 34. MOVESPEED = 6 35. 36. 37. # Запуск игрового цикла. 38. while True: 39. # Проверка событий. Обнаружение столкновений 355 40. for event in pygame.event.get(): 41. if event.type == QUIT: 42. pygame.quit() 43. sys.exit() 44. if event.type == KEYDOWN: 45. # Изменение переменных клавиатуры. 46. if event.key == K_LEFT or event.key == K_a: 47. moveRight = False 48. moveLeft = True 49. if event.key == K_RIGHT or event.key == K_d: 50. moveLeft = False 51. moveRight = True 52. if event.key == K_UP or event.key == K_w: 53. moveDown = False 54. moveUp = True 55. if event.key == K_DOWN or event.key == K_s: 56. moveUp = False 57. moveDown = True 58. if event.type == KEYUP: 59. if event.key == K_ESCAPE: 60. pygame.quit() 61. sys.exit() 62. if event.key == K_LEFT or event.key == K_a: 63. moveLeft = False 64. if event.key == K_RIGHT or event.key == K_d: 65. moveRight = False 66. if event.key == K_UP or event.key == K_w: 67. moveUp = False 68. if event.key == K_DOWN or event.key == K_s: 69. moveDown = False 70. if event.key == K_x: 71. player.top = random.randint(0, WINDOWHEIGHT - player.height) 72. player.left = random.randint(0, WINDOWWIDTH - player.width) 73. 74. if event.type == MOUSEBUTTONUP: 75. foods.append(pygame.Rect(event.pos[0], event.pos[1], FOODSIZE, FOODSIZE)) 76. 77. foodCounter += 1 78. if foodCounter >= NEWFOOD: 79. # Добавление новой "еды". 80. foodCounter = 0 356 Глава 19 81. foods.append(pygame.Rect(random.randint(0, WINDOWWIDTH - FOODSIZE), random.randint(0, WINDOWHEIGHT - FOODSIZE), FOODSIZE, FOODSIZE)) 82. 83. # Создание на поверхности белого фона. 84. windowSurface.fill(WHITE) 85. 86. # Перемещение игрока. 87. if moveDown and player.bottom < WINDOWHEIGHT: 88. player.top += MOVESPEED 89. if moveUp and player.top > 0: 90. player.top -= MOVESPEED 91. if moveLeft and player.left > 0: 92. player.left -= MOVESPEED 93. if moveRight and player.right < WINDOWWIDTH: 94. player.right += MOVESPEED 95. 96. # Отображение игрока на поверхности. 97. pygame.draw.rect(windowSurface, BLACK, player) 98. 99. # Проверка, не пересекся ли игрок с какими-либо блоками "еды". 100. for food in foods[:]: 101. if player.colliderect(food): 102. foods.remove(food) 103. 104. # Отображение "еды". 105. for i in range(len(foods)): 106. pygame.draw.rect(windowSurface, GREEN, foods[i]) 107. 108. # Вывод окна на экран. 109. pygame.display.update() 110. mainClock.tick(40 ) Импорт модулей Pygame -программа с функцией обнаружения столкновений импортирует те же модули, что и программа из главы 18, а также модуль random. 1. import pygame, sys, random 2. from pygame.locals import * Обнаружение столкновений 357 Использование объекта Clock для управления скоростью работы программы Инструкции в строках 5–17 в основном выполняют те же действия, что и в программе из главы 18: они инициализируют pygame, устанавливают пе- ременные WINDOWHEIGHT и WINDOWWIDTH и назначают константы цвета и направ- ления. Однако код в строке 6 вам незнаком. 6. mainClock = pygame.time.Clock() В программе из главы 18 вызов функции time.sleep(0.02) замедлял работу программы, чтобы она не выполнялась слишком быстро. В то время как этот вызов приостанавит работу на 0,02 секунды на всех компьютерах, скорость выполнения остальной части программы зависит от скорости работы самого компьютера. Если мы хотим, чтобы наша программа работала на любом ком- пьютере с одинаковой скоростью, нам понадобится функция, делающая на быстрых компьютерах более продолжительную паузу, чем на медленных. Объект pygame.time.Clock может приостановить работу на необходимое количество времени на любом компьютере. Код в строке 110 вызывает функ- цию mainClock.tick(40) внутри игрового цикла. Этот вызов метода tick() объ- екта Clock ждет достаточно времени, чтобы цикл выполнялся со скоростью около 40 итераций в секунду, независимо от скорости компьютера, вслед- ствие чего игра никогда не будет работать быстрее, чем вы ожидаете. Вызов tick() должен быть осуществлен в игровом цикле только один раз. Настройка окна и структур данных Код в строках 19 — 22 создают несколько переменных для блоков «еды», появляющихся на экране. 19. # Создание структур данных игрока и "еды". 20. foodCounter = 0 21. NEWFOOD = 40 22. FOODSIZE = 20 Переменная foodCounter начнет со значения 0, NEWFOOD — с 40, а переменная FOODSIZE — со значения 20. Мы увидим, как они будут использоваться позже, когда создадим «еду». 358 Глава 19 Код в строке 23 создает объект pygame.Rect для обозначения местополо- жения игрока. 23. player = pygame.Rect(300, 100, 50, 50) Переменная player содержит объект pygame.Rect, который представляет размер и положение блока. Блок игрока будет перемещаться так же, как бло- ки в программе из главы 18 (см. раздел «Перемещение каждого блока» в гла- ве 18), но в этой программе игрок сможет управлять его перемещением. Затем мы создали код для отслеживания блоков «еды». 24. foods = [] 25. for i in range(20): 26. foods.append(pygame.Rect(random.randint(0, WINDOWWIDTH - FOODSIZE), random.randint(0, WINDOWHEIGHT - FOODSIZE), FOODSIZE, FOODSIZE)) Программа будет отслеживать каждый блок «еды» с помощью списка объектов Rect в foods. Код в строках 25 и 26 создает 20 блоков «еды», случайно разбросанных по экрану. Можно использовать функцию random.randint() для генерации случайных координат x и y. (0, 0) (400, 0) (0, 400) (400, 400) (300, 200) (400, 200) Рис. 19.2. Установление значения 400 координаты верхнего левого угла блока размером 100×100 в окне размером 400×400 поместит блок за пределы окна. Чтобы этого не произошло, координата левого угла должна равняться 300 В строке 26 программа вызывает функцию-конструктор pygame.Rect() , чтобы вернуть новый объект pygame.Rect . Он будет представлять положение и размер нового блока «еды». Первые два параметра pygame.Rect() — это ко- Обнаружение столкновений 359 ординаты x и y верхнего левого угла. Нужно, чтобы случайная координата находилась между 0 и размером окна минус размер блока «еды». Если вы уста- новите значение случайной координаты между 0 и размером окна, то блок «еды» может быть целиком вытолкнут за пределы окна, как на рис. 19.2. Третий и четвертый параметры для pygame.Rect() — ширина и высота бло- ка «еды». Ширина и высота являются значениями в константе FOODSIZE. Третий и четвертый параметры pygame.Rect() — ширина и высота блока «еды». Ширина и высота являются значениями в константе FOODSIZE. Создание переменных для отслеживания перемещения Начиная со строки 29, код создает определенные переменные, которые отслеживают движение блока игрока для каждого направления, в котором он может перемещаться. 28. # Создание переменных перемещения. 29. moveLeft = False 30. moveRight = False 31. moveUp = False 32. moveDown = False Четыре переменные имеют логические значения, изначально равные False , для отслеживания того, какая клавиша со стрелкой была нажата. На- пример, когда игрок для перемещения блока нажимает клавишу ←, перемен- ная moveLeft принимает значение True. Когда он отпускает эту клавишу, пере- менная moveLeft возвращается к значению False. Код в строках 34–43 почти идентичен коду в предыдущих pygame- программах. Эти строки обрабатывают начало игрового цикла и ситуацию, когда игрок выходит из программы. Я пропущу объяснение данного кода, так как делал это в предыдущей главе. Обработка событий Модуль pygame способен генерировать события в ответ на пользователь- ский ввод с клавиатуры или мыши . Ниже перечислены события, которые мо- гут быть возвращены pygame.event.get(): • QUIT . Генерируется, когда игрок закрывает окно. • KEYDOWN . Генерируется, когда игрок нажимает клавишу. Имеет атрибут key , сообщающий, какая клавиша была нажата. Также имеет атрибут 360 Глава 19 mod , который сообщает, удерживалась ли клавиша Shift, Ctrl, Alt или какая-либо другая во время нажатия игровой клавиши. • KEYUP . Генерируется, когда игрок отпускает клавишу. Имеет атрибуты key и mod, аналогичные тем, которые есть у KEYDOWN. • MOUSEMOTION . Генерируется каждый раз, когда курсор мыши переме- щается по окну. Имеет атрибут pos (сокращение от position — поло- жение), который возвращает кортеж (x, y) для координат позиции в окне, где находится курсор. Атрибут rel также возвращает кортеж (x, y), но он возвращает координаты относительно последнего собы- тия MOUSEMOTION. Например, если мышь перемещается налево на 4 пик- селя с (200, 200) до (196, 200), тогда rel вернет кортеж (-4, 0). Атрибут button возвращает кортеж из трех целых чисел. Первое целое число в кортеже представляет левую кнопку мыши, второе — среднюю (если таковая существует), а третье — правую кнопку. Эти целые числа бу- дут равны 0, если при движении мыши кнопка не нажата, и 1, если кнопка нажата. • |