Основы создания сетевых игр с использованием DirectPlay
Материал из Blitz Et Cetera.
Итак, вы решили, что достаточно круты для создания сетевой игры? Значит, вы пришли по адресу. В этом руководстве я постараюсь подробно описать, как сделать сие чудо. Что собой представляет сетевая игра? Есть как минимум два компьютера - сервер и клиент. Сервер - это тот, кто создает игру, а клиент, соответственно, тот, кто к ней подключается. Причем, на одном компьютере можно запустить несколько игровых сеансов, но вот скорость, с которой они будут работать - это уже вопрос отдельный. Далее, каждый компьютер имеет свой адрес, именуемый IP-адресом. IP-адрес выглядит, например, вот так: "77.122.56.45". Чтобы подключится к серверу, клиент должен ввести его IP-адрес, однако тут есть один маленький нюанс, который может сильно подпортить жизнь. Это, так называемые, прокси. Прокси - это когда один компьютер имеет доступ в Интернет через другой компьютер. И подключенных к Интернету таким способом может быть хоть вся локальная сеть. Однако, они все имеют один IP-адрес - IP-адрес сервера. Видите проблему? Если сеанс создан на подключенным таким образом компьютером, к нему подключится нельзя. Впрочем, этот компьютер может подключаться к другим в сети. Но, к сожалению, к Blitz'у это не относится. И в первом и во втором случае он банально выдает ошибку. Я до сих пор ищу решение этой проблемы внутренними средствами. А вот внешние есть - например, спец. библиотеки, написанные на других языках.Например, K-Net Lib. Мы же будем использовать средство Blitz'а именуемое DirectPlay. Но не беспокойтесь - даже, если у вас Интернет через прокси (кстати, Интернет может быть через прокси провайдера), то вы сможете опробовать игру в оффлайне.
Дело в том, что есть очень полезные специальные адреса. Один из них, именуемый "loopback" всегда подразумевает под собой IP-адрес машины, на котором запущена игра. Этот IP-адрес - "127.0.0.1". Запомните его, потому что он понадобится вам очень часто.
После длинной теории, наверняка вам не терпится приступить к практике. Если вы хорошо прочитали и запомнили теорию, то можно приступать. Итак, смело вбиваем:
NetGame=StartNetGame()
Что же делает эта команда? Она вызывает диалоговое окно, в котором вы выбираем тип подключения, вводим IP-адрес сервера и, конечно, выбираем нужный нам сеанс. Некоторых сбивает это с толку, впрочем, я объясню. Сначала выбираете тип соединения в верхнем списке. Для локальной сети и Интернета рекомендую пункт с TCP/IP. Затем при выборе последнего выскакивает окно с просьбой ввести IP сервера. Если вы сервер - оставляйте пустым, если клиент - введите адрес сервера. В остальных случаях придется вводить параметры СОМ-порта, либо номер телефона для модемного соединения. Если мы сервер - в нижнем поле вводим имя сеанса и нажимаем кнопочку "Create New game" и смело идем в игру. Если же мы клиент - то двойным кликом щелкаем на нужном сеансе. Как видите, все довольно просто.
Эта функция возвращает 0 - подключение/создание сеанса не удалось, 1 - удачно подключились, 2 - удачно создали сеанс. Прервать сеанс можно прервать командой StopNetGame. Разрывается сеанс только на локальной машине! Причем, если сервер выходил из игры, то сервером становится другой игрок. И второму отправляется системное сообщение типа 102. Впрочем, о типах сообщений попозже.
Есть еще две функции, заменающие ее. Это HostNetGame и JoinNetGame. Первая запускает сеанс (нужно указать имя) и возвращает 2 при удаче, 0 - при неудаче. Вторая подключается к сеансу (нужно указать имя и IP) и возвращает 1 при удаче и 0 - при неудаче.
Далее, одна из самых важных команд - СreateNetPlayer. В скобках надо указать имя игрока. Обычно указывается имя, введенное с клавиатуры. Внимание! Это создает локального игрока! И только локального. Когда кто-то другой создает своего игрока, то остальным отправляется сообщение с типом 100. Поэтому надо добавлять имена, IDы, а также всю остальную информацию о игроках в типы. Рекомендуется именно в типы, так как в таком случае не будет ограничений по количеству игроков. Функция возвращает ID созданного игрока. Запишите его в секцию про свое alter ego.
Дальше - DeleteNetPlayer. Удаляет локального игрока по IDу. Может встать вопрос - а почему собственно, может быть несколько игроков на одном ПК? А в некоторых играх может быть так, что несколько игроков играют вот так за одним компьютером.
RecvNetMsg. Без нее вы не сможете получать никаких сообщений. Она возвращает True, если пришло сообщение и False, если нет.
NetMsgType. Возвращает тип сообщения. О, вот мы и добрались до типов. Итак, повторим, и скажу про новые. 100 - новый игрок(приходит от пришедшего игрока), 101 - игрок ушел (приходит от ушедшего игрока), 102 - теперь мы сервер, 200 - сеанс накрылся медным тазом. Сообщения в диапазоне 1 - 99 - пользовательские. Некоторые могут спросить - а зачем много типов сообщений? А для того, чтобы не путать сообщения с 3Д-координатами, и сообщения чата.
NetMsgFrom. Возвращает ID игрока, пославшего последнее сообщение.
NetMsgTo. Возвращает ID игрока, которому слали сообщение.
NetMsgData. Возвращает содержание сообщения. ВНИМАНИЕ! Обьединяйте данные в одну строку! Во-первых, вы избежите жутких тормозов, а во-вторых сократите траффик.
NetPlayerName. Возвращает имя игрока по его ID.
NetPlayerLocal. Возвращает True, если указанный ID игрока совпадает с нашим и False иначе.
SendNetMsg. Первый параметр - тип сообщения, второй - содержание, третье - наш ID, четвертое - ID получателя (0 если всем), пятое (необязательный параметр) - приоритет сообщений. Правда, если приоритет высокий, то скорость посылки сообщения низкая. Рекомендуется для чата.
Вот мы и разобрали, что есть что. Если вам нужны более точные сведения, то посмотрите справку, или мой перевод ее на Boolean.name -> Blitz3D -> Переводы -> Перевод раздела DirectPlay. И хотя там довольно много ошибок и неточностей, он понятен.
Далее, сделаем небольшую игру про кубики. Итак.
Type Player
Field name$,id,ent
End Type
Создаем тип игрока, который будет содержать его имя, ID и ссылку на кубик.
Global cam
Global myent
Global myid
Global myname$
Инициализируем глобальные переменные: для камеры, ссылку на свой кубик, ссылку на свой ID, ссылку на свое имя.
Graphics3D 800,
600,
32,
2
SetBuffer BackBuffer()
jng=JoinNetGame("World Arena","127.0.0.1")
If Not jng
Then HostNetGame("World Arena")
Инициализируем графику и создаем игру. Заметьте - мы сначала пробуем подключится к сеансу на своем компьютере, а если его нет - создаем. Это очень удобно для тестирования и отладки. Я надеюсь, вы не забыли loopback?
p.Player=
New Player
pname=Input("Enter your name:")
pid=CreateNetPlayer(pname)
pent=CreateCube()
myname=pname
myid=pid
myent=pent
Создаем игрока. Также даем значения ссылкам на будущее. Рекомендую вам тоже так делать.
cam=CreateCamera()
PositionEntity cam,0,2,-5
Создаем и располагаем камеру - трудно не догадаться :).
While Not KeyHit(1)
UpdatePlayer()
UpdateNetwork()
RenderWorld
Flip
Wend
Цикл - сердце игры :) Далее, приведу функции.
Function UpdatePlayer
()
If KeyDown(17) MoveEntity myent,0,0,.1
If KeyDown(31) MoveEntity myent,0,0,-.1
If KeyDown(30) TurnEntity myent,0,1,0
If KeyDown(32) TurnEntity myent,0,-1,0
SendNetMsg 2,PackMsg$(myent),myid,0
End Function
Function UpdateNetwork()
While RecvNetMsg()
Select NetMsgType()
Case 100
p.Player=New Player
pid=NetMsgFrom()
pname=NetPlayerName(pid)
pent=CreateCube()
Case 101
p.Player=FindPlayer(NetMsgFrom())
FreeEntity pent
Delete p
Case 2
p.Player=FindPlayer(NetMsgFrom())
UnpackMsg(pent,NetMsgData$())
End Select
Wend
End Function
Function FindPlayer.Player( id )
For p.Player=Each Player
If pid=id Then Return p
Next
End Function
Function PackMsg$(ent)
Return EntityX(ent)+"|"+EntityZ(ent)+"|"+EntityYaw(ent)
End Function
Function UnpackMsg(ent,dat$)
xp=Instr(dat$,"|")
x=Float(Left(dat$,xp))
dat$=Right(dat$,Len(dat$)-xp)
zp=Instr(dat$,"|")
z=Float(Left(dat$,zp))
dat$=Right(dat$,Len(dat$)-zp)
yaw=Float(Left(dat$,Len(dat$)))
PositionEntity ent,x,0,z
RotateEntity ent,0,yaw,0
End Function
Первая обеспечивает управление кубиком, вторая - проверяет, есть ли новые сообщение и реагирует на них. Третья - ищет игрока по типу, четвертая - объединяет данные в одно. Как я уже говорил, это делается для экономии трафика, и чтобы не было тормозов. Пятая - распаковывает запакованное :). Ну а также перемещает нужный кубик куда надо.
Как видите, создавать сетевые игры - это не так сложно как, возможно, кажется на первый взгляд. А может быть когда-нибудь вы сделаете супер-пупер ММОРПГ? :). В любом случае, желаю удачи.