Něco teorie
Než začneme programovat, musíte vědět jak multiplayer v GM funguje. Nejdříve někdo musí vytvořit session, na kterou se připojí druhý hráč a mezi sebou si potom posílají proměnné - souřadnice apod. Příkazy pro multiplayer začínají mplay_. Jsou dva způsoby, jak odesílat a přijímat, multiplayerové data a zprávy. Data můžou číst všichni, zprávy se můžou odeslat všem, ale i jednomu hráči.
Připojení
Nejdříve musíme inicializovat připojení. Můžete si vybrat mezi IPX, TCP/IP, vytáčené připojení nebo řadové (?). Nejlepší je vybrat TCP/IP, vytáčené připojení by bylo dobré před několika lety, ale možná se ještě někdo s vytáčeným připojením najde :). Každopádně, inicializace TCP/IP připojení se dělá takto:
Do uvozovek můžete doplnit IP adresu, pokud nic nedoplníte, bude hra vyhledávat session v LAN síti. Nyní máme inicializováno připojení, při ukončení hry ho musíme ukončit, aby nedošlo k chybě:
Session
Pokud máte server a chcete na něm začít hru, musíte vytvořit session. Když se hráč bude chtít ke hře přidat, musí se do session připojit. Jak vyhledat session? V Create (a můžete i do alarmu který nastavte aby se opakoval, bude se tak vyhledávání opakovat a nemusíte restartovat hru):
A do Drawu:
draw_text(1,1+(16*a),mplay_session_name(a));
}
V eventu Create se zjistí počet nalezených session (pro vyhledání nebo připojení MUSÍTE funkci mplay_session_find() volat, i když nic nevykreslujete) a v Drawu se vykreslí jejich jména pod sebe. Pro vytvoření session použijte:
Zobrazí se okno kam zadáte svou přezdívku, kterou budete mít ve hře. 6 je maximální počet hráčů, kteří v session můžou být připojeni (když napíšete 0, je to neomezený počet hráčů, ale raději to omezte, GM velkou zátěž nezvládne). Pro připojení k session slouží:
Opět se zobrazí okno kam zadáte přezdívku. 2 je ID session, první nalezená session je 0, druhá 1... Jak nejlépe zjistit ID uvidíte v ukázkovém příkladu. Při ukončení hry nebo návratu do menu s výběrem session k připojení musíte session ukončit, takto:
Při zakládání session ji můžete ještě nastavit, jestli se při odchodu hráče, který session založil má dát jako zakládající hráč jiný hráč:
Místo 1 můžete napsat 0, true nebo false. True nebo 1 pokud chcete přeložit zakládajícího hráče na někoho jiného, false nebo 0 pokud ne (pokud nechcete přeložení zakládajícího hráče, nemusíte tento příkaz psát vůbec, false (0) je původní nastavení). Jak zjistit, jestli jsme session založili, připojili se k nějaké nebo jsme zatím neudělali nic?
Vrací 0, 1 nebo 2. 0 pokud nic, 1 pokud jste session vytvořili a 2 pokud jste se k nějaké připojili.
Hráči
Po připojení do hry můžete zjistit počet, jména a ID hráčů (na ID odeesíláte zprávy). Nejdříve musíte provést příkaz:
A pro vykreslení všech hráčů do Drawu:
draw_text(1,1+(16*a),mplay_player_name(a));
}
Funkce mplay_player_name() slouží na zjištění jména hráče, mplay_player_id() na ID hráče. Argument obou funkcí je číslo hráče.
Data
Posílání dat je jednoduché, každé data mají svoje ID, do kterého se zapíše text nebo číslo. Ostatní hráči můžou data číst. ID je od 0 do 1000000 (10 milionů). Zde jsou příkazy, které budete potřebovat:
mplay_data_read(ID); /*vrací obsah ID, pokud nic do ID není vloženo, vrací 0*/
Přesněji to uvidíte v ukázkovém příkladu.
Zprávy
Druhou možností je odesílání zpráv, buď jednomu nebo všem hráčům. Každá má své vlastní ID. Pro odeslání zprávy slouží:
mplay_message_send_guaranteed(HRAC,ID,OBSAH);
HRAC je jméno nebo ID hráče, ID (druhý argument) je identifikační číslo zprávy a místo OBSAH napište obsah zprávy, buď text nebo číslo. Druhý příkaz (mplay_message_send_guaranteed) zajišťuje, že zpráva dorazí, ale je pomalejší než první příkaz, ale pokud nepošlete zprávu v garantovaném módu, může se zpráva vytratit (je to nepravděpodobné, ale stát se to může). Pro přijetí zprávy použijte:
Přijme zprávu od hráče, místo HRAC napište ID hráče, jméno nebo 0 pro všechny hráče.
Zde jsou ostatní příkazy, před jejich použitím musíte zprávu přijat:
mplay_message_value(); /*vrací hodnotu poslední přijaté zprávy, po přijetí zprávy musíte tuto funkci použít pro zjištění jejího obsahu*/
mplay_message_player(); /*zjistí ID hráče, který odeslal poslední přijatou zprávu*/
mplay_message_name(); /*zjístí jméno hráče, který odeslal poslední přijatou zprávu*/
Bohužel jsme nepřišel na to, co dělají funkce mplay_message_count() a mplay_message_clear() (argumentem u obou funkcí je jméno nebo ID hráče), ještě jsem je nepoužil ale můžete je zkusit, budu rád když do komentářů napíšete jejich funkci ;). Teorie končí, pokud něco nechápete, podívejte se na ukázkový příklad, ke každému eventu je skript a popis jeho funkcí.
Ukázka
Session
Vytvoříme místnost room_load a objekt control. Nejdříve si uděláme tabulku, kde budou vypsány všechny session a tlačítko na vytvoření. Začneme s výpisem. Musíme vytvořit připojení TCP/IP a zjistit počet session - budeme ho zjišťovat každých 5 sekund, proto si nastavíme alarm na 5 sekund a do eventu Game Start vložíme:
Mezi uvozovky můžete doplnit IP adresu, když nic nevyplníte, budou se vyhledávat session na LAN síti. A nyní do create, ale i do alarmu:
Do alarmu ještě dáme jeho opakování, ať se počet kontroluje každých pět sekund a ne dvakrát (1x v create a 1x v alarmu).
V eventu create ještě definujeme proměnnou global.host na hodnotu false, proč to uděláme uvidíte později. Nyní přejdeme k vykreslování, využijeme k tomu cyklus for:
if mouse_y>1+(a*14) and mouse_y<15+(a*14){
draw_set_color(c_maroon);
}
else{
draw_set_color(c_black);
}
draw_text(1,1+(a*14),mplay_session_name(a));
}
draw_set_color(c_black);
if num=0{
draw_text(1,1,"Nenalezeny žádné session");
}
Nejdříve provedeme cyklus, ve kterém zjistíme, jestli je kurzor na vykresleném textu a podle toho nastavíme barvu, ať můžeme poznat kterou session vybíráme. Potom napíšeme jméno session. Pro jistotu, doporučuji před to vložit draw_set_font(font); a ve fontu nazvaném font klikněte na All, čímž nastavíte, že se budou zobrazovat všechny znaky, takže i háčky a čárky (záleží ale i na fontu, musí je podporovat). Nakonec, po ukončení cyklu máme podmínku, jestli neexistuje žádná session, a pokud doopravdy neexistuje, napíšeme text. Ale to snad chápete, když tvoříte multiplayerovou hru :).
Nyní tlačítko na vytvoření session. Vykreslíme si tmavý obdélníček s textem, naco se dělat se spritem, ne? :).
draw_rectangle(2,room_height-24,130,room_height-2,1);
if mouse_x>=2 and mouse_x<=130 and mouse_y>=room_height-24 and mouse_y<=room_height-4{
draw_set_color(c_maroon);
}
else{
draw_set_color(c_black);
}
draw_set_valign(fa_center);
draw_set_halign(fa_center);
draw_text(66,room_height-14,"Vytvořit");
draw_set_valign(fa_top);
draw_set_halign(fa_left);
Nejdříve nastavíme barvu, vykreslíme obdélník a pak zjistíme jestli je někde v jeho ploše kurzor, pokud ano tak obarvíme text na tmavě červenou, pokud ne, text bude černý. A nakonec text vykreslíme doprostřed obdélníku, museli jsme nastavit aby se vykreslování vycentrovalo ať to máme jednodušší :).
Nyní musíme zjistit pozici kurzoru při kliknutí, vytvoříme si k tomu event Step a vložíme do něj kód:
for(a=0; a<num; a+=1){
if mouse_y>1+(a*14) and mouse_y<15+(a*14){
mplay_session_join(a,"PLAYER");
room=room_game;
}
}
if mouse_x>=2; and mouse_x<=130 and mouse_y>=room_height-24 and mouse_y<=room_height-4{
mplay_session_create(get_string("Zadej název session",""),2,"HOST");
global.host=true;
room=room_game;
}
}
Abychom ušetřili procesor, provede se zjištění pozice až po kliknutí levým tlačítkem myši. Následuje cyklus, kterým projdeme všechny session a pokud jsme na některou z nich klikli, připojíme se na ni a přejdeme do místnosti room_game, ve které budeme hrát. Po skončení cyklu se provede kontrola, jestli je kurzor v obdélníku pro vytvoření session, pokud ano tak se zobrazí okno kam napíšeme název session, já volím název "hřiby" ale můžete si vymyslet vlastní :D. Nakonec vytvoříme session s limitem 2 hráčů a jménem hostovacího hráče HOST, ten který se připojuje k session dostane jméno PLAYER, protože víc než 2 lidi v této ukázce hrát nemohou (samozřejmě když si to vytvoříte aby hrálo víc lidí, musíte jim rozdělit jména tak aby 2 hráči neměli jména stejné, nebo je nechce ať si je zvolí sami). A nakonec vytvoříme event Game End, do kterého vložíme:
Plošinovka
Jako příklad vytvoříme plošinovku. Vytvoříme místnost room_game a vytvoříme si level. Já jsem vytvořil tento z tilesetů a udělal si kolizní objekty:
Také jsem si vytvořil jednoduché sprity pro oba hráče a kulky (1 sprit pro oba druhy), které po sobě hráči budou střílet. Sprity pro hráče i kulky jsem vycentroval.
Vytvoříme si skript, který nazveme ResetData, resetuje všechny použité multiplayerové data na 0, ať při nové session při ukončení staré nevznikne chyba. Bylo by jednodušší projet cyklem všechno od 0 až do 100, ale bylo by to časově náročnější, tak zapíšeme čísla do dočasné proměnné s poly (array) a cyklem for data změníme:
a[0]=10;
a[1]=11;
a[2]=20;
a[3]=21;
a[4]=29;
a[5]=30;
a[6]=31;
a[7]=39;
a[8]=40;
a[9]=41;
a[10]=80;
a[11]=81;
a[12]=82;
for(b=0; b<13; b+=1){
mplay_data_write(a[b],0);
}
Nyní vytvoříme 4 objekty, pojmenujeme je hrac_1, hrac_2, kulka_1 a kulka_2. Objektům kulka_1 a kulka_2 nastavíme depth (hloubku) na 1.
Otevřeme objekt hrac_1 a vytvoříme eventy Create, Step, Collision (s podlahou, zdmi apod.), Collision (s kulka_2), Game End, Room End a Draw.
Nyní vkládáme kódy do eventů - Create:
dr=1;
screen_redraw();
}
ResetData();
dr=0;
if (global.host){
gravity_direction=270;
mplay_data_write(80,100);
}
Nejdříve zjistíme počet hráčů, nemůžeme hrát bez druhého hráče tak se cyklus opakuje než se druhý hráč připojí. Dokud se druhý hráč nepřipojí, vykresluje se text (to uvidíte v eventu Draw, proto proměnná dr). Až se druhý hráč připojí, zkontroluje se jestli je proměnná global.host nastavena na true, kdyby ne, byl by obsah podmínky zbytečný. Přejdeme ke stepu, kde je trochu delší kód, začátečníci by se v něm mohli ztratit :).
if place_free(x,y+1){
gravity=0.34;
}
else{
gravity=0;
vspeed=0;
}
if keyboard_check(vk_left) and place_free(x-4,y){
x-=4;
}
if keyboard_check(vk_right) and place_free(x+4,y){
x+=4;
}
if keyboard_check_pressed(vk_up) and !place_free(x,y+1){
vspeed=-8;
}
if vspeed>8{
vspeed=8;
}
if mouse_check_button_pressed(mb_left) and instance_number(kulka_2)=0{
a=instance_create(x,y,kulka_2);
mplay_data_write(39,1);
a.direction=point_direction(x,y,mouse_x,mouse_y);
a.speed=12;
}
hrac_1.x=real(mplay_data_read(10));
hrac_1.y=real(mplay_data_read(11));
mplay_data_write(20,x);
mplay_data_write(21,y);
if mplay_data_read(29)=1{
var xx,yy;
xx=real(mplay_data_read(30));
yy=real(mplay_data_read(31));
if instance_number(kulka_1)=0{
instance_create(xx,yy,kulka_1);
}
kulka_1.x=xx;
kulka_1.y=yy;
}
else{
if instance_number(kulka_1)>0{
with(kulka_1){ instance_destroy(); }
}
}
if mplay_data_read(39)=0{
if instance_number(kulka_2)>0{
with(kulka_2){ instance_destroy(); }
}
}
if mplay_data_read(99)=1{
show_message("Druhý hráč ukončil hru!");
room=room_load;
}
if real(mplay_data_read(80))<=0{
screen_redraw();
show_message("Gratuluji, porazil jsi druhého hráče!");
room=room_load;
}
if hlth<=0{
screen_redraw();
show_message("Prohrál jsi.");
room=room_load;
}
}
Nejdříve zkontrolujeme, jestli je global.host nastavena na true, jinak by hráč ovládal obě postavy. Pokud ano, kontrolujeme volné místo pod hráčem, děláme plošinovku a tak se hráč nesmí propadnout. Potom zjištění kláves na pohyb, limitování maximální rychlosti skoku nebo padání a nakonec střílení kulek. A konečně část s odesíláním.
Ve hře pro 2 hráče je v podstatě jedno, kterou možnost (data/zprávy) si zvolíme, já jsem zvolil zápis a čtení dat. Nejdříve čteme pozici druhého hráče, potom zapisujeme svou pozici, dále kontrolujeme jestli druhý hráč nevystřelil, pokud ano tak zjistíme pozici kulky, vytvoříme její instanci a pozici nastavujeme, pokud ne a kulka ještě existuje, tak ji zničíme, zjistíme i jestli existuje druhá kulka a pokud ne, tak ji zničíme taky (jinak by kulka u jednoho hráče letěla dál), dále zjišťujeme jestli druhý hráč hru neukončil, kontrolujeme zdraví soupeře a v případě prohry zobrazíme text a zapíšeme proměnnou kterou zkontrolujeme u druhého hráče.
Dostáváme se ke kolizím. Začneme kolizí s podlahou a zdmi (a i stropem). Pokud na to máte více objektů, použijte parent. Vložte do eventu:
move_contact_solid(direction,8);
vspeed=0;
}
A druhá kolize, s kulka_2:
with(kulka_2){ instance_destroy(); }
Kulku zničíme a aktualizujeme zdraví.
Dostáváme se k eventu Game End. Musíte při ukončení hry ukončit i připojení, což uděláme jednoduše takto:
A další End event, tentokrát Room End:
Resetujeme multiplayerová data a ukončíme session. Budeme kreslit, ale nebudou to dělat barvičky a štětce ale grafická karta :).
draw_set_color(c_black);
draw_set_valign(fa_center);
draw_set_halign(fa_center);
draw_text(x,y,string(mplay_data_read(80) div 10));
if dr=1{
draw_text(room_width/2,room_height/2,"ČEKÁM NA DRUHÉHO HRÁČE");
}
draw_set_valign(fa_top);
draw_set_halign(fa_left);
Aby sprite nezmizel, musíme ho vykreslit. S ním také vykreslíme zdraví do středu instance. A pokud je ona proměnná dr, kterou jsem zmiňoval v eventu Create nastavena na hodnotu 1, vykreslíme text, že hra čeká na připojení druhého hráče, jinak by hráč, který session vytvořil nemohl hrát :).
Otevřeme objekt hrac_2 a vytvoříme následující eventy: Create, Step, Collision (s podlahou, zdmi apod.), Collision (s kulka_1), Game End, Room End a Draw. Do Create:
gravity_direction=270;
mplay_data_write(81,100);
mplay_data_write(82,1);
}
Zkontrolujeme jestli je global.host na false a pokud ano, nastavíme směr gravitace a zapíšeme zdraví k multiplayerovým datům. Nyní dlouhý, předlouhý step. Jeho obsah je prakticky stejný, jen změněné ID multiplayerových dat, objekty a první podmínka:
if place_free(x,y+1){
gravity=0.34;
}
else{
gravity=0;
vspeed=0;
}
if keyboard_check(vk_left) and place_free(x-4,y){
x-=4;
}
if keyboard_check(vk_right) and place_free(x+4,y){
x+=4;
}
if keyboard_check_pressed(vk_up) and !place_free(x,y+1){
vspeed=-8;
}
if vspeed>8{
vspeed=8;
}
if mouse_check_button_pressed(mb_left) and instance_number(kulka_2)=0{
a=instance_create(x,y,kulka_2);
mplay_data_write(39,1);
a.direction=point_direction(x,y,mouse_x,mouse_y);
a.speed=12;
}
hrac_1.x=real(mplay_data_read(10));
hrac_1.y=real(mplay_data_read(11));
mplay_data_write(20,x);
mplay_data_write(21,y);
if mplay_data_read(29)=1{
var xx,yy;
xx=real(mplay_data_read(30));
yy=real(mplay_data_read(31));
if instance_number(kulka_1)=0{
instance_create(xx,yy,kulka_1);
}
kulka_1.x=xx;
kulka_1.y=yy;
}
else{
if instance_number(kulka_1)>0{
with(kulka_1){ instance_destroy(); }
}
}
if mplay_data_read(39)=0{
if instance_number(kulka_2)>0{
with(kulka_2){ instance_destroy(); }
}
}
if mplay_data_read(99)=1{
show_message("Druhý hráč ukončil hru!");
room=room_load;
}
if real(mplay_data_read(80))<=0{
screen_redraw();
show_message("Gratuluji, porazil jsi druhého hráče!");
room=room_load;
}
if hlth<=0{
screen_redraw();
show_message("Prohrál jsi.");
room=room_load;
}
}
Nyní kolize se zdmi, podlahou a stropem:
move_contact_solid(direction,8);
vspeed=0;
}
Plní stejnou funkci jako u objektu hrac_1 - zkotntroluje proměnnou global.host a zastaví hráče, aby se nepropadl. Kolize s kulka_1:
with(kulka_1){ instance_destroy(); }
Aktualizuje zdraví a zničí kulku. Obsah Game End je stejný jako u hrac_1:
A Room End skoro taky:
Ve Drawu budeme vykreslovat jen sprite a zdraví, vykreslování textu při čekání by k ničemu nebylo:
draw_set_color(c_black);
draw_set_valign(fa_center);
draw_set_halign(fa_center);
draw_text(x,y,string(mplay_data_read(81) div 10));
draw_set_valign(fa_top);
draw_set_halign(fa_left);
Vykreslíme sprite, nastavíme centrování textu a vykreslíme zdraví. Otevřete objekt kulka_1 a kulka_2 a vytvořte eventy Destroy, Step a Collision (se zdmi, podlahou apod.). Do kulka_1 do eventu Destroy vložíme:
mplay_data_write(30,-999);
mplay_data_write(31,-999);
Do Stepu:
mplay_data_write(31,y);
A do kolize:
Nyní objekt kulka_2, Destroy:
mplay_data_write(40,-999);
mplay_data_write(41,-999);
Do Stepu:
mplay_data_write(41,y);
A do kolize:
Vložte do místnosti hrac_1 a hrac_2 a hru můžete spustit.
Ke stažení
Ukázkový příklad můžete stáhnout:
GMK (GM8): KLIK
EXE: KLIK
Žvatly patly
Nerozumíte něčemu, máte nápad jak něco vylepšit, nefunguje něco, našli jste chybu...? Pište do komentářů nebo pošlete SZ přes fórum.
Komentáře
Přidat nový
Pro přispívání musíte být přihlášen
Zapsal chylex - 08.04.2010 08:42
Později bych mohl udělat trochu větší návod, s podporou více hráčů, chatem ve hře, dálkovém ovládáním serveru na kterém session poběží apod. :)
Zapsal PepiCzech - 07.04.2010 20:46
Luxusní. Konečně první použitelný návod na multiplayer v češtině. Jak urvu víkend pro sebe, tak určitě vyzkouším, už jen proto, že pár vyloženě multiplayerových projektů nosím už delší dobu v hlavě.
Zapsal velda898 - 07.04.2010 17:49
skvělej článek. Zatím sem to nakousnul a počítám že se mi to v nejbližší době bude moc hodit. :-) tak to dočtu. takže diky diky diky.
Zapsal chylex - 04.04.2010 20:43
doncek: To si taky myslím, ale nikdy jsem to nezkoušel tak nechci dávat neověřené informace :)
Zapsal doncek - 04.04.2010 19:47
funkce mplay_message_count() zřejmě vrací počet zpráv ve frontě tzn. počet "nepřijatých" zpráv a funkce mplay_message_clear() vyčistí frontu poslaných zpráv - ale nevim to jistě, hádám dle názvu...
pěkný článek
Zapsal bildo - 04.04.2010 11:34
Chylexi, super, zbezne jsem to precetl (Jeste to urcite prectu vicekrat), ale stejne to budu muset s Tebou jeste probrat pres ICQ. :). Super clanek !!!



