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 пишем: И в Step к этому объекту так же дописываем: Код image_angle=direction В Draw к этому объекту переносим иконку из вкладки draw под названием Draw Self, не забываем что это мы делаем и в клиенте, и в сервере, и так же пишем код:
Теперь спасибо пользователю 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; }
Хух, с сервером разобрались... Теперь приступаем к клиенту... Там тоже есть такой скриптик и он тоже объёмный...
Открываем в КЛИЕНТ скрипт 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 |
|
| |