Журнал LinuxFormat - перейти на главную

LXF103:Игра с мячом

Материал из Linuxformat
(Различия между версиями)
Перейти к: навигация, поиск
(Новая: {{Цикл/Программирование}} == Кодируем: игра с мячом! == : ''ЧАСТЬ 3 На последнем уроке этой серии, '''Майк Сон...)
 

Текущая версия на 20:07, 21 марта 2009

Программирование

Содержание

[править] Кодируем: игра с мячом!

ЧАСТЬ 3 На последнем уроке этой серии, Майк Сондерс займется кодированием простой, но захватывающей игры...

За последние два урока мы неплохо набили руку, создав IRC-бота и программу всплывающих карточек – вещи довольно серьезные; давайте же в последнем проекте развлечемся игрой. Создание большинства современных игр требует тысячи человеко-часов, не считая армии художников и музыкантов, но все еще есть область, где хакеры-одиночки могут написать что-то забавное. В конце концов, для создания Тетриса не потребовалась команда из 500 кодеров и бюджет голливудского фильма – Алексей Пажитнов вполне обошелся своими силами (конечно, пока подлые акулы капитализма с запада не подхватили его идею...). Как и для проекта прошлого месяца, в качестве основы нашего проекта используем Python и PyGame. Кстати, уже имеется три реализации Тетриса на базе PyGame, см. http://www.pygame.org/tags/tetris.

Если это первый номер LXF, который вы взяли в руки, и до этого вы ни строчки не написали на Python, вы будете приятно удивлены, насколько прост он в понимании: код Python знаменит в мире программирования своей самодокументированностью. А если вы знакомы с другим языком программирования, типа C или PHP, вы восхититесь простотой Python. Например, блоки кода выделяются отступами, а не фигурными скобками – взгляните сюда:

def saystuff(somestring):
 print "String passed: ", somestring
saystuff("Wowzers")

Если вы новичок в Python, то убедитесь, что он у вас установлен (большинство дистрибутивов инсталлируют его по умолчанию, но если это не ваш случай, то он доступен в вашем менеджере пакетов). Введите указанный выше код в текстовом редакторе и сохраните в вашем домашнем каталоге как test.py. Затем откройте терминал и наберите:

python test.py

Если все в порядке, то Python интерпретирует код и выдаст строку текста. В данном примере просто определяется подпрограмма с именем saystuff – она выводит любую строку текста, которая ей передается. Вы можете видеть, что код подпрограмы имеет отступ на одну табуляцию. Выполнение начинается с первого вызова saystuff, приводящего к печати строки Wowzers. Вот так все просто; вы практически готовы к кодированию.

Еще один момент: для данного урока вам понадобятся модули PyGame. PyGame – это дополнительный слой, связывающий SDL и Python и позволяющий отображать картинки и использовать звуковые эффекты в ваших программах. Он широко распространен и скорее всего доступен в репозиториях вашего дистрибутива; в противном случае обратитесь к разделу Разработка нашего DVD. (Если вы выполнили урок проекта прошлого месяца, то PyGame у вас уже установлен!)

[править] Скачки по кругу

Скорая помощь

Есть проблемы с проектом PyGame? Отслеживайте значения переменных, просто выводя их в терминал с помощью простого оператора print. Например, если в нашей игре с перемещением мяча что-то пошло наперекосяк, вы сможете легко выяснить причину, выводя значения переменных xmove или ymove – разместите в основном цикле игры print имя_переменной, и сможете следить за изменениями во время работы программы в терминале.

Хотя жанр игр весьма разнообразен, основы механики большинства из них, включающих передвижение спрайтов (изображений объектов), укладываются в следующее описание:

  1. Настраиваем экран, графику, счетчик очков и т.д.
  2. Запускаем цикл до момента смерти/выхода игрока.
  3. Отрисовываем графические объекты на экране.
  4. Получаем ввод пользователя (например от мыши или с клавиатуры).
  5. Следуем логике игры (например, ударил игрок врага?).
  6. Соответственно обновляем графику.
  7. Возвращаемся к шагу 3.

Мы напишем небольшую игру, где будет несколько мячей, прыгающих по экрану, а задача игрока – постараться избегать столкновения указателя мыши с мячами. Звучит просто? Ну, если мы введем некоторую случайность в движение мячей – то есть они не всегда будут двигаться с одинаковой скоростью – то все мигом осложнится. Вы, например, не сможете просто держать указатель мыши в нижнем левом углу экрана, потому что мяч может упасть туда в любой момент. Счетчик будет отслеживать, сколько секунд вы продержались. Это очень простая концепция, но она требует немалой ловкости с мышью и буквально лазерной фокусировки на экране.

Но все по порядку: давайте подумаем, как заставить один мяч скакать по экрану. Как мяч узнает, что пора менять направление? К счастью, для этого имеется очень простой метод: заведем две переменные, которые используем для изменения движения мяча. Для каждого прохода цикла игры будем добавлять их значения к позиции мяча. Если мяч, движется, например, вправо – это потому, что мы на каждом шаге цикла добавляем 1 к его горизонтальной координате. Если мяч ударяется о правый край экрана, то мы начинаем прибавлять к его горизонтальной координате -1, и он пойдет влево.

Представили? Если вы не понимаете, как это работает, вот вам программа на Python, демонстрирующая это в действии. Вы можете найти этот код в разделе DVD Журнал/CodeProject в файле ball1. py. Для запуска программы, кроме кода, нужно еще изображение с именем ball.png – это картинка размером 32х32 пикселя: закрашенный белый круг на черном фоне. На DVD оно есть, но вы можете нарисовать его за пару секунд в GIMP – создайте новое изображение 32х32 пикселя, залейте его черным, вырежьте кружок инструментом выделения окружности и залейте его белым. Сохраните файл как ball. png в том же каталоге, что и ball1.py, а затем запустите программу, набрав python ball1.py.

from pygame import * # Подключаем функционал PyGame!
ballpic = image.load(‘ball.png)
done = False
ballx = 0 # переменные позиции мяча
bally = 0
ballxmove = 1
ballymove = 1
init() # Запуск PyGame
screen = display.set_mode((640, 480)) # Получаем прекрасное окно
display.set_caption(‘Ball game’) # И устанавливаем его заголовок
while done == False:
screen.fill(0) # Заполняем экран черным (цвет 0)
screen.blit(ballpic, (ballx, bally)) # Рисуем мяч
display.update()
time.delay(1) # Задержка!
ballx = ballx + ballxmove # Обновляем позицию мяча
bally = bally + ballymove
if ballx > 600: # Мяч достиг границ экрана?
ballxmove = -1
if ballx < 0:
ballxmove = 1
if bally > 440:
ballymove = -1
if bally < 0:
ballymove = 1
for e in event.get(): # Проверяем нажат ли ESC
if e.type == KEYUP:
if e.key == K_ESCAPE:
done = True

Пройдемся по шагам. В первой строке мы сообщаем Python, что хотим использовать подпрограммы из библиотеки PyGame. Затем загружаем созданное нами изображение мяча, сохраняем его в объекте с именем ballpic и создаем логическую [true/false] переменную для определения завершения игры.

Следующие четыре строки очень важны: в них описываются переменные, управляющие позицией и перемещением мяча. ballx и bally хранят положение (в пикселях) мяча в нашем игровом окне: 0,0 означает верхний левый, а 640,480 – правый нижний пиксель. ballxmove и ballymove хранят числа, добавляемые к позиции мяча на каждом шаге; в начале мы устанавливаем в них 1, и когда начинается игра, 1 добавляется к ballx и bally на каждом шаге цикла, тем самым перемещая мяч направо вниз. Итак, при запуске программы наш мяч находится слева вверху и начинает двигаться по диагонали вправо вниз.

Затем мы открываем новое окно PyGame и запускаем основной цикл игры, заполняя (очищая) экран черным и отрисовывая наш мяч в текущей позиции (комментарии в коде обозначены символом #). Следующий кусок кода определяет, как будет двигаться мяч:

ballx = ballx + ballxmove
bally = bally + ballymove
if ballx > 600:
ballxmove = -1
if ballx < 0:
ballxmove = 1
if bally > 440:
ballymove = -1
if bally < 0:
ballymove = 1

В первых двух строках мы обновляем положение мяча по горизонтали (x) и вертикали (y), прибавляя две переменные передвижения. Если ballxmove и ballymove равны 1, то мяч переместится на 1 пиксель вправо и 1 пиксель вниз на каждом шаге цикла. Но затем оператор if проверяет, достиг ли мяч края экрана, и если это так, изменяет соответственно ballxmove и ballymove. Если, например, значение горизонтальной координаты мяча более 600 пикселей, он должен отскочить и начать двигаться влево – то есть мы начинаем прибавлять к его позиции -1 (по сути, вычитая 1). Несколькими строками кода мы создали впечатление, что мяч отскакивает от границ экрана – неплохо! Последние строки этой программы устанавливают связь с клавиатурой, чтобы вы могли в любой момент выйти из игры, нажав клавишу Esc.

[править] Игра с мячом 2.0

Хочу красивые

Окончательная версия нашей игры – не прорыв на графическом фронте, но мы можем принарядить ее, добавив фоновое изображение. Важно только помнить, как мы определяем столкновение с мячом – мы ищем белые пиксели. Поэтому фоновое изображение не должно содержать пикселей совершенно белого цвета (255,255,255 RGB), не то игра закончится, когда мышь окажется над ними!

Подыщите изображение и измените его размер до 640х480. Если на изображении окажется белый пиксель, вы всегда можете понизить яркость в GIMP и избавиться от проблемы. Сохраните изображение рядом с ball2.py и назовите его background.jpg. Теперь, в ball2.py, введите следующий код под строкой ballpic.set_ colorkey:

backdrop = image.load('background.jpg')

Теперь наша фоновая картинка находится в памяти и готова к использованию. Нам необходимо отображать ее на экране на каждом шаге, так что переместитесь вниз по ball2.py и замените строку screen. fill(0) следующим:

screen.blit(backdrop, (0,0))

и фоновое изображение будет отрисовываться до мячей. Заметьте, что если изображение сложное (то есть в нем много цветов), этот дополнительный процесс немного замедлит игру – но вы можете подстроить скорость мячей и переменную delay, чтобы это скомпенсировать.

Пока все отлично – мы создали базовую структуру нашей игры. Теперь добавим мячей, а также определим столкновения курсора мыши с любым их них. Для первой задачи введем массив-словарь для отслеживания мячей. Это придаст программе гибкость: мы сможем иметь столько мячей, сколько захотим, не ограничиваясь ball0, ball1, ball2 и т. д. Словари – плевое дело в Python:

mydict = {‘Bach’: 100, ‘Handel’: 75, ‘Vivaldi’: 90}
print mydict[‘Vivaldi’]

Здесь мы ставим числа в соответствие трем словам, а затем выводим значение, содержащиеся в ‘Vivaldi’, то есть 90. Используем словарь для хранения значений X, Y, X-перемещения и Y-перемещения каждого мяча – почти как структуру в C. Но если C погружает нас в сумятицу управления памятью, то в Python можно создавать наборы объектов-мячей без труда, давая им свои записи в словаре.

Наконец, подумаем об обнаружении столкновений. Как угадать, что указатель мыши столкнулся с мячом? Логически кажется очевидным идти от позиции каждого мяча и сравнивать ее с позицией курсора мыши. Но мы пойдем на хитрость: мячи белые, а фон черный, так почему бы просто не проверять, находится ли курсор мыши над белым пикселем? Это всего одна строка кода, и так быстро...

Вот код, который вы можете найти в файле ball2.py в разделе Журнал/CodeProject на DVD, вместе с картинкой ball.png, которую мы создали ранее (он точно такой же).

from pygame import *
import random
ballpic = image.load(‘ball.png)
ballpic.set_colorkey((0,0,0))
numballs = 10
delay = 5
done = False
balls = []
for count in range(numballs):
balls.append(dict)
balls[count] = {‘x’: 0, ‘y’: 0, ‘xmove’: random.randint(1, 2),‘ymove’:
random.randint(1, 2)}
init()
screen = display.set_mode((640, 480))
display.set_caption(‘Ball game’)
event.set_grab(1)
while done == False:
 
screen.fill(0)
for count in range(numballs):
screen.blit(ballpic, (balls[count][‘x’], balls[count][‘y’]))
display.update()
time.delay(delay)
for count in range(numballs):
balls[count][‘x’] = balls[count][‘x’] + balls[count][‘xmove’]
balls[count][‘y’] = balls[count][‘y’] + balls[count][‘ymove’]
for count in range(numballs):
if balls[count][‘x’] > 620:
balls[count][‘xmove’] = random.randint(-2, 0)
if balls[count][‘x’] < -10:
balls[count][‘xmove’] = random.randint(0, 2)
if balls[count][‘y’] > 470:
balls[count][‘ymove’] = random.randint(-2, 0)
if balls[count][‘y’] < -10:
balls[count][‘ymove’] = random.randint(0, 2)
for e in event.get():
if e.type == KEYUP:
if e.key == K_ESCAPE:
done = True
if screen.get_at((mouse.get_pos())) == (255, 255, 255, 255):
done = True
print “You lasted for”, time.get_ticks()/1000, “seconds!

Основная идея этой программы та же, что и раньше, но добавился смачный код, требующий объяснений. В самом начале, где мы загружаем изображение мяча, мы заодно устанавливаем его colorkey в (0,0,0), что соответствует черному в RGB (Red/Green/Blue – Красный/Зеленый/Синий). Так мы превращаем черные пиксели картинки нашего мяча в прозрачные. Это важно, когда у нас перемещается несколько мячей, если мы хотим, чтобы они накладывались изящно, не создавая черных углов поверх друг друга. Итак, у наших мячей будут отображаться только белые пиксели.

Следующие переменные, numballs и delay, влияют на сложность игры. numballs управляет числом мячей, а delay – время (в миллисекундах) остановки игры после каждой итерации цикла. Можете оставить их как есть; но если вы стремитесь к большей сложности, увеличьте число мячей и снизьте задержку.

Строка balls = [] создает новый массив объектов-мячей, и, в типичной манере Python, количество объектов не ограничивается (и не нужно указывать его прямо сейчас). Строка

for count in range(numballs):

создает цикл, который выполняется numball раз (10), добавляя новые объекты словаря к массиву balls и присваивая им начальные значения – левый верхний угол экрана и случайные смещения вниз-вправо. Числа 1, 2 в генераторе случайных чисел означают «любое число в промежутке от 1 до 2 (включительно)». Итак, мы получили 10 мячей, стартующих со случайными скоростями.

Затем мы настраиваем экран, как раньше, и добавляем строку event. set_grab(1), которая заключает курсор мыши внутри окна игры – было бы слишком просто, если бы курсор мыши мог сбежать за границы! Затем идет главный цикл игры. Как и ранее, мы заполняем экран черным, а затем в другом цикле for вбрасываем все мячи на экран.

После обновления экрана и задержки (чтобы игра шла с одинаковой скоростью на всех машинах), мы вновь проходимся по массиву мячей, обновляя их позиции при помощи переменных перемещения. Каждый мяч имеет свою собственную копию xmove и ymove в своем словаре, так что все они передвигаются независимо. Далее следует логика игры, определяющая, достигли ли мячи границ экрана. Здесь мы слегка подогнали значения так, чтобы мячи могли чуть-чуть заходить за край экрана (помните, их размер 32х32 пикселей). Это жизненно важно для игрового процесса, поскольку означает, что вам нельзя просто забиться курсором мыши в угол, где мячи вас не достанут! Мячи теперь достигают любой точки экрана, так что пошевеливайте мышью.

Последние три строки кода новые: screen.get_at() возвращает значение цвета пиксела в указанной позиции, то есть в положении курсора мыши, определяемого при помощи mouse.get_pos(). Мы говорим: «если цвет пикселя в точке нахождения курсора белый (255,255,255), то выполнить done = True», и главный цикл игры while закончится.

И наконец, мы выводим число секунд, в течение которых игрок смог выжить – time.get_ticks() возвращает его в миллисекундах, так что перед выводом мы делим его на 1000.

[править] Отделка

Неплохо для 55 строк кода, не так ли? Как уже говорилось, вы можете усложнить игру, увеличив значение numballs в начале – стандартное значение 10 достаточно непросто, но если вы надеетесь на свое проворство, рискните установить 15 или 20, для уворачивания с буквально бешеной скоростью. Есть еще много аспектов игры, с которыми можно поэкспериментировать: например, изменить случайные числа в разделе основной логики программы (при ударе мяча о край экрана).

PyGame ломится от функций, готовых к экспериментам, и, используя несколько строк кода, вы можете добавить в игру звуковые эффекты или даже фоновую музыку. На http://www.pygame.org/docs/ имеется фантастически основательная документация, помогающая пользователям изучить функциональность библиотеки, включая подпрограммы, использованные на нашем уроке. Имея опыт программирования на бесчисленном множестве языков и в различных средах, от Amiga Blitz Basic до C#-SDL в Mono/.NET, я могу смело заявить, что PyGame – один из самых простых в мире наборов для программирования игр: это прекрасный способ воплотить любые идеи, возникшие в вашей голове. Удачи!

Персональные инструменты
купить
подписаться
Яндекс.Метрика