Blender Game Networking

Материал из Blender3D.

Перейти к: навигация, поиск

автор: Brian Cordell Hynds

перевод: wow

Содержание

Введение

Увеличить

По этому руководству, мы создадим работающий сервер\клиент для использования в Blender Game Engine. Вам будет представлена низкоуровневая работа с сетью на Python используя сокеты. Все будет объяснено шаг за шагом и проиллюстрировано, готовые учебные файлы доступны для скачивания.

Часть 1. Работа с сетью и скрипты.

Давайте потратим минуту для объяснения некоторых вещей о компьютерных сетях. Я могу написать целую энциклопедию о компьютерных сетях, но я не думаю что вам необходимо знать гораздо больше чем самые основы, что такое пакеты. Вам не нужно полностью понимать следующую часть, но она включена для тех кому это необходимо. Когда компьютеру требуется послать данные другому компьютеру, он посылает пакеты информации через физическую среду получателю. Этот пакет включает в себя заголовок, в котором указано куда пакет надо отправить. Когда пакет отправлен и получен, он проходит через стандарт, известный как OSI модель. Только одну вещь вы должны знать об OSI модели (и протоколе TCP/IP) это то, что это работает. Пакеты посылаются через системную службу, которая связана с портом. Порт это просто номер который TCP использует для отправления и получения информации. Теперь, когда я собрал шесть месяцев курса по компьютерным сетям в один параграф, давайте посмотрим на следующую схему:

Увеличить

Эта простая схема показывает как мы будем устанавливать наш сервер и клиент. Клиент свяжется с сервером и соединение сокетами будет установлено. Клиент пошлет строку с данными на сервер. Сервер прочитает данные, отправит обратно клиенту и закроет соединение. Это все. Эта модель известна как эхо-сервер (Echo server), так как его единственная функция повторять данные обратно клиенту. Сперва запустим Blender. Чтобы избежать трудностей в прохождении шаг за шагом через создание игрового меню, я освобождаю вас от создания его для демонстрационных целей. Вы можете скачать файл по ссылке в конце статьи. Давайте посмотрим что мы имеем. Кратко, в двух словах, это камера, три ввода (IP сервера, порт и сообщение) и кнопка соединения. Здесь так же поле для сообщений сервера, пустышка (empty), ее мы будем использовать в качестве контроллера пользовательского интерфейса. Давайте начнем соединять все это вместе. Чтобы получить некое взаимодействие с игрой, мы сделаем наш курсор видимым. Мы напишем скрипт на Python, который будет делать это. Откройте окно редактора текста (Text Editor) и создайте новый файл "onGameLoad.py". Это даст мне возможность ввести вас в программирование на Python, остальная часть руководства не будет такой сложной. С Python мы можем добавить почти безграничную функциональность в нашу игру. Логические блоки – хороший способ добавить не сложную функциональность без кодирования, но этот способ может стать очень сложным когда пробуешь добавить функциональность которая требует более 20 соединений блоков. Используя Python мы можем избежать создания паутины из соединений блоков и продвинуться дальше за ограничения, которыми логические блоки сдерживают нас. Однако, без логических блоков у нас не будет способа вызвать наши скрипты на Python. Давайте вернемся к скрипту "onGameLoad.py". Мы хотим создать скрипт, который делает курсор видимым в игре. Модуль Python’а который делает это – Rasterzer. Для доступа к функциям Rasterizer, мы должны сперва импортировать модуль в наш скрипт. После импорта модуля вы будете иметь доступ ко всем его функциям. Вы можете импортировать скрипт используя "import [module]".Для краткости будем использовать опцию "as [name]". Это очень поможет когда будут писаться очень длинные скрипты, которые потребуют печатать название модуля.

import Rasterizer as r

Чтобы показать курсор мыши на экране, мы используем функцию showMouse():

r.showMouse(1)

Заметьте, для вызова функции showMouse(), мы должны сперва сослаться на модуль. Чтобы вызвать любые функции содержащиеся в модуле, вы должны сослаться на них в следующем порядке "module.function().subfunction()". Другая вещь на которую надо обратить внимание это атрибуты функции. В нашем случае я использовал «1» для задания истинного значения ('True'). Ложь ('False') представлена как «0». Фактически любое ненулевое значение это истина ('True'). Мы могли применить "r.showMouse(True)" и это сработало бы также. Это зависит от персональных предпочтений, но я думаю, печатать «1» и «0» намного быстрее и легче, чем 'True' и 'False'.

Теперь, когда наш скрипт сделан, мы должны создать несколько логических блоков, которые будут вызывать его. Мы назначим их нашему empty, названному "GameController". Выберите empty и откройте окно логических блоков (Logic Bricks window) нажав [F4]. Добавьте сенсор Always и отключите level pulsing. Затем добавьте контроллер Python. В нем мы запишем имя скрипта, который будет запускаться. Соедините точки и протестируйте нашу игру (перев.: нажав "P"). Вы теперь можете видеть ваш курсор на экране игры.

Сказанное выше позволило лучше понять как скрипты на Python работают в игровом движке. Теперь давайте выйдем из режима для начинающих и посмотрим немного настоящего кода. Отсюда и далее я не буду объяснять все вроде «кликни это», «помести сюда» и т.д. Если вы действительно собираетесь добавить сетевые компоненты в ваши игры, вы уже должны иметь хорошее представление об основах.

Часть 2: Настройка сервера на Python'е

Теперь, когда наш курс для начинающих позади (вполне возможно букварь для кого-то), давайте запустим наш простой TCP сервер на Python. Я люблю использовать редактор текста (Text Editor) в Blender для редактирования моих скриптов, главным образом потому что я ненавижу переключаться взад и вперед между приложениями. [CTRL+W] сохраняет .blend файл и [ALT+S] сохраняет текстовой файл вне файла .blend (отдельным файлом). Следующий скрипт надо будет сохранить вне .blend файла, потому что это будет наш сервер. Я пытаюсь найти способ просто запускать сервер внутри игры, но пока простого способа сделать это я найти не могу. Запуск его вне Blender так же делает его более быстрым при реагировании на запросы и обработке клиентских соединений.


Теперь давайте посмотрим на сердце сетевой инфраструктуры, сервер. Как я упоминал ранее, мы будем использовать сокеты Python чтобы сделать соединение между клиентом и сервером. Я буду показывать вам TCP сервер на Python, который мы будем использовать. С помощью комментариев я буду объяснять код.

# импортируем модуль socket
import socket
host = 
# Зададим в переменной номер порта. Задание значения таким способом 
# позволяет легче вносить изменения, вместо изменения каждой ссылки на порт в скрипте. 
# Внимание, используйте номера порта более 1024. Популярные сервисы, 
# такие как ftp, http и т.д. используют номера портов меньше 1024, это 
# поможет избежать конфликтов.
port = 2020

# Мы обозначим сокет как 's' вызвав модуль socket и функцию socket(). 
# Атрибуты функции socket() говорят сокету создать сокет 
# типа STREAM из области INET (INET socket family).
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# Затем мы свяжем порт и хост (bind the port to the hostname). 
# Это сообщит серверу что мы хотим использовать определенный порт для приема.
s.bind((socket.gethostname() , port))

# Теперь давайте скажем серверу начать ожидать подключения клиента.
# для тестовых целей мы настроим сервер на обработку 1 совместного соединения. 
# Вы можете изменить это познее если вам необходимо
s.listen(1)

# Так как этот скрипт будет запускаться вне игры, вероятнее всего в командном 
# окне (terminal window), мы можем использовать функцию print(), чтобы показать 
# что сервер запущен, я вставил переменную port в команду выводящую на экран номер порта.
print "Game server is running on port", port

# Это наш основной цикл обработки входящих клиентских подключений.
# Подключения принимаются и сообщение сохраняется в переменную.
# Принятая информация печатается функцией print() в командное окно (terminal window) 
# и затем сообщение отсылается обратно клиенту. Затем соединение закрывается.
while 1:
conn, addr = s.accept()
data = conn.recv(1024)
# выводит на экран информацию о клиентском подключении
print "connected...", addr, data
conn.send(data)
conn.close()

Сохраните этот файл как "server.py". Если вы создали файл внутри blender'а, используйте ALT+S чтобы сохранить его вне файла .blend. Затем откройте командное окно (terminal window), найдите папку где вы сохранили скрипт сервера, и напишите в командной строке "python server.py" чтобы запустить сервер. Вы увидите что-то типа:

$ python server.py
Game server is running on port 2020

Linux: Чтобы правильно завершить процесс, используйте команду "top" и найдите в списке "Python". Запомните его PID и нажмите клавишу "Q" для того чтобы выйти. Затем используя команду "kill [pid]" замените [pid] на PID Питона. После выполнения команды, вы получите сообщение "Terminated" об отключении вашего сервера.


Часть 3: Создание клиента.

Увеличить

Теперь мы имеем работающий сервер, но он ничего не сделает если мы не создадим наш клиент. Используя .blend файл из предыдущей части, давайте настроим несколько вещей перед созданием кода клиента. Во-первых, если вы посмотрите на меню, вы увидите 3 поля ввода (3 inputs): Server IP (input1), Port (input2) and Message (input3), они используются для создания соединения с сервером. Сейчас нет простого способа для ввода данных в несколько текстовых объектов, мы должны будем сделать для этого скрипт. Перед тем как мы начнем, выберите каждый объект ввода (input object) и создайте два свойства. Первое будет строка с именем "Text" и второе – логическая переменная с именем "edit" . Задайте свойству edit объекта input1 значение True, а другим False. Это покажет нам где начало.

Я только кратко опишу скрипт, вместо того чтобы пройти с вами по коду шаг за шагом. Мы создадим триггер, который проверяет, имеет ли Сенсор (сенсор клавиатуры) положительное значение. Если это так , тогда мы создаем список (Python list) из 3 полей ввода. Затем мы запускаем цикл if/elif чтобы задать свойства "edit" соответствующих полей в зависимости от того в каком условном операторе истина. Достаточно просто. Вот скрипт. Сохраните его как "tabController.py" в текстовом редакторе Blender.

import GameLogic as gl
cont = gl.getCurrentController()
trigger = cont.getSensor("tabcheck")
if trigger.isPositive():
# получить объекты 
input1 = gl.getCurrentScene().getObjectList()["OBinput1"] # ip адрес сервера
input2 = gl.getCurrentScene().getObjectList()["OBinput2"] # номер порта
input3 = gl.getCurrentScene().getObjectList()["OBinput3"] # сообщение
proplist = [input1, input2, input3]
# сервер => порт
if (proplist[0].edit == 1):
proplist[0].edit = 0
proplist[1].edit = 1
proplist[2].edit = 0
# порт => сообщение
elif (proplist[1].edit == 1):
proplist[0].edit = 0
proplist[1].edit = 0
proplist[2].edit = 1
# сообщение => сервер
elif (proplist[2].edit == 1):
proplist[0].edit = 1
proplist[1].edit = 0
proplist[2].edit = 0

Выберите "GameController" empty и задайте сенсор клавиатуры (Keyboard Sensor). Для этого упражнения, я буду использовать кнопку Enter, просто потому что нажатие кнопки Tab добавляет символ "@" в текстовое поле. Вы можете использовать Tab если хотите, но вы должны будете отредактировать скрипт чтобы удалять "@" в текстовых объектах (text object). Присоедините этот сенсор клавиатуры к контроллеру Python и впишите в поле контроллера "tabController.py".

Увеличить

Здесь есть несколько проблем относящихся к созданию логических блоков. Мы должны создать два сенсора мыши («курсор мыши над» и «левая кнопка»), и когда они оба сработают скрипт на Python начнет выполняться. Глянув вы можете подумать «хорошо, подключим их обоих на контроллер скрипта». Но это не будет работать потому, что каждый сенсор мыши будет срабатывать независимо, как если бы это был логический оператор «ИЛИ» (OR).

Увеличить

Скрипт будет запускаться каждый раз когда левая кнопка мыши будет нажата и когда курсор будет над кнопкой. Мы должны присоединить их к контроллеру «И» (AND), но теперь мы столкнемся с проблемой невозможности запустить скрипт напрямую. Так как это не актуатор (Python Actuator), мы будем использовать актуатор Свойств (Property Actuator) чтобы задать свойство с логическим значением, которое будет триггером сенсора свойств (Property Sensor), оно будет инициализировать скрипт. Посмотри на картинку чтобы увидеть как это работает: Теперь о скрипте. Аналогично скрипту сервера, я покажу вам скрипт который мы будем использовать и с помощью коментариев в скрипте буду объяснять что происходит.

# Так как мы будем использовать функции игрового движка Blender,
# мы должны загрузить модуль GameLogic.
import socket
import GameLogic as gl

# Здесь другая проблема с логическими блоками. Мы можем инициализировать скрипт 
# используя контроллер Python, но мы не можем остановить их когда их сенсор 
# не имеет значения «Истина» (true). Если мы не проверим свойство, то скрипт 
# фактически будет выполняться дважды (при клике и при release).
# Мы создадим цикл для проверки, если свойство имеет значение «Истина» (true), 
# то в этом случае мы запускаем только один раз.
conprop = gl.getCurrentController().getOwner()
if (conprop.connect == True):

# Теперь давайте получим значение свойства "Text" из полей ввода и назначим их 
# соответствующим переменным. Обратите внимание что мы должны использовать 
# функцию int() для задания порта. Если мы не сделаем, переменная получит 
# в качестве значения строку (string) и вы будете получать ошибку когда будете 
# связываться с портом, потому что это не целое (integer) значение.
hostinput = gl.getCurrentScene().getObjectList()["OBinput1"]
host = hostinput.Text
portinput = gl.getCurrentScene().getObjectList()["OBinput2"]
portstring = portinput.Text
port = int(portstring)

# Как и для сервера, мы создаем сокет
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# Вместо ожидания соединения, мы бы будем присоединятся к серверу.
s.connect((host, port))

# Мы возьмем сообщение из input3 и используя функцию send() пошлем его на сервер.
message = gl.getCurrentScene().getObjectList()["OBinput3"]
sendme = message.Text
s.send(sendme)

# Запишем в переменную ответ сервера, так мы сможем использовать его позднее
# Закроем соединение
data = s.recv(1024)
s.close()

# Теперь мы можем вывести на экран полученное сообщение в текстовом
# объекте "ответ сервера", назначив его свойству Text.
resp = gl.getCurrentScene().getObjectList()["OBresponseValue"]
resp.Text = data

# И в завершение, мы присвоим свойству connect значение 'ЛОЖЬ'
conprop.connect = 0

Давайте протестируем все. Запустите сервер, затем запустите игру. Напечатайте IP сервера, номер порта на котором запущен сервер и сообщение, которое вы хотите послать. Нажмите соединение (connect), и если все правильно, сервер повторит ваше сообщение, которое будет выведено на экран в поле для ответов (response area). Вы можете увидеть данные соединения и сообщение в командном окне (terminal window) сервера. Надеюсь, эта статья даст вам хорошее представление о том, как делается простое соединение сокетом, и о том как вы можете добавить сетевые функции в ваши игры. Вы можете скачать готовый .blend файл, который содержит сервер и клиент для игры. В моей следующей статье, мы будем рассматривать как разработать простой чат-сервер на Python, идентификацию паролем и настойку Blender UI чтобы создать чат-клиент в игровом движке. До тех пор, если у вас есть вопросы, комментарии или проблемы, не стесняйтесь пишите мне на bchynds@mac.com или заходите на дискуссию на BlenderArtists в ветку форума http://blenderartists.org/forum/showthread.php?t=111532


От переводчика:

  • для работы скрипта необходима установка Python'а соответствующей версии (скачать здесь http://www.python.org/download/) например для Blender 2.48 нужен Python 2.5.2 и выше (но только не Python 3.0)
  • если код выдает ошибку, возможно требуется заменить ip-адрес на "localhost"
Личные инструменты