https://habrahabr.ru/company/piter/blog/339934/- Профессиональная литература
- Python
- Блог компании Издательский дом «Питер»
Привет, Хаброжители! Предлагаем вместе создать в игре Minecraft копировальный аппарат.
Создание каркаса программы копировального аппарата
Первое, что нужно сделать, — создать каркас будущей программы. Сначала вы напишете несколько фиктивных функций, которые выводят свои имена, когда их вызывают, а затем постепенно наполните их программным кодом из существующих функций, написанных в других программах.
1. Создайте новый файл, выбрав в меню пункт File-New File (Файл-Создать файл), и сохраните его с именем duplicator.py.
2. Импортируйте необходимые модули:
import mcpi.minecraft as minecraft
import mcpi.block as block
import glob
import time
import random
3. Подключитесь к игре Minecraft:
mc = minecraft.Minecraft.create()
4. Определите несколько констант для хранения размера копировального аппарата. Не выбирайте слишком большие габариты, иначе аппарат будет слишком долго сканировать и воссоздавать объекты. Также определите начальные координаты копировальной камеры:
SIZEX = 10
SIZEY = 10
SIZEZ = 10
roomx = 1
roomy = 1
roomz = 1
5. Определите несколько фиктивных функций, которые потом будут делать все, что необходимо. Позднее вы заполните их программным кодом:
def buildRoom(x, y, z):
print("buildRoom")
def demolishRoom():
print("demolishRoom")
def cleanRoom():
print("cleanRoom")
def listFiles():
print("listFiles")
def scan3D(filename, originx, originy, originz):
print("scan3D")
def print3D(filename, originx, originy, originz):
print("print3D")
6. Фиктивная функция, реализующая меню, особенная, потому что в конце возвращает значение. Пока можно сгенерировать случайное число и вернуть его, чтобы иметь возможность проверить работу ранних версий программы. Скоро вы напишете полноценное меню. Фиктивная функция написана с целью проверить работу структуры программы; еще немного, и вы заполните ее настоящим программным кодом:
def menu():
print("menu")
time.sleep(1)
return random.randint(1,7)
7. Напишите главный игровой цикл, отображающий меню и использующий функцию, соответствующую выбранному в нем числу. Подробнее о том, как используется переменная
anotherGo, рассказано во врезке «Углубляемся в код»:
anotherGo = True
while anotherGo:
choice = menu()
if choice == 1:
pos = mc.player.getTilePos()
buildRoom(pos.x, pos.y, pos.z)
elif choice == 2:
listFiles()
elif choice == 3:
filename = raw_input("filename?")
scan3D(filename, roomx+1, roomy+1, roomz+1)
elif choice == 4:
filename = raw_input("filename?")
print3D(filename, roomx+1, roomy+1, roomz+1)
elif choice == 5:
scan3D("scantemp", roomx+1, roomy+1, roomz+1)
pos = mc.player.getTilePos()
print3D("scantemp", pos.x+1, pos.y, pos.z+1)
elif choice == 6:
cleanRoom()
elif choice == 7:
demolishRoom()
elif choice == 8:
anotherGo = False
Сохраните программу и запустите ее. Что вы видите? На рисунке 5.7 показано, что произошло у меня, когда я запустил эту программу на своем компьютере. Как видите, программа сообщает обо всех своих действиях — она выбрала случайный пункт в меню, а затем вызвала функцию, обрабатывающую его. Так как сейчас все функции в вашей программе просто выводят свои имена, вы увидите на экране лишь разные последовательности слов, как на рисунке 5.7, потому что функция menu() каждый раз делает случайный выбор.
Инструкция return дает возможность вернуть значение из функции в программу, которая использует функцию.
Например, когда требуется случайное число, вы вызываете функцию random.randint(). Она возвращает значение, которое вы сохраняете в переменной, например, так: a = random.randint(1,100). Инструкция return в языке Python позволяет использовать тот же прием, чтобы вернуть значение из своей функции.
Функция raw_input() читает строку текста, введенную с клавиатуры. Если добавить строку,
заключенную в круглые скобки, эта строка появится на экране как приглашение к вводу, то есть
инструкция name = raw_input(«What is your name?») задаст вопрос и вернет ответ. Функция raw_input() всегда возвращает введенный текст в виде строки. Если вам понадобится ввести число (например, в своей системе меню), используйте функцию int() для преобразования строки в число. Некоторые программисты на Python делают все в одной строке, например: age = int(raw_input(«What is your age?»)).
УГЛУБЛЯЕМСЯ В КОД
Cейчас нужно пояснить, как используется переменная anotherGo в программе duplicator.py.
Ниже приведены самые важные части программы:
anotherGo = True
while anotherGo:
choice = menu()
if choice == 8:
anotherGo = False
Это распространенный способ организации циклов, которые выполняются хотя бы один раз, спрашивают, хотите ли вы продолжить, и в случае отрицательного ответа завершаются. В Python есть множество способов достичь той же цели, но использование булевой (логической) переменной — пожалуй, лучший вариант, наглядно демонстрирующий, как работает программа.
В языке Python есть инструкция break, прерывающая выполнение циклов, однако в этой книге мы не будем ее использовать. Вы можете провести свое исследование в Интернете и узнать, как переписать этот цикл, чтобы использовать в нем инструкцию break и убрать булеву переменную.
Вывод меню
Система меню — очень удобная особенность и заслуживает быть добавленной в программы,
когда требуется возможность выбора из нескольких вариантов. Данная система меню выводит все доступные варианты и затем ждет, когда будет введен номер варианта из допустимого диапазона. Если ввести число за его пределами, меню появится повторно и предоставит еще одну попытку выбора.
Измените функцию menu — замените ее содержимое следующим программным кодом (не забудьте правильно оформить отступы!):
def menu():
while True:
print("DUPLICATOR MENU")
print(" 1. BUILD the duplicator room")
print(" 2. LIST files")
print(" 3. SCAN from duplicator room to file")
print(" 4. LOAD from file into duplicator room")
print(" 5. PRINT from duplicator room to player.pos")
print(" 6. CLEAN the duplicator room")
print(" 7. DEMOLISH the duplicator room")
print(" 8. QUIT")
choice = int(raw_input("please choose: "))
if choice < 1 or choice > 8:
print("Sorry, please choose a number between 1 and 8")
else:
return choice
Сохраните программу и запустите ее. Чем отличается эта версия программы от предыдущей,
с фиктивной функцией меню? На рисунке 5.8 показано, как выглядит настоящее меню.
Создание копировальной камеры
Копировальная камера строится из стекла и без передней стенки, чтобы персонаж мог лег-
ко зайти в нее и добавить или удалить блоки.
Замените содержимое функции buildRoom() следующим программным кодом. Будьте внимательны при вводе длинных строк, где используется функция setBlocks(), и обратите внимание, что на этот раз я не использовал стрелку ↲, потому что Python позволяет разбивать длинные строки на несколько строк (подробнее о том, в каких ситуациях можно переносить длинные строки, а в каких — нет, рассказано во врезке «Углубляемся в код»):
def buildRoom(x, y, z):
global roomx, roomy, roomz
roomx = x
roomy = y
roomz = z
mc.setBlocks(roomx, roomy, roomz,
roomx+SIZEX+2, roomy+SIZEY+2, roomz+SIZEZ+2,
block.GLASS.id)
mc.setBlocks(roomx+1, roomy+1, roomz,
roomx+SIZEX+1, roomy+SIZEY+1, roomz+SIZEZ+1,
block.AIR.id)
Сохраните программу и запустите ее. Проверьте работу варианта 1 в меню, чтобы убедиться, что он создает копировальную камеру. Перейдите в другое место в мире Minecraft и снова выберите вариант 1, чтобы увидеть, что из этого получится. На рисунке 5.9 показана копировальная камера сразу после ее постройки.
УГЛУБЛЯЕМСЯ В КОД
Отступы (сделанные с помощью пробелов или табуляторов) в языке Python используются, чтобы определить группы инструкций. Всякий раз, когда в программе используются циклы, инструкции if/else или функции, нужно добавлять отступы к сгруппированным инструкциям. Язык Python необычен как раз тем, что для группировки инструкций в блоки использует отступы. Во многих других языках, таких как C и C++, для этого используются специальные символы, например { и }. Поэтому при программировании на языке Python приходится уделять особое внимание оформлению отступов, иначе программа работает неправильно.
В большинстве случаев язык Python не позволяет переносить длинные строки. Именно поэтому в данной книге часто можно видеть стрелку ↲ в листингах, указывающую на то, что длинная строка не должна переноситься.
Однако в Python есть два других способа справиться с длинными строками.
Во-первых, можно использовать символ продолжения строки: если последним в строке поставить символ обратного слэша (\), вы можете продолжить программную инструкцию на следующей строке.
a = 1
if a == 1 or \
a == 2:
print("yes")
Второй способ, который иногда используется для переноса длинных строк, — выполнять перенос в точках, где интерпретатор Python сможет понять, что инструкция не закончилась. Например, когда определяется начальное значение списка или используется функция, инструкцию можно перенести — если Python поймет, что далее следует код. Ниже даны два примера с переносом строки. Так, открывающая скобка явно показывает, что строка не завершена, пока не встретится закрывающая скобка:
names = ["David",
"Steve",
"Joan",
"Joanne"
]
mc.setBlocks(x1, y1, z1,
x2, y2, z2, block.AIR.id)
Уничтожение копировальной камеры
Если запустить программу копировального аппарата много раз, в мире Minecraft появится множество копировальных камер, а функционировать будет лишь последняя. Спустя какое-то время в вашем мире окажется столько копировальных камер, что вы запутаетесь, какая из них действующая! Для решения этой проблемы нужно добавить в программу возможность уничтожить ставшую ненужной копировальную камеру, чтобы мир Minecraft не захламлялся.
Уничтожение копировальной камеры выполняется так же, как очистка пространства в программе clearSpace.py из Приключения 3. Вам нужно лишь знать внешние координаты камеры. Поскольку камера имеет толщину стен в один блок, ее внешние размеры на единицу больше внутреннего рабочего пространства, определяемого константами SIZEX, SIZEY и SIZEZ; вы легко их вычислите, использовав простую арифметику.
Измените функцию demolishRoom(), как показано ниже:
def demolishRoom():
mc.setBlocks(roomx, roomy, roomz,
roomx+SIZEX+2, roomy+SIZEY+2, roomz+SIZEZ+2,
block.AIR.id)
Сохраните и запустите программу. Теперь вы легко построите копировальную камеру и уничтожите ее. Достаточно выбрать вариант 1 для постройки и вариант 7 для уничтожения.
Сканирование объектов в копировальной камере
Вы уже написали эту часть программы в программе scan3D.py, поэтому просто вставьте готовое тело функции, внеся небольшие изменения.
1. Замените функцию scan3D() следующим программным кодом. Этот код почти идентичен коду в программе scan3D.py — в него добавлены строки, выделенные жирно, чтобы отразить ход сканирования в чате Minecraft. Сканирование в камере большого объема может длиться долго, поэтому хорошо иметь перед глазами индикацию, показывающую, как идет процесс. Вы можете скопировать функцию из предыдущей программы и сэкономить время на вводе:
def scan3D(filename, originx, originy, originz):
f = open(filename, "w")
f.write(str(SIZEX) + "," + str(SIZEY) + "," + str(SIZEZ)
+ "\n")
for y in range(SIZEY):
mc.postToChat("scan:" + str(y))
f.write("\n")
for x in range(SIZEX):
line = ""
for z in range(SIZEZ):
blockid = mc.getBlock(originx+x, originy+y,
originz+z)
if line != "":
line = line + ","
line = line + str(blockid)
f.write(line + "\n")
f.close()
2. Сохраните программу и проверьте, как она работает. Для этого зайдите в копировальную
камеру, постройте что-нибудь, а затем выберите вариант 3 в меню. Откройте файл, созданный в результате, и проверьте, правильно ли выполнено сканирование. На рисунке 5.10 показан фрагмент файла, полученного в результате сканирования. Обратите внимание на большое количество нулей — это объясняется тем, что блоки типа AIR тоже были зафиксированы сканером.
Очистка копировальной камеры
Представьте себе генеральную уборку. Полезно иметь возможность очистить камеру, чтобы начать с нуля. Для этого можно было бы просто уничтожить камеру и вновь ее создать, но гораздо проще добавить функцию очистки. Она будет отличаться от функции demolish-Room() только используемыми координатами:
1. Измените функцию cleanRoom(), чтобы она выглядела, как показано ниже. Обратите внимание: все начальные координаты в этой функции больше координат границ комнаты, чем в функции demolishRoom(), а конечные координаты — меньше. Благодаря этому уничтожено будет лишь то, что находится внутри камеры, а ее стены останутся целыми:
def cleanRoom():
mc.setBlocks(roomx+1, roomy+1, roomz+1,
roomx+SIZEX+1, roomy+SIZEY+1, roomz+SIZEZ+1,
block.AIR.id)
2. Сохраните и запустите программу. Постройте что-нибудь внутри камеры и затем выберите вариант 6, чтобы проверить, сможете ли вы быстро очистить камеру.
Воспроизведение объектов в копировальной камере
Написать функцию воспроизведения (создания копии) объекта, находящегося в копировальной камере, очень просто: у вас уже есть готовая программа print3D.py, которая делает это. Ниже я повторю программный код функции, чтобы вы увидели ее целиком.
def print3D(filename, originx, originy, originz):
f = open(filename, "r")
lines = f.readlines()
coords = lines[0].split(",")
sizex = int(coords[0])
sizey = int(coords[1])
sizez = int(coords[2])
lineidx = 1
for y in range(sizey):
mc.postToChat("print:" + str(y))
lineidx = lineidx + 1
for x in range(sizex):
line = lines[lineidx]
lineidx = lineidx + 1
data = line.split(",")
for z in range(sizez):
blockid = int(data[z])
mc.setBlock(originx+x, originy+y, originz+z,
blockid)
Сохраните и запустите программу. Постройте что-нибудь в копировальной камере, затем перейдите в другое место Minecraft и выберите в меню вариант 5. Содержимое камеры будет отсканировано и воспроизведено прямо перед персонажем. Попробуйте снова перейти в другое место мира Minecraft и воспроизвести объект еще несколько раз. Воспроизведите копию объекта в воздухе и под водой, чтобы посмотреть, что получится. На рисунке 5.11 показаны результаты работы этой программы.
» Более подробно с книгой можно ознакомиться на
сайте издательства
»
Оглавление
»
Отрывок
Для Хаброжителей скидка 20% по купону —
Minecraft