Программирование аркадных игр
и обучение информатикеChapter 8: Введение в анимацию
8.1 Прыгающий прямоугольник
Чтобы начать работать с анимацией, начнём с простой pygame программы, открывающей
пустой экран. Исходный код pygame_base_template.py может быть взят здесь:
pygame_base_template.py
Мы создадим программу, которая будет перемещать белый прямоугольник по экрану с чёрным фоном. Можете выбирать свои цвета, просто удостоверьтесь, что цвет фона отличается от цвета самого прямоугольника! Так что начнём со взятия основного шаблона и поменяем цвет фона с белого на чёрный. Этот код должен находится на 46й строке.
screen.fill(black)
Теперь перейдём к анимации объекта. Простого прямоугольника будет достаточно. Этот код должен быть размещён после очистки экрана и до его переворота.
pygame.draw.rect(screen,white,[50,50,50,50])
Этот код будет рисовать прямоугольник в точке (50,50) каждый раз проходял через цикл. До тех пор, пока числа остаются одинаковыми, прямоугольник двигаться не будет.
Получить изменяемое значение нам поможет использование переменной. Нижеприведённый код - только первый шаг к достижению этой цели:
rect_x = 50 pygame.draw.rect(screen,white,[rect_x,50,50,50])
Чтобы переместить прямоугольник направо, x должен быть увеличен на 1 в каждом кадре. Этот код близок к такому положению дел, но он всё ещё не помогает нам достичь цели:
rect_x = 50 pygame.draw.rect(screen,white,[rect_x,50,50,50]) rect_x += 1
Проблема в вышеуказанном коде - значение rect_x скидывается на 50 каждый раз во время прохождения через цикл. Чтобы исправить эту проблему, уберите инициализацию rect_x = 50 наверх, чтобы она находилась вне цикла. Следующий отрывок кода будет успешно двигать прямоугольник направо.
# Начальная позиция прямоугольника rect_x = 50 # -------- Цикл главной программы ----------- while done==False: for event in pygame.event.get(): # Пользователь что-то сделал if event.type == pygame.QUIT: # Если пользователь щёлкнул на кнопки закрытия done=True # Поставить значение done, чтобы выйти из цикла # Установить фон окна screen.fill(black) pygame.draw.rect(screen,white,[rect_x,50,50,50]) rect_x += 1
Чтобы двигать коробку быстрее, увеличьте значение, на которое растёт rect_x:
rect_x += 5
Увеличение как x, так и y позиций одновременно, заставит прямоугольник двигаться вниз и направо:
# Начальная позиция прямоугольника rect_x = 50 rect_y = 50 # -------- Основной цикл программы ----------- while done==False: for event in pygame.event.get(): if event.type == pygame.QUIT: done=True # Установить фон экрана screen.fill(black) # Нарисовать прямоугольник pygame.draw.rect(screen,white,[rect_x,rect_y,50,50]) # Подвинуть начальную точку прямоугольника rect_x += 5 rect_y += 5
Направление и скорость движения ящика может быть сохранено в векторе. Это сделает лёгким изменение направления и скорости объекта. Следующая часть кода покажет использование переменных для сохранения изменений x и y, равному (5, 5).
# Скорость и направление прямоугольника rect_change_x = 5 rect_change_y = 5 # -------- Основной цикл программы ----------- while done==False: for event in pygame.event.get(): # Пользователь что-то сделал if event.type == pygame.QUIT: # Если пользователь щёлкнул на кнопки закрытия done=True # Поставить значение done, чтобы выйти из цикла # Установить фон окна screen.fill(black) # Нарисовать прямоугольник pygame.draw.rect(screen,white,[rect_x,rect_y,50,50]) # Передвинуть исходную точку прямоугольника rect_x += rect_change_x rect_y += rect_change_y
Когда ящик ударится о границу экрана, он всё равно будет продолжать движение. Ничто не заставляет прямоугольник отскочить от границы экрана. Чтобы развернуть траекторию движения прямоугольник, нужно поменять rect_change_y с 5 на -5 сразу после того, как ящик достигнет нижней границы окна. Ящик находится внизу когда rect_y больше, чем высота экрана. Нижеприведённый код показывает изменение направления:
# Развернуть ящик, если нужно: if rect_y > 450: rect_change_y = rect_change_y * -1
Зачем проверять rect_y с 450? Если экран будет высотой в 500 пикселей,
тогда сравнение с 500 будет вполне логичной первой догадкой. Однако прямоугольник рисуется
начиная с верхней левой точки. Так что прямоугольник, прежде чем отпрыгнуть, сначала полностью
ускользнёт за границу экрана. Примите во внимание, что высота прямоугольника - ровно 50 пикселей:
$500-50=450$.
Нижеприведённый код заставит прямоугольник отпрыгивать от всех четырёх сторон окна 700x400:
# Заставить прямоугольник отпрыгнуть, если нужно if rect_y > 450 or rect_y < 0: rect_change_y = rect_change_y * -1 if rect_x > 650 or rect_x < 0: rect_change_x = rect_change_x * -1
Несколько других команд могут быть использованы для анимации более сложных форм. Нижеприведённый код рисует красный прямоугольник внутри белого прямоугольника. Красный прямоугольник сдвинут на 10 пикселей по осям x и y относительно верхнего левого угла белого прямоугольника. Он меньше на 20 пикселей по размеру в обоих измерениях, результируя в 10 пикселевой рамке белого прямоугольника, окружающей красный.
# Нарисовать красный прямоугольник внутри белого pygame.draw.rect(screen,red,[rect_x+10,rect_y+10,30,30])
8.2 Анимация снега
8.2.1 Объяснение кода
Для начала работы с этой главой, начните с основной pygame программы, которая открывает пустой
экран. Исходный код pygame_base_template.py можно найти здесь:
http://ProgramArcadeGames.com/python_examples/show_file.php?file=pygame_base_template.py
Возможно создавать звёзды, снег, дождь, используя случайные числа. Самый простой способ: начать с цикла for для рисования кружков в случайных x,y позициях. Проверьте следующий код внутри основного цикла while.
for i in range(50): x=random.randrange(0,400) y=random.randrange(0,400) pygame.draw.circle(screen,white,[x,y],2)
Запуск программы показывает нам проблему. Каждый раз в цикле, мы рисуем звёзды в новых случайных местах. Программа рисует звёзды с новым местоположением 20 раз в секунду!
Для сохранения звёзд в одном месте, необходимо составить список с их местоположениями. Программа может использовать список python для достижения этой цели. Это должно быть сделано до главного цикла, иначе программа будет добавлять по 50 новых звёзд в список каждую 1/20 секунды.
for i in range(50): x=random.randrange(0,400) y=random.randrange(0,400) star_list.append([x,y])
Когда местоположение звёзд было добавлено, их можно ассоциировать как нормальный список. Следующий код выведет x и y координаты первой звезды:
print( star_list[0] )
Это выведет значение выведет x и y первой звезды, потому что координаты сами по себе являются списком. Следующий код покажет только значение y первой точки:
print( star_list[0][1] )
Внутри главного цикла while, программа может использовать цикл for для рисования каждой вещи в списке звёзд. Помните, len(star_list) возвратит количество элементов в списке звёзд.
# Обработать каждую звезду из списка for i in range(len(star_list)): # Draw the star pygame.draw.circle(screen,white,star_list[i],2)
Если программе нужно сдвинуть все объекты в массиве вниз, как в случае со снегом, тогда добавление следующей строки кода в вышеприведённом цикле for заставит координату y увеличиваться:
# Передвинуть звезду вниз на один пиксель star_list[i][1]+=1
Это заставит снег падать вниз, но после того, как он уйдёт с экрана, ничего нового появляться не будет. Добавив нижеприведённый код, снег будет снова и снова падать сверху экрана из случайных точек:
# Если снежинка упала за границу экрана if star_list[i][1] > 400: # Вернуть её наверх y=random.randrange(-50,-10) star_list[i][1]=y # Дать ей новую позицию x x=random.randrange(0,400) star_list[i][0]=x
8.2.2 Полный код
""" Animating multiple objects using a list. Sample Python/Pygame Programs Simpson College Computer Science http://programarcadegames.com/ http://simpson.edu/computer-science/ Explanation video: http://youtu.be/Gkhz3FuhGoI """ # Import a library of functions called 'pygame' import pygame import random # Initialize the game engine pygame.init() BLACK = [0, 0, 0] WHITE = [255, 255, 255] # Set the height and width of the screen SIZE = [400, 400] screen = pygame.display.set_mode(SIZE) pygame.display.set_caption("Snow Animation") # Create an empty array snow_list = [] # Loop 50 times and add a snow flake in a random x,y position for i in range(50): x = random.randrange(0, 400) y = random.randrange(0, 400) snow_list.append([x, y]) clock = pygame.time.Clock() # Loop until the user clicks the close button. done = False while not done: for event in pygame.event.get(): # User did something if event.type == pygame.QUIT: # If user clicked close done = True # Flag that we are done so we exit this loop # Set the screen background screen.fill(BLACK) # Process each snow flake in the list for i in range(len(snow_list)): # Draw the snow flake pygame.draw.circle(screen, WHITE, snow_list[i], 2) # Move the snow flake down one pixel snow_list[i][1] += 1 # If the snow flake has moved off the bottom of the screen if snow_list[i][1] > 400: # Reset it just above the top y = random.randrange(-50, -10) snow_list[i][1] = y # Give it a new x position x = random.randrange(0, 400) snow_list[i][0] = x # Go ahead and update the screen with what we've drawn. pygame.display.flip() clock.tick(20) # Be IDLE friendly. If you forget this line, the program will 'hang' # on exit. pygame.quit()
8.3 3D анимация
Переход от двух измерений к трём, вводя игровую физику, на самом деле не так сложен, как может показаться. Хотя это выходит за границы данного класса, стоит рассказать, как это делается.
В открытом доступе существует 3D программа Blender, в которую встроен “игровой движок”, позволяющий программистам делать 3D игры. 3D объекты в игре могут содержать в себе код на Python, который будет контролировать их игровые действия.
Посмотрите на рисунок 8.1. На нём показан зелёный ящик с несколькими объектами внутри него. Синий объект контролируется Python скриптом, который двигает его вокруг ящика, сталкивая с другими объектами. Скрипт, показаный ниже, содержит в себе много особенностей, которые есть и у 2D программ. Есть главный цикл, есть список x,y координат, есть переменные, контролирующие вектор.
Основной цикл программы контролируется самим Blender'ом. Нижеприведённый код python автоматически вызывается каждый раз в программном цикле. Поэтому код на Python не показывает главного программного цикла. Однако он всё-же существует.
У синего объекта есть местоположение, записанное в формате x,y,z. К нему можно получить доступ, а затем и изменить с помощью переменной blueobject.position. Массив location [0] содержит x, [1] содержит y, а [2] содержит z.
Вместо использования переменных change_x и change_y, как это было сделано 2D примерах этой главы, пример на Blender использует blueObject["x_change"] и blueObject["y_change"].
Выражение if проверяет, достиг ли синий объект границ экрана и нужно ли изменить направление. В отличии от пикселей в 2D играх, местоположения объектов могут храниться в числах с плавающей точкой. Если хочется расположить объект между 5 и 6, вполне разрешено ставить его местоположение на 5.5.
Такое расширение позволяет лёгкое взаимодействие с джойстиком, которое будет показано далее в книге.
import bge # Получить ссылку на синий объект cont = bge.logic.getCurrentController() blueObject = cont.owner # Вывести x,y координаты синего объекта print (blueObject.position[0],blueObject.position[1] ) # Поменять x,y координаты в соответствии с x_change и # y_change. x_change и y_change - игровые свойства, # связанные с синим объектом. blueObject.position[0]+=blueObject["x_change"] blueObject.position[1]+=blueObject["y_change"] # Проверить, достиг ли объект границ. # Если да - поменять направление. Проделать это со всеми 4 краями. if blueObject.position[0] > 6 and blueObject["x_change"] > 0: blueObject["x_change"] *= -1 if blueObject.position[0] < -6 and blueObject["x_change"] < 0: blueObject["x_change"] *= -1 if blueObject.position[1] > 6 and blueObject["y_change"] > 0: blueObject["y_change"] *= -1 if blueObject.position[1] < -6 and blueObject["y_change"] < 0: blueObject["y_change"] *= -1
Blender можно скачать с:
http://www.blender.org/
Файл, продемонстрированный в видео данной главы, можно скачать здесь.
8.4 Повторение пройденного
Пройдите тест с несколькими вариантами ответов (на англ. яз.).
You are not logged in. Log in here and track your progress.
English version by Paul Vincent Craven
Spanish version by Antonio Rodríguez Verdugo
Russian version by Vladimir Slav
Turkish version by Güray Yildirim
Portuguese version by Armando Marques Sobrinho and Tati Carvalho
Dutch version by Frank Waegeman
Hungarian version by Nagy Attila
Finnish version by Jouko Järvenpää
French version by Franco Rossi
Korean version by Kim Zeung-Il
Chinese version by Kai Lin