Программирование аркадных игр и обучение информатике

Программирование аркадных игр
и обучение информатике

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 анимация

Видео: Демонстрация игрового движка Blender

Переход от двух измерений к трём, вводя игровую физику, на самом деле не так сложен, как может показаться. Хотя это выходит за границы данного класса, стоит рассказать, как это делается.

В открытом доступе существует 3D программа Blender, в которую встроен “игровой движок”, позволяющий программистам делать 3D игры. 3D объекты в игре могут содержать в себе код на Python, который будет контролировать их игровые действия.

fig.blender01
Figure 8.1: Пример Blender файла

Посмотрите на рисунок 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.