Nastavení multiplayerové hry pomocí Socket.io
Část 1:
Preferované předpoklady
V tomto tutoriálu se zaměříme na to, jak nastavit server a připojit vaši hru k němu, abyste mohli aktivovat funkce více hráčů pro váš projekt pomocí metod Socket.io. Dále se budeme zabývat pokročilými funkcemi a metodami v aplikaci hyperPad a silně doporučujeme nejprve si prostudovat několik dalších tutoriálů a mít dobrý pocit z tohoto softwaru, než budete pokračovat.
Tento tutoriál předpokládá dobrou znalost základní funkčnosti hyperPadu a minimální znalost psaného programování, protože se budeme soustředit na skriptování v Javascriptu pro vytvoření herního serveru. Tento tutoriál také předpokládá základní znalosti síťování a rozdílů mezi serverem a jeho klienty.
Hlavním cílem tohoto tutoriálu je naučit vás, co je potřeba udělat, abyste přidali funkci více hráčů do vaší hry; nikoli vytvořit samotnou hru. Prozkoumáme vztah mezi klienty Socket.io a serverem a jak si navzájem komunikují pomocí metod Socket.io.
Požadavky
Pro vytvoření a hostování serveru budete potřebovat počítač schopný spustit Node.JS (Mac, Windows atd.). Instalace Node.JS a jeho nastavení pro naše účely bude pokryto v tomto tutoriálu. Pokud hostujete server v domácí síti, pravděpodobně budete muset změnit port-forwarding nastavení vaší brány, pokud chcete mít připojení zvenčí vaší místní sítě.
Pro tento tutoriál jsme již připravili jednoduchou demo hru. V ní hrají dva hráči hru na naháněnou v malé bludišti. Prozkoumáme, jak jsou použity metody Socket.io v příkladu projektu, takže je doporučeno si projekt stáhnout a otevřít ho v aplikaci hyperPad.
Dokončený příklad projektu hyperPad si můžete stáhnout zde: Multiplayer tag tutorial.tap
Přehled
V tomto tutoriálu projdeme základy vytváření serveru pro vaši hru. Server se postará o většinu podrobností ve hře, jako jsou skóre, herní lobby atd. Poté vytvoříme metody, které odešlou informace z naší hry na server a naopak.
Zde je obecný průběh naší hry:
1. Z hlavního menu se připojíme k našemu serveru a dovolíme uživateli rozhodnout se vytvořit nebo se připojit k hře.
a. Pokud vytváří hru, načte se čekárna.
b. Pokud se připojuje k hře, načte se seznam dostupných herních pokojů.
c. Jakmile jsou dva hráči v čekárně, hra se spustí.
2. Ve hře server náhodně umístí hráče do jedné ze čtyř oblastí a přidělí jednomu z nich status „To je“.
a. Označení jiného hráče randomizuje jejich umístění, vymění status To a přidá bod hráči, který označil druhého.
b. Pokud během určitého časového úseku nedojde k žádnému označení, server odečte 1 bod od hráče, který je aktuálně To a vymění status To, než znovu náhodně umístí hráče do spawn zón.
3. Jakmile hráč dosáhne určitého počtu bodů, načte se překryvná vrstva, která vyhlásí vítěze. Poté se hráči odpojí z místnosti a vrátí se zpět do hlavního menu.
Část 2: Nastavení serveru
Vytvoření serveru Socket.io vyžaduje spustit aplikaci Javascript pomocí knihovny Socket.io, která naslouchá připojením hráčů. Socket.io je Javascriptová síťová knihovna, která zjednodušuje mnoho vnitřních procesů při vytváření síťových aplikací pro nás. Další podrobnosti budou následovat.
Stáhnout a nainstalovat Node.JS
Abychom začali, přejděte na Node.JS a stáhněte ho do počítače (Mac, Windows atd.), na kterém chcete server hostovat. Po dokončení stahování spusťte instalátor a postupujte podle jeho pokynů. Všechny možnosti během instalace mohou zůstat ve výchozím nastavení. Node.JS nám dává schopnost spouštět Javascriptové aplikace samostatně bez potřeby webového prohlížeče.
Stahování a spuštění příkladu serveru
Dále si stáhněte tento tutoriál multiplayer server example na GitHubu:
https://github.com/hyperPad/multiplayerServerExample
Klikněte na tlačítko "Clone or download" a vyberte "Download ZIP". To stáhne kopii kódu pro příklad serveru.
Rozbalte stažený ZIP. Uvnitř naleznete několik malých souborů, ale nejvýznamnějším souborem zde je soubor "index.js", který obsahuje kód pro náš server. Nyní otevřete příkazový řádek/terminál uvnitř složky s příkladem serveru.
Napište "npm install" a stiskněte enter, poté nechte proces probíhat. Npm je správce balíčků, který čte soubor package.json a stahuje potřebné balíčky pro náš server. (Včetně Socket.io!)
Jakmile je příkaz ukončen, máme vše, co potřebujeme pro běh serveru. V terminálu zadejte "node ." a server se spustí.
To je vše! Náš server nyní naslouchá na portu 3000 pro příchozí Socket.io připojení.
"node ." spouští Node.JS pro aktuální adresář, kde hledá adresářový indexový soubor a spustí ho. Ve výchozím nastavení je to soubor Javascript "index.js", který obsahuje většinu kódu našeho serveru a bude analyzován během tohoto tutoriálu.
Nechte terminál otevřený, protože jeho zavřením se server také zavře. Uvidíte, jak se při připojení nebo odpojení hráčů a při vytváření nebo ničení místností tisknou zprávy a další události.
Poznámka: Pro povolení příchozích připojení zvenčí domácí sítě bude pravděpodobně nutné otevřít port 3000 na vaší domácí bráně. Tento proces se liší od sítě k síti, ale návod obvykle můžete nalézt hledáním port-forwarding návodu pro modem/router vaší domácnosti. Možná budete muset zavřít a restartovat server Node.JS, když změníte nastavení port-forwarding.
Část 3: Připojení k serveru
Jakmile máte základy serveru nastaveny, můžeme se k němu připojit v hře hyperPad. To by mělo být nastaveno jako první věc na vašem projektu, bez ohledu na to, na které scéně právě jste (protože všechno musí komunikovat se serverem). Je nejlepší připojit tuto metodu k objektu na globální vrstvě, aby se použila ve všech scénách.
V příkladu projektu obsahuje globální vrstva štítek "Server" výše uvedený obsah. (Stáhnout projekt lze nalézt v části 1 tohoto tutoriálu v sekci Požadavky.)
Tyto dvě metody jsou vlastně všechno, co potřebujete k připojení k serveru. Nejprve v záložce vlastní tabulky uchopte metodu Socket.io Client a umístěte ji dolů. Zde zadáte URL adresu vašeho serveru pod kartu URL v okně vlastností. Na výše uvedeném obrázku byla URL našeho serveru na "http://192.168.0.191:3000", včetně protokolu a portu. Chcete-li, změňte to na URL vašeho serveru, jinak to pravděpodobně nebude fungovat, když hru spustíte.
Nyní máme informace o serveru, které chceme, ale stále se k němu musíme připojit. Takže potřebujeme další metodu Connect to Socket. Umístěte ji dolů, otevřete její vlastnosti a vyberte klienta v prázdném boxu a nastavte vlastnost funkce na "Connect".
Nyní, když se náš projekt načte, automaticky se připojíme k našemu serveru.
Část 4: Vytváření a připojování k místnostem
Dalším krokem je vytvoření našich herních lobby, kde se hráči mohou připojit a hrát spolu.
Zde je naše jednoduchá obrazovka hlavního menu. Vše, co je potřeba udělat, je klepnout na jedno z tlačítek, aby se buď spustila místnost, nebo vyhledaly dostupné místnosti.
Vytváření místností
Pojďme zkontrolovat, jak komunikujeme se serverem pro vytvoření naší místnosti.
Docela jednoduché, že? Abych to rychle shrnul, jakmile stiskneme naše tlačítko, budeme vyzváni k napsání názvu místnosti. Jakmile to dokončíme, tento název je odeslán serveru, kde je místnost vytvořena a my načteme scénu čekárny.
Od tohoto okamžiku budeme často používat Emit to Socket. To je naše hlavní cesta k odesílání dat na server. Myslete na to jednoduše jako na "online verzi" odeslané zprávy a přijímané zprávy (Emit to Socket provádí obě akce najednou).
Nyní musíme přidat některé informace na náš server, aby vytvořil místnost, jakmile obdrží zprávu z naší metody Emit.
socket.on('createRoom', (roomName, callback) => {
Tento řádek vytvoří událost socketu (jako výraz lambda) na serveru, který naslouchá Emitům s událostí 'createRoom'.
První parametr je hodnota, kterou předáváme skrze metodu Emit to Socket, v tomto případě název místnosti, který uživatel zadal. Zde jsme tento parametr pojmenovali 'roomName'.
Druhý parametr je funkce, kterou později zavoláme v události socketu, abychom signalizovali klienta. Metoda Emit to Socket pokračuje pouze v provádění, pokud je funkce zpětného volání (callback) na serveru zavolána. Zde jsme tento parametr pojmenovali 'callback'.
const room = {
id: uuid(),
name: roomName,
sockets: []
};
Toto vytvoří instanci struktury, která bude obsahovat základní informace o místnosti.
'id' bude unikátní identifikátor vygenerovaný pomocí utilitární funkce 'uuid()'. To nám pomůže specificky identifikovat tuto místnost proti seznamu mnoha dalších vytvořených místností později.
'name' bude nastaven na název, který uživatel předtím zadal.
'sockets' bude inicializován jako prázdné pole. Později bude sledovat sockety hráčů připojené k této místnosti.
rooms[room.id] = room;
'rooms' je globální seznam místností, které jsou aktuálně aktivní. Protože vytváříme novou místnost, uložíme ji do seznamu podle jejího ID.
joinRoom(socket, room);
Tímto vyvoláváme globální funkci 'joinRoom' (řádek 29), která přidá socket hráče do pole 'sockets' místnosti. Protože hráč vytvořil místnost, znamená to, že se do ní také připojí.
callback();
});
Na závěr zavoláme funkci zpětného volání, aby klient dostal zprávu o tom, že místnost byla vytvořena. To umožní metodě Emit to Socket pokračovat v provádění, což načte scénu čekárny jako příští. Tento úkon také označuje konec události socketu 'createRoom'.
Připojování k místnostem
Připojení k místnostem je trochu odlišné, protože budeme potřebovat přístup k informacím z jiné scény, než je tlačítko.
Pro připojení k místnostem umístíme začátek našich metod na naši globální vrstvu spolu s místem, kde se připojujeme k serveru. Tímto způsobem můžeme přistupovat k informacím z jakékoli scény, konkrétně ze seznamu místností. Pro tlačítko, pokud na něj klepneme, jednoduše načte hráče do scény seznamu místností.
Zde máme naši druhou metodu pro komunikaci se serverem. Socket Event můžeme považovat za Přijmout zprávu, protože se aktivuje pouze tehdy, když je z serveru vyslána nastavena zpráva.
Nejlepší způsob, jak přemýšlet při použití Socket Event a Emit to Socket, je ten, že Socket Event reaguje pouze na informace přicházející ze serveru, zatímco Emit to Socket je volán v reakci na věci dělané lokálně.
Pokud jde o naši logiku, jakmile se připojíme k serveru, vyšleme požadavek 'getRoomNames', abychom získali jakékoli dostupné názvy místností.
Poté nastavíme štítek, na který může hráč klepnout pro vstup.
Toto je naše scéna Seznam místností. Zde se načtou a zobrazí jakékoli dostupné herní místnosti. Pouhým klepnutím na název místnosti se hráč připojí. Díky našim předchozím metodám se seznam automaticky načte jakékoli otevřené místnosti a na obrazovce se vytvoří štítky názvů místností. Pokud se nic neobjeví, máme tlačítko Obnovit seznam, které jednoduše opakuje metodiku, aby je znovu načetlo.
Zde máme logiku pro nastavení seznamu. Nechceme zabíhat do přílišného detailu, protože pouze chceme vědět, jak to souvisí s naším serverem.
Na začátek máme naši metodu Emit to Socket. Ta volá server o zaslání informací o dostupných místnostech. Odtud máme chování Get Array Value. Všechny údaje, které přicházejí ze serveru, budou zasílány jako pole a potřebné informace budou na prvních hodnotách. Takže nastavíme naši Get Array Value, abychom získali hodnotu na indexu 0. Odtud naše metody extrahují tyto údaje a vytvářejí štítek pro každou místnost, což je vygeneruje na naše obrazovce.
Další, musíme zkontrolovat objekt, na který se potřebujeme dotknout, v naše scéně je to štítek s názvem Room Name.
Tento text slouží jako naše tlačítko při jeho vytvoření, ale stále musíme připojit ID místnosti, k níž se chceme připojit. Chcete-li to udělat, nejprve musíme získat ID místnosti a k tomu použijeme chování Get Attribute a nastavíme ho na dynamické. Poté, co jsme vyjádřili touhu připojit se k této místnosti a načíst se do čekárny, pomocí emitování na server.
socket.on('joinRoom', (roomId, callback) => {
Toto je vstupní bod pro socket události 'joinRoom'. První parametr bude hodnota ID místnosti, kterou se chceme připojit, která byla emitována na server. Zde jsme parametr pojmenovali 'roomId'.
const room = rooms[roomId];
joinRoom(socket, room);
Pomocí roomId poskytnutého klientem můžeme najít správnou instanci místnosti na serveru. Tímto způsobem vyvoláme globální funkci 'joinRoom' (řádek 29) se socketem hráče, který se chce připojit, a s instancí místnosti. Na chvíli se podíváme na funkci 'joinRoom'.
callback();
});
Na závěr vyvoláme funkci zpětného volání, abychom upozornili klienta na pokračování načítání scény čekárny, což označuje konec události socketu 'joinRoom'.
Co se tedy děje ve funkci 'joinRoom'? Pojďme na to.
room.socket.push(socket);
Jak bylo zmíněno, člen pole 'room.socket' sleduje sockety připojené v místnosti. Tento řádek to dělá tím, že do pole vkládá socket.
socket.join(room.id, () => {
socket.roomId = room.id;
console.log(socket.id, "Připojeno", room.id);
});
Toto je oficiální volání pro připojení klienta k místnosti. Nejprve řekneme socketu, aby připojil k místnosti podle ID. Když to bude dokončeno, vyvolá se následující zpětná vazba, kde připojíme ID místnosti k socketu. Nakonec zapisujeme do konzole, že se hráč připojil k místnosti!
Čekárna
Čekárna je prostě scéna, kterou načítáme hráče do, zatímco čekají na další hráče, aby se připojili, nebo na začátek hry.
Po vstupu do místnosti, odešleme serveru zprávu 'ready'. Jakmile server obdrží tyto dvě zprávy, odešle zprávu, že začíná hra ('initGame'). Tu zprávu zachytí naše Socket Event a tím načteme naši herní úroveň. Co se týče našich metod, to je v podstatě vše. Můžete přidat tlačítko, které vás odpojí od místnosti a pošle vás zpět do hlavního menu, pokud chcete.
Na serverové straně se podívejme na kód, abychom zjistili, co se tam děje.
Toto je událost 'ready', která se vyvolá, když se klient připojí k místnosti a je připraven se připojit.
const room = rooms[socket.roomId];
Protože jsme k socketu připojili ID místnosti, jsme schopni získat místnost, abychom zkontrolovali, zda je hra schopna začít.
if (room.sockets.length == 2) {
Zde zkontrolujeme, zda již čekají dva hráči v místnosti. Řekněme, že to nyní platí, a pokračujeme v zahájení hry.
for (const client of room.sockets) {
client.emit('initGame');
}
Jakmile jsou ve hře dva hráči, projdeme každý socket a emitujeme událost 'initGame', takže každý klient načte scénu úrovně, jak bylo ukázáno dříve.
Část 5: Hratelnost
Nyní se dostáváme k jádru věcí. Zde je úroveň naší hry, kterou jsme navrhli pro tento tutoriál.
Předtím, než se do toho pustíme, máme štítek s názvem "Herní logika", podívejme se na to.
Páni, to je hodně metod! Nebojte se, toto je jen způsob, jakým vkládáme naše hráče do hry. Podívejme se blíže;
Začínáme metodou Emit to Socket, která říká serveru, že naše hra začala, pomocí události 'startGame'. Poté zachytíme pole, které server vrátí, vezmeme jeho první hodnotu a pomocí metody Get Dictionary Value získáme různé atributy, které naše objekty budou potřebovat. Samostatný strom dělá totéž, pouze pro protihráče.
Nakonec vysíláme zprávu 'init' na náš objekt hráče, abychom vše spustili.
Pojďme se podívat, co se děje na serverové straně, když vysíláme událost 'startGame'.
První polovina této socketové události, kterou zde vidíte, nastavuje některé počáteční hodnoty na každém klientovi, a poté přidává jakéhokoli klienta, který není vyřizujícím klientem, do místního pole 'others'.
Ve druhé polovině se vytvoří místní seznam na základě slovníku s názvem 'ack'. Obsahuje informace o nás samých a o ostatních klientech. Poté vracíme tyto informace klientovi tím, že předáme náš slovník 'ack' funkci zpětného volání, která se stane výslednou hodnotou volající metody Emit to Socket.
Nakonec bude provedeno volání s časovým limitem na 5 sekund, aby nakonec začal kolo, jakmile budou mít všichni všechny potřebné informace ke hře. Funkce 'beginRound' (řádek 99) ovládá určitou herní logiku pro tento projekt. Nebudeme zabíhat do přílišného detailu, ale v podstatě to kontroluje, kde se hráči vyskytují, kontroly skóre, a také říká klientům, kdo je To.
Jak bylo zmíněno, zpráva 'init' se vyvolává na našem štítku "Herní logika", jakmile je všechno připraveno. Nyní se podíváme na chování na našem objektu hráče, kde přijímá zprávu 'init'.
Zde vidíte, že máme několik stromů chování na našem hráči. Nejprve se podívejme na strom v levém horním rohu.
Toto je chování, které prakticky spouští vše ostatní ve hře.
Nejprve obdržíme zprávu 'init', která byla odeslána ze štítku "Herní logika". Odtud získáme server ID našeho objektu a aktivujeme jeden z našich Socket Events, stejně jako nastavíme obrazovku naší hry, abychom mohli přesně sledovat naši postavu.
Synchronizace pohybu
Zde je jedno z našich nejdůležitějších chování. Tento malý strom je navržen tak, aby aktualizoval pozici našeho hráče na serveru pokaždé, když se posuneme joystickem. Pravděpodobně jste si také všimli, že odkazuje na některé hodnoty slovníků. Tyto hodnoty získáváme z samostatného chování slovníku, které obsahuje X a Y pozice našeho hráče.
Pojďme se podívat na událost 'moved' na serveru.
data = JSON.parse(data);
Slovníky, které jsou odesílány z klienta na server, musí být parsovány, abychom mohli snadno číst data. To proto, že slovníky v hyperPad jsou zakódovány do JSON struktury při odesílání na server. Tento řádek analyzuje JSON řetězcovou strukturu a ukládá seznam slovníku zpět do stejné místní proměnné 'data'.
socket.x = data.x;
socket.y = data.y;
Zde aktualizujeme X a Y pozice uložené na socketu s novými hodnotami, které poskytl klient.
for (const client of room.sockets) {
if (client == socket) {
continue;
}
client.emit(socket.id, {
x: socket.x,
y: socket.y,
score: socket.score,
isIt: socket.isIt
});
}
Následně procházíme všechny klienty, abychom je aktualizovali o naší nové pozici a dalších detailech, kromě nás samých (tj. Emitující klient nepotřebuje znát svou vlastní pozici).
Tento strom zde ovládá většinu našeho herního mechanismu. Používame Socket Event, když server zkoumá, který hráč je označován jako To, což je emitováno 'beginRound' (řádek 99) na serveru.
Poté získáváme server ID našeho objektu ze seznamu a používáme hodnoty slovníku na rozebrání různých fragmentů dat, které obsahuje. Odtud získáváme skóre, zda jsme označováni jako To, stejně jako x a y pozice našeho objektu, a pak je aplikujeme na atributy objektu. Zbytek chování je pro nastavení a ovládání uživatelského rozhraní v naší hře.
Závěr
Bylo zde mnoho informací, ale pokud jste se dostali až sem, měli byste nyní mít nějaké povědomí o tom, jak využít metody Socket.io k vytvoření herní zkušenosti pro více hráčů pro vaše hráče.
Vyzkoušejte to sami! Vezměte si již existující hru, kterou jste vytvořili, a zkuste jí dát nějakou online funkčnost, jako je scéna s tabulkou nejlepších skóre, která se připojí k serveru a požádá o seznam nejlepších 10 skóre hráčů k zobrazení.
Je těžké naučit skriptovací jazyk, jako je Javascript, v jednom článku. Naštěstí, pokud máte potíže, existuje mnoho dalších zdrojů, které vám pomohou s psaním Javascript aplikací pro Node.JS a Socket.io;
Naučte se Javascript - https://developer.mozilla.org/bm/docs/Web/JavaScript
Naučte se Socket.io - https://socket.io/docs/
Naučte se Node.JS - https://nodejs.org/en/docs/

