Среда, 13 Ноября 2024, 14:34

Приветствую Вас Гость

[ Новые сообщения · Игроделы · Правила · Поиск ]
  • Страница 1 из 1
  • 1
Мультиплеер GMS(Game Maker Studio networking) 2 часть.
mehanicДата: Четверг, 09 Июля 2015, 14:12 | Сообщение # 1
был не раз
Сейчас нет на сайте
1 часть - 3 часть на другом сайте - 4 часть на другом сайте(примеры тут же)
Мультиплеер в GMS #2 Игроки, ходьба, стрельба, жизни и немножко защиты.
Вот и пришло время написать вторую статью, думаю она будет гораздо больше чем первая, хотя комментировать я буду реже, потому что практически всё мы прошли в 1 части.

И так, откроем наш проект, в котором сделано абсолютно всё из 1 части и приступим. Начнём с создания спрайтов и объектов, создадим спрайт spr_bullet в сервере и клиенте который будет залит желтым цветом и иметь размер 8х8 пикселей, отцентрируем. Создадим объект obj_bullet в сервер и клиенте и присвоим ему наш спрайт. Всё, остальные объекты у нас уже есть.

Начнём с сервера...
СЕРВЕР: Первым делом создадим скрипт Send_to_all:
Код

for(ii=0;ii<ds_list_size(obj_controller.SocketList);ii++){
          var result=ds_list_find_value(obj_controller.SocketList,ii);
          network_send_packet( result , obj_controller.Buffer , buffer_tell( obj_controller.Buffer ) );
}

Как видно из скрипта, мы проходимся циклом по листу с сокетами(см. 1 часть) и отсылаем буффер каждому клиенту. Всё просто.

Теперь в сервере и в клиенте в объекте obj_player в Create пишем:
Код

image_speed=0

И в Step к этому объекту так же дописываем:
Код

image_angle=direction

В Draw к этому объекту переносим иконку из вкладки draw под названием Draw Self, не забываем что это мы делаем и в клиенте, и в сервере, и так же пишем код:
Код

draw_text(x,y,hp)


Теперь спасибо пользователю Qvant (замечание от пользователя) заменяем код в СЕРВЕРЕ в obj_controller в Asynchronouos в Networking на:
Код

var type_event = ds_map_find_value( async_load , "type" );/*получаем тип пришедших данных, в данном случаи это network_type_connect и другие...*/       
         switch( type_event ) {
            case network_type_connect://если игрок вошёл...
               var socket = ds_map_find_value( async_load , "socket" );//получаем его сокет
               var socketip_news = ds_map_find_value( async_load , "ip" );//получаем айпи пользователя
               if(ds_map_exists(SocketIp, socketip_news)){//если на сервере есть игрок с таким айпи, то...               
                  network_destroy(socket);//отключаем зашедшего от сервера
               }else{//иначе
                  ds_map_add(SocketIp, socketip_news, socket);//заносим новый айпи в словарь
                  ds_list_add( SocketList , socket );//заносим новый сокет в наш лист
               }
            break;
            case network_type_disconnect://если игрок вышел...
              var socket = ds_map_find_value( async_load , "socket" );
              var socketip = ds_map_find_value( async_load , "ip" );
              var findsocket = ds_list_find_index( SocketList , socket );//получаем индекс сокета из нашего списка который пришёл
              if ( findsocket >= 0 ) {//если есть...
                  ds_list_delete( SocketList , findsocket );//удаляем
                  ds_map_delete(SocketIp, socketip);//удаляем айпи из словаря
              }
            break;
            case network_type_data://если пришли данные...
               var buffer = ds_map_find_value( async_load , "buffer" );//получаем буффер
               var socket = ds_map_find_value( async_load , "id" );//получаем айди сокета
               buffer_seek( buffer , buffer_seek_start , 0 );/*ставим курсор в начало буфера(буффер, отсчитывать с начала буффера, отступаем с начала 0 знаком)*/
               ReceivedPacket( buffer , socket );//скрипт который принимает два аргумента, сейчас займёмся...
            break;
         }

И в СЕРВЕР в obj_controller в Create дописываем строчку:
Код

SocketIp=ds_map_create();//карта(словарь) с айпи пользователей

Объясняю что мы сделали, если это не понятно, при заходи на сервер каждый игрок проверятся, нету ли на сервере игрока с таким же айпи, и если такой игрок есть, то просто отключаем зашедшего игрока от сервера, тем самым освобождая место для других игроков, иначе вносим этого игрока с его айпи в лист с сокетами и словарь с айпи. За всё это отвечают строчки
Код
var socketip_news = ds_map_find_value( async_load , "ip" );
               if(ds_map_exists(SocketIp, socketip_news)){               
                  network_destroy(socket);
               }else{
                  ds_map_add(SocketIp, socketip_news, socket);
                  ds_list_add( SocketList , socket );//заносим новый сокет в наш лист
               }

Если вы запустите на своём компьютере два клиента, и присоединитесь к серверу, то последний клиент разумеется отключиться, что не нужно при тестирование, поэтому закомментируем эти строчки так:
Код

var socketip_news = ds_map_find_value( async_load , "ip" );
               /*if(ds_map_exists(SocketIp, socketip_news)){               
                  network_destroy(socket);
               }else{*/
                  ds_map_add(SocketIp, socketip_news, socket);
                  ds_list_add( SocketList , socket );//заносим новый сокет в наш лист
               //}

Думаю такой простой защиты хватит для небольшого проекта.:) В отключение пользователя от сервера думаю тоже всё понятно. Не забудьте в СЕРВЕР в obj_controller в Game End добавить строчку:
Код
ds_map_destroy( SocketIp );//удалить словарь с айпи пользователей


СЕРВЕР: В obj_player в collision с obj_bullet:
Код

if(other.idd!=idd){//если присвоенный айди пули не равен присвоенному айди нашего игрока
          hp-=10//отнимаем 10 hp
          buffer_seek( obj_controller.Buffer , buffer_seek_start , 0 );
          buffer_write( obj_controller.Buffer , buffer_u8 , 6 );
          buffer_write( obj_controller.Buffer , buffer_s32, idd);
          buffer_write( obj_controller.Buffer , buffer_u16, hp);
          Send_to_all();//отсылаем всем клиентам новые жизни у этого игрока
}


В сервере и в клиенте в obj_bullet в collision с obj_player:
Код

if(other.idd!=idd){//если присвоенный айди игрока не равен присвоенному айди пули, то...
          instance_destroy()//уничтожаем эту пулю при столкновение с игроком
}


В сервере и в клиенте в obj_bullet в Other в Outside Room перетаскиваем иконку уничтожения объекта(instance destroy) из вкладки main1.

Отложим пока что наш сервер, и займёмся клиентом.
КЛИЕНТ: В obj_player в step заменяем код на:
Код

image_angle=direction
if(idd!=global.myid){exit;}/*если айди игрока не равен нашему айди клиента(см. 1 часть), то прекращаем выполнение кода, сделано для того что бы мы не могли управлять всеми объектами.*/
direction=point_direction(x,y,mouse_x,mouse_y);
if(keyboard_check(ord("A"))){x-=4}
if(keyboard_check(ord("D"))){x+=4}
if(keyboard_check(ord("W"))){y-=4}
if(keyboard_check(ord("S"))){y+=4}
buffer_seek( obj_controller.Buffer , buffer_seek_start , 0 );
buffer_write( obj_controller.Buffer , buffer_u8 , 2 );
buffer_write( obj_controller.Buffer , buffer_s32 , idd );
buffer_write( obj_controller.Buffer , buffer_u16 , direction );
buffer_write( obj_controller.Buffer , buffer_f32 , x );
buffer_write( obj_controller.Buffer , buffer_f32 , y );
network_send_packet( obj_controller.Socket , obj_controller.Buffer , buffer_tell( obj_controller.Buffer ) );/*отправляем на сервер буффер с направлением, координатами и айди игрока.*/

if(keyboard_check_released(ord("E"))){
          buffer_seek( obj_controller.Buffer , buffer_seek_start , 0 );
          buffer_write( obj_controller.Buffer , buffer_u8 , 3 );
          buffer_write( obj_controller.Buffer , buffer_s32 , idd );
          network_send_packet( obj_controller.Socket , obj_controller.Buffer , buffer_tell( obj_controller.Buffer ) );/*отправляем на сервер буффер с айди игрока(уникальный айди 3 будет отвечать за то, что этот скрипт отвечает за стрельбу)*/
}


Ну вот подготовка в принципе и закончена, теперь приступаем к самому сложному, где главное не запутаться.
Открываем в СЕРВЕР скрипт ReceivedPacket, и заменяем всё на следующий код... Не знаю как тут всё объяснить, поэтому просто прокомментирую...
Код

var buffer = argument[ 0 ];//всё понятно
var socket = argument[ 1 ];
         var msgid = buffer_read( buffer , buffer_u8 );//читаем из buffer байт, который может быть от 0 до 255
         switch( msgid ) {
            case 1://если байт который мы прочитали равен 1(то есть когда игрок зашёл на сервер), то...
              with(obj_player){//каждый игрок который есть на сервере...
                  buffer_seek( obj_controller.Buffer , buffer_seek_start , 0 );
                  buffer_write( obj_controller.Buffer , buffer_u8 , 4 );
                  buffer_write( obj_controller.Buffer , buffer_s32, idd);
                  buffer_write( obj_controller.Buffer , buffer_u16, image_index);
                  buffer_write( obj_controller.Buffer , buffer_u16,  hp);
                  network_send_packet( socket , obj_controller.Buffer , buffer_tell( obj_controller.Buffer ) );/*отсылает зашедшему игроку буффер со своим уникальным айди, присвоенным айди, жизнями и кадром*/
              }
              global.max_players+=1//всего игроков было на сервере увеличиваем на один
              players=instance_create(96,96,obj_player);//создаём объект нового игрока
              players.idd=global.max_players;/*это самое главное, присваиваем объекту нового игрока его уникальный айди(это и есть присвоенный айди)*/
              players.hp=100//ставим объекту жизни
              players.image_index=irandom(1);//ставим объекту случайный кадр
              buffer_seek( Buffer , buffer_seek_start , 0 );//чистим буффер
              buffer_write( Buffer , buffer_u8 , 1 );/*записываем в Buffer значение 1(значение должно быть уникальным для разных данных, допустим что бы отправить координаты надо будет писать 2, а что бы получить ник игрока 3 и т.д) и указываем что оно от 0 до 255 (ниже приведу таблицу из справки GM:S)*/
              buffer_write( Buffer , buffer_u16,  global.max_players);//записываем уникальный айди для клиента в будущем
              buffer_write( Buffer , buffer_f32,  players.x);
              buffer_write( Buffer , buffer_f32,  players.y);
              buffer_write( Buffer , buffer_u16,  players.image_index);
              buffer_write( Buffer , buffer_u16,  players.hp);
              network_send_packet( socket , Buffer , buffer_tell( Buffer ) );/*отправляем буффер клиенту который послал запрос на получение этих данных (собственно сам игрок, буффер, размер отправляемого буффера). Отправляет клиенту уникальный айди, позицию, кадр, жизни*/
              buffer_seek( Buffer , buffer_seek_start , 0 );//чистим буффер
              buffer_write( Buffer , buffer_u8 , 5 );
              buffer_write( Buffer , buffer_s32,  global.max_players);
              buffer_write( Buffer , buffer_u16,  players.image_index);
              buffer_write( Buffer , buffer_u16,  players.hp);
              Send_to_all();//отправляем всем игрокам информацию о новом игроке
            break;
            case 2://отвечает за передвижение объектов
              var idd_player=buffer_read( buffer , buffer_s32 );//получаем айди из буффера который пришёл от клиента(от того который нажал клавишу)
              var img_angle=buffer_read( buffer , buffer_u16 );//поворот
              var xx=buffer_read( buffer , buffer_f32 );//позицию
              var yy=buffer_read( buffer , buffer_f32 );//позицию
              with(obj_player){
                  if(idd=idd_player){//если присвоенный айди объекта равен пришедшему айди от клиента, то...
                      direction=img_angle;//присваиваем поворот
                      x=xx;//позицию
                      y=yy;//позицию
                      buffer_seek( other.Buffer , buffer_seek_start , 0 );
                      buffer_write( other.Buffer , buffer_u8 , 2 );
                      buffer_write( other.Buffer , buffer_s32,  idd);
                      buffer_write( other.Buffer , buffer_u16,  direction);                      
                      buffer_write( other.Buffer , buffer_f32,  x);
                      buffer_write( other.Buffer , buffer_f32,  y);
                      Send_to_all();//отсылаем всем клиентам информацию о том, что игрок двинулся
                  }
              }
            break;
            case 3://отвечает за стрельбу
              var bullet_idd=buffer_read( buffer , buffer_s32 );
              with(obj_player){
                  if(idd=bullet_idd){//если присвоенный айди равен пришедшему айди, то...
                      bullet=instance_create(x,y,obj_bullet);//создаём пулю
                      bullet.idd=idd;//присваиваем пули пришедший присвоенный айди
                      bullet.speed=10;//устанавливаем пули скорость
                      bullet.direction=direction;//устанавливаем направление
                      buffer_seek( other.Buffer , buffer_seek_start , 0 );
                      buffer_write( other.Buffer , buffer_u8 , 3 );
                      buffer_write( other.Buffer , buffer_s32,  bullet.idd);
                      buffer_write( other.Buffer , buffer_u16,  direction);                      
                      buffer_write( other.Buffer , buffer_u16,  bullet.speed);
                      Send_to_all();//отсылаем информацию о пули всем игрокам, даже тому который послал нам данные
                  }       
              }         
            break;
         }


Хух, с сервером разобрались... Теперь приступаем к клиенту... Там тоже есть такой скриптик и он тоже объёмный... facepalm

Открываем в КЛИЕНТ скрипт ReceivedPacket, и заменяем всё на следующий код...
Код

         var buffer = argument[ 0 ];
         var msgid = buffer_read( buffer , buffer_u8 );
         switch( msgid ) {
            case 1://если мы получили информацию о том, что мы создались на сервере...
               global.myid = buffer_read( buffer , buffer_u16 );//присваиваем нашему клиенту пришедший айди
               player=instance_create(0,0,obj_player);//создаём НАШЕГО игрока
               player.idd=global.myid;/*присваиваем игроку присвоенный на сервере айди(тут буду тоже называть присвоенный айди), и далее присваиваем остальные данные...*/
               player.x=buffer_read( buffer , buffer_f32 );
               player.y=buffer_read( buffer , buffer_f32 );
               player.image_index=buffer_read( buffer , buffer_u16 );
               player.hp=buffer_read( buffer , buffer_u16 );
            break;
            case 2://отвечает за передвижение всех игроков на клиенте...
              var idd_player=buffer_read( buffer , buffer_s32 );
              if(idd_player!=global.myid){/*если пришедший с сервера айди игрока не равен нашему(потому что наш объект мы двигаем клавишами, а не получаем информацию о нём с сервера)*/
              var img_angle=buffer_read( buffer , buffer_u16 );
              var xx=buffer_read( buffer , buffer_f32 );
              var yy=buffer_read( buffer , buffer_f32 );
              with(obj_player){
                  if(idd=idd_player){/*то ищем среди всех объектов игрока у которого присвоенный айди равен пришедшему, и изменяем данные соответственно*/
                      direction=img_angle;
                      x=xx;
                      y=yy;
                  }
              }
              }
            break;
            case 3://отвечает за пули
              var idd_bullet=buffer_read( buffer , buffer_s32 );
              var direction_bullet=buffer_read( buffer , buffer_u16 );              
              var speed_bullet=buffer_read( buffer , buffer_u16 );
              with(obj_player){
                  if(idd=idd_bullet){//если присвоенный айди игрока равен пришедшему, то...
                      bullet=instance_create(x,y,obj_bullet);//создаём пулю у этого игрока и посылаем исходя из информации с сервера...
                      bullet.idd=idd_bullet;
                      bullet.direction=direction_bullet;
                      bullet.speed=speed_bullet;
                  }
              }
            break;
            case 4://отвечает за показ всех игроков, когда мы подключились к серверу...
              players=instance_create(x,y,obj_player);
              players.idd=buffer_read( buffer , buffer_s32 );
              players.image_index=buffer_read( buffer , buffer_u16 );
              players.hp=buffer_read( buffer , buffer_u16 );
            break;
            case 5: //отвечает за показ нашего игрока у других игроков, когда мы подключились к серверу...
              var idd_enemy_player=buffer_read( buffer , buffer_s32 );
              if(idd_enemy_player!=global.myid){//сделано для того что бы мы не появились у самих себя
                  enemy_players=instance_create(x,y,obj_player);
                  enemy_players.idd=idd_enemy_player;
                  enemy_players.image_index=buffer_read( buffer , buffer_u16 );
                  enemy_players.hp=buffer_read( buffer , buffer_u16 );
              }
            break;
            case 6://отвечает за изменение жизней, всё просто, попробуйте сами понять.
              var idd_player=buffer_read( buffer , buffer_s32 );
              var hp_player=buffer_read( buffer , buffer_u16 );
              with(obj_player){
                  if(idd=idd_player){
                      hp=hp_player;
                  }
              }
            break;
         }


Вроде всё, запускаем и тестируем. Если есть предложения или ошибки, или недочёты, или неточности... Пишем! Буду не сильно рад, но всё исправлю.:}
Ссылка на основной сайт с моей статьёй.


http://forum.hellroom.ru/index.php?topic=21720
Мультиплеер GMS(Game Maker Studio networking) туториал на русском создание игры:}


Сообщение отредактировал mehanic - Вторник, 28 Июля 2015, 20:31
  • Страница 1 из 1
  • 1
Поиск:

Все права сохранены. GcUp.ru © 2008-2024 Рейтинг