Konfigurowanie gry wieloosobowej za pomocą Socket.io | hyperPad Documentation

Loading...

Logo

z-f2VkDQ.jpg

Część 1: Wprowadzenie

Preferowane Wymagania Wstępne

W tym samouczku skoncentrujemy się na tym, jak skonfigurować serwer i połączyć swoją grę z nim, abyś mógł włączyć funkcje multiplayer w swoim projekcie za pomocą zachowań Socket.io. Zajmiemy się zaawansowanymi funkcjami i zachowaniami w hyperPad i mocno zaleca się najpierw zapoznać się z innymi samouczkami oraz dobrze poznać oprogramowanie przed kontynuowaniem.

Ten samouczek zakłada, że masz dobrą znajomość podstawowej funkcjonalności hyperPad, a także minimalne zrozumienie programowania tekstowego, ponieważ skoncentrujemy się na skryptach Javascript do budowy serwera gry. Zakładamy także pewne podstawowe zrozumienie sieci oraz różnice między serwerem a jego klientami.

Głównym celem tego samouczka jest nauczenie Cię, co musisz zrobić, aby dodać funkcjonalność multiplayer do swojej gry; nie tworzyć samą grę. Zbadamy związek między klientami Socket.io a serwerem oraz to, jak komunikują się ze sobą za pomocą zachowań Socket.io.

Wymagania

Aby utworzyć i hostować serwer, potrzebujesz komputera, który może uruchomić Node.JS (Mac, Windows itp.). Instalacja Node.JS oraz jego konfiguracja do naszych celów zostanie omówiona w tym samouczku. Jeśli hostujesz serwer w swojej domowej sieci, prawdopodobnie będziesz musiał zmienić ustawienia przekierowania portów swojej bramy, jeśli chcesz mieć połączenia przychodzące z zewnątrz swojej lokalnej sieci.

Dla tego samouczka stworzyliśmy już prostą grę demonstracyjną. W niej mamy dwóch graczy grających w zabawę w chowanego w małym labiryncie. Będziemy badać, jak zachowania Socket.io są używane w przykładowym projekcie, dlatego zaleca się pobranie i otwarcie projektu w hyperPad.

Wypełniony projekt przykładowy hyperPad można pobrać tutaj: Multiplayer tag tutorial.tap

Przegląd

W tym samouczku omówimy podstawy tworzenia serwera dla Twojej gry. Serwer zajmie się większością szczegółów w grze, takich jak punkty, lobby gier itp. Następnie stworzymy zachowania, które będą wysyłać informacje z naszej gry do serwera i odwrotnie.

Oto ogólny przebieg naszej gry:

1. Z głównego menu połączymy się z naszym serwerem i pozwolimy użytkownikowi zdecydować, czy stworzyć, czy dołączyć do gry.

a. Jeśli tworzysz grę, załaduj pokój oczekiwania.

b. Jeśli dołączają do gry, załaduj listę dostępnych pokoi gier.

c. Gdy dwóch graczy znajdzie się w poczekalni, gra się rozpocznie.

2. W grze serwer losowo umieści graczy w jednej z czterech stref i przypisze jednego z nich do roli "Myśliwego".

a. Oznaczenie drugiego gracza spowoduje zrandomizowanie ich lokalizacji, zamianę statusu Myśliwego oraz dodanie punktu graczowi, który oznaczył drugiego.

b. Jeśli w określonym czasie nie dojdzie do oznaczeń, serwer odejmie 1 punkt graczowi, który jest aktualnie Myśliwym i zamieni status Myśliwego przed losowym umieszczeniem graczy z powrotem w strefach startowych.

3. Gdy gracz osiągnie określoną liczbę punktów, załadujemy nakładkę ogłaszającą zwycięzcę. Następnie gracze rozłączą się z pokoju i wrócą do głównego menu.

Część 2: Konfiguracja Serwera

Tworzenie serwera Socket.io wymaga uruchomienia aplikacji Javascript przy użyciu biblioteki Socket.io, która będzie nasłuchiwać połączeń graczy. Socket.io to biblioteka sieciowa Javascript, która upraszcza wiele wewnętrznych działań przy budowie aplikacji sieciowej. Więcej szczegółów wkrótce.

Pobierz i Zainstaluj Node.JS

Aby rozpocząć, przejdź do Node.JS i pobierz na komputer (Mac, Windows itp.), na którym chcesz hostować serwer. Po zakończeniu pobierania uruchom instalator i postępuj zgodnie z jego instrukcjami. Wszystkie opcje podczas instalacji można pozostawić domyślne. Node.JS umożliwia nam uruchamianie aplikacji Javascript bez potrzeby używania przeglądarki internetowej.

Pobieranie i Uruchamianie Przykładowego Serwera

Następnie pobierz przykładowy serwer multiplayer dla tego samouczka z GitHub:

https://github.com/hyperPad/multiplayerServerExample

Kliknij przycisk "Clone or download" i wybierz "Download ZIP". To pobierze kopię kodu dla przykładu serwera.

chrome_2019-08-05_14-22-14.png

Rozpakuj pobrane archiwum ZIP. Wewnątrz znajdziesz kilka małych plików, ale najbardziej zauważalnym plikiem tutaj jest plik "index.js", który jest kodem naszego serwera. Następnie otwórz swoją linię poleceń/terminal w folderze przykładu serwera.

Wpisz "npm install" i naciśnij Enter, a następnie pozwól, aby się uruchomiło. npm to menedżer pakietów, który odczytuje plik package.json i pobiera niezbędne pakiety dla naszego serwera. (W tym Socket.io!)

cmd_2019-08-05_14-55-19.png

Gdy polecenie zostanie zakończone, mamy wszystko, co potrzebne do uruchomienia serwera. W terminalu wpisz "node ." i serwer się uruchomi.

cmd_2019-08-05_15-02-38.png

To wszystko! Nasz serwer teraz nasłuchuje połączeń Socket.io na porcie 3000.

"node ." uruchamia Node.JS dla bieżącego katalogu, w którym będzie szukać katalogowego pliku indeksowego i uruchomi go. Domyślnie plik javascript to "index.js", w którym znajduje się większość kodu naszego serwera i który będzie analizowany w tym samouczku.

Pozostaw terminal otwarty, ponieważ zamknięcie go spowoduje również zamknięcie serwera. Zobaczysz, jak będą drukowane komunikaty, gdy gracze się łączą lub rozłączają, oraz kiedy pokoje są tworzone lub niszczone i inne zdarzenia.

Uwaga: Aby umożliwić połączenia przychodzące z zewnątrz sieci domowej, najprawdopodobniej będziesz musiał otworzyć port 3000 na swojej bramie domowej. Proces ten różni się w zależności od sieci, ale zazwyczaj można znaleźć przewodnik, wyszukując zasoby w Internecie na temat przekierowywania portów dla modemu/routera w Twoim domu. Może być konieczne zamknięcie i ponowne uruchomienie serwera Node.JS, gdy zmienisz ustawienia przekierowywania portów.

Część 3: Łączenie z Serwerem

Gdy masz podstawy serwera skonfigurowane, możemy teraz połączyć się z nim w grze hyperPad. Powinno to być skonfigurowane jako pierwsza rzecz, która się zdarza w twoim projekcie, niezależnie od tego, na której scenie się znajdujesz (ponieważ wszystko musi komunikować się z serwerem). Najlepiej dołączyć to zachowanie do obiektu na Global Layer, aby miało zastosowanie we wszystkich scenach.

Untitled.jpg

W przykładowym projekcie etykieta Global Layer "Serwer" zawiera powyższe. (Pobieranie projektu można znaleźć w Części 1 tego samouczka w sekcji Wymagania.)

Te dwa zachowania są naprawdę wszystkim, co potrzebujesz, aby połączyć się z serwerem. Przede wszystkim, w zakładce niestandardowej, wybierz zachowanie Klient Socket.io i przeciągnij je w dół. Tutaj wprowadzisz adres URL swojego serwera w zakładce URL w oknie właściwości. Na powyższym obrazku, URL naszego serwera to "http://192.168.0.191:3000", w tym protokół i port. Będziesz chciał to zmienić, aby pasowało do swojego adresu URL serwera, inaczej prawdopodobnie nie zadziała, gdy rozpoczniesz grę.

Teraz mamy informacje o serwerze, które chcemy, ale musimy się z nim połączyć. Dlatego potrzebujemy tylko zachowania Połącz z Socket. Przeciągnij jedno, otwórz jego właściwości i wybierz klienta w pustym polu, a następnie ustaw właściwość Funkcja na "Połącz".

Teraz, gdy nasz projekt się załadowuje, automatycznie połączymy się z naszym serwerem.

mceclip1.png

Część 4: Tworzenie i Dołączanie do Pokoi

Następnym krokiem jest stworzenie lobby gier, w których gracze mogą dołączyć i grać razem.

mceclip2.png

Oto nasz prosty ekran Menu Głównego. Wystarczy, że klikniesz jeden z przycisków, aby rozpocząć pokój lub szukać dostępnych pokoi.

Tworzenie Pokoi

Sprawdźmy, jak komunikować się z serwerem, aby stworzyć nasz pokój.

mceclip3.png

Całkiem proste, prawda? Aby to szybko przejrzeć, gdy dotkniemy naszego przycisku, zostaniemy poproszeni o wpisanie nazwy pokoju. Gdy to zrobimy, ta nazwa jest przesyłana do serwera, gdzie pokój jest tworzony, a my wczytujemy scenę poczekalni.

Od teraz będziemy często używać Emit to Socket. To dlatego, że to nasz główny sposób przesyłania danych do serwera. Myśl o tym jako o "internetowej wersji" Broadcast Message i Receive Message (Emit to Socket wykonuje obie akcje jednocześnie).

Teraz musimy dodać informacje do naszego serwera, aby utworzył pokój, gdy otrzyma wiadomość od naszego zachowania Emit.

mceclip4.png

socket.on('createRoom', (roomName, callback) => {

Linia ta tworzy zdarzenie socket (jako wyrażenie lambda) na serwerze, nasłuchując Emitów z wydarzeniem 'createRoom'.

Pierwszy parametr to wartość, którą przesyłamy przez zachowanie Emit to Socket, w tym przypadku nazwa pokoju, którą wpisał użytkownik. Tutaj nazwaliśmy ten parametr 'roomName'.

Drugi parametr to funkcja, którą wywołujemy później w zdarzeniu socket, aby poinformować klienta. Zachowanie Emit to Socket będzie kontynuować wykonanie tylko wtedy, gdy wywołana zostanie funkcja zwrotna na serwerze. Tutaj nazwaliśmy ten parametr 'callback'.

const room = {
id: uuid(),
name: roomName,
sockets: []
};

To stworzy instancję struktury, która będzie zawierać istotne informacje o pokoju.

'id' będzie unikalnym identyfikatorem wygenerowanym przez funkcję pomocniczą 'uuid()'. To pomoże nam zidentyfikować ten pokój w stosunku do listy wielu innych stworzonych pokoi później.

'name' zostanie ustawione na nazwę, którą użytkownik wpisał wcześniej.

'sockets' zostanie zainicjowane jako pusty zestaw. Później będzie to śledzić gniazda graczy podłączonych do tego pokoju.

rooms[room.id] = room;

'rooms' to globalna lista pokoi, które są aktualnie aktywne. Ponieważ tworzymy nowy pokój, przechowamy go w liście według jego ID.

joinRoom(socket, room);

To wywołuje globalną funkcję 'joinRoom' (linia 29), która doda gniazdo gracza do tablicy 'sockets' pokoju. Ponieważ gracz stworzył pokój, także dołączy do niego.

callback();
});

Na koniec wywołujemy funkcję zwrotną, aby klient został poinformowany, że pokój został stworzony. To pozwoli Emit to Socket kontynuować wykonanie, co załadowuje scenę poczekalni. To również kończy zdarzenie socket 'createRoom'.

Dołączanie do Pokoi

Dołączenie do pokoi jest nieco inne, ponieważ musimy uzyskać dostęp do informacji z innej sceny przyciskiem.

mceclip5.png

Dla pokoi dołączających zaczekom początku naszych zachowań na naszej globalnej warstwie, gdzie łączymy się z serwerem. Dzięki temu możemy mieć dostęp do informacji z każdej z naszych scen, w tym przypadku z listy pokoi. Dla przycisku, jego kliknięcie po prostu załadowanie gracza na scenie listy pokoi.

Oto nasze drugie zachowanie komunikujące się z serwerem. Zdarzenie gniazda można rozumieć jako Odbierz Wiadomość, ponieważ aktywuje się tylko wtedy, gdy ustawiona wiadomość została nadana z serwera.

Najlepszym sposobem myślenia o używaniu Socket Event i Emit to Socket jest to, że Socket Event reaguje tylko na informacje przychodzące z serwera, podczas gdy Emit to Socket wywołuje w odpowiedzi na rzeczy robione lokalnie.

Jeśli chodzi o naszą logikę, gdy połączymy się z serwerem, wyślemy Emit z zapytaniem 'getRoomNames', aby pobrać wszelkie dostępne nazwy pokoi.

mceclip19.png

Następnie ustawimy etykietę, na którą gracz może kliknąć, aby dołączyć.

mceclip6.png

To jest nasza scena listy pokoi. Tutaj zostaną załadowane i wyświetlone wszelkie dostępne pokoje gier. Po prostu klikając nazwę pokoju, gracze zostaną dołączeni. Dzięki wcześniejszym zachowaniom, lista automatycznie załadowuje wszelkie otwarte pokoje i generuje etykiety nazw pokoi na ekranie. Jeśli nic się nie pojawi, mamy przycisk Odśwież Listę, który po prostu powtarza zachowania, aby załadować je jeszcze raz.

mceclip8.png

Oto nasza logika, aby skonfigurować listę. Nie będziemy wchodzić w zbyt wiele szczegółów, ponieważ chcemy tylko wiedzieć, jak to łączy się z naszym serwerem.

Aby rozpocząć, mamy zachowanie Emit to Socket. To prosi serwer o wysłanie informacji o dostępnych pokojach. Stąd mamy zachowanie Get Array Value. Wszystkie dane, które pochodzą z serwera, będą wysyłane jako tablica i potrzebne informacje znajdą się w pierwszych wartościach. Więc ustawiliśmy nasz Get Array Value, aby uzyskać wartość na indeksie 0. Stąd, nasze zachowania będą wydobywać te dane i tworzyć etykiety dla każdego pokoju, generując je na naszym ekranie.

Następnie sprawdzimy obiekt, którego musimy dotknąć; w naszej scenie to etykieta o nazwie Nazwa Pokoju.

mceclip9.png

Ten tekst działa jako nasz przycisk, gdy zostanie wygenerowany, ale nadal musimy dołączyć ID pokoju, do którego chcemy się połączyć. Aby to zrobić, najpierw musimy uzyskać ID pokoju i robimy to, używając zachowania Get Attribute, ustawiając je na dynamiczne. Następnie emitujemy do serwera, że chcemy dołączyć do tego pokoju i załadować się do pokoju oczekiwania.

mceclip10.png

socket.on('joinRoom', (roomId, callback) => {

To jest punkt wejścia dla zdarzenia gniazda 'joinRoom'. Pierwsza wartość parametru będzie ID pokoju, do którego chcemy dołączyć, które zostało wysłane do serwera. Tutaj nazwaliśmy ten parametr 'roomId'.

const room = rooms[roomId];
joinRoom(socket, room);

Używając roomId podanego przez klienta, możemy znaleźć poprawną instancję pokoju na serwerze. Z tym wywołujemy globalną funkcję 'joinRoom' (linia 29) z gniazdem gracza, który chce się połączyć, oraz instancją pokoju. Przyjrzymy się funkcji 'joinRoom' za chwilę.

callback();
});

Na koniec wywołujemy funkcję zwrotną, aby powiadomić klienta o kontynuacji ładowania sceny poczekalni, co oznacza koniec zdarzenia gniazda 'joinRoom'.

Co dokładnie dzieje się w funkcji 'joinRoom'? Przyjrzyjmy się temu.

mceclip11.png

room.socket.push(socket);

Jak wcześniej wspomniano, tablica członkowska 'room.socket' śledzi gniazda podłączone w pokoju. Ta linia dokładnie to robi, dodając gniazdo do tablicy.

socket.join(room.id, () => {
socket.roomId = room.id;
console.log(socket.id, "Dołączono do", room.id);
});

To oficjalne wywołanie, aby połączyć klienta z pokojem. Najpierw mówimy gniazdu, aby dołączyło do pokoju według jego ID. Gdy to zostanie zrealizowane, wywoływana jest następująca funkcja zwrotna, w której przypisujemy ID pokoju do gniazda. Ostatnio logujemy w konsoli, że gracz dołączył do pokoju!

mceclip12.png

Poczekalnia

Poczekalnia to po prostu scena, do której wchodzimy, podczas gdy czekamy na dołączenie innych graczy lub na rozpoczęcie gry.

mceclip13.png

Po wejściu do pokoju wyślemy wiadomość 'ready' do serwera. Gdy serwer odbierze dwie z tych wiadomości, wyśle wiadomość, że zaczyna grę ('initGame'). Odeberamy tę wiadomość za pomocą naszego Socket Event i w ten sposób ładujemy naszą scenę gry. Co do naszych zachowań, to na tym wszystko. Możesz dodać przycisk, który rozłączy cię z pokoju i przywróci do głównego menu, jeśli zechcesz.

Po stronie serwera, przyjrzyjmy się kodowi, aby zobaczyć, co tam się dzieje.

mceclip14.png

To zdarzenie 'ready', które jest wywoływane, gdy klient dołączył do pokoju i jest gotowy do połączenia.

const room = rooms[socket.roomId];

Ponieważ do gniazda przypisaliśmy ID pokoju, możemy uzyskać pokój, aby sprawdzić, czy gra może się rozpocząć.

if (room.sockets.length == 2) {

Sprawdzamy tutaj, czy teraz w pokoju czekają dwa graczy. Powiedzmy, że to prawda, a my przechodzimy do rozpoczęcia gry.

for (const client of room.sockets) {
client.emit('initGame');
}

Teraz, gdy mamy dwóch graczy, iterujemy przez każde gniazdo i emitujemy zdarzenie 'initGame', aby każdy klient załadował scenę poziomu, tak jak pokazano wcześniej.

Część 5: Rozgrywka

Teraz, aby przejść do sedna sprawy. To jest miejsce, w którym będzie 90% naszej pracy. Poniżej znajduje się nasz poziom gry zaprojektowany dla tego samouczka.

mceclip15.png

Zanim się w to zanurzymy, mamy etykietę zatytułowaną "Logika Gry", otwórzmy ją i rzućmy okiem.

mceclip16.png

Wow, to wiele zachowań! Nie martw się jednak, to po prostu sposób, w jaki wprowadzamy naszych graczy do gry. Przyjrzyjmy się z bliska;

mceclip17.png

Zaczynamy od zachowania Emit to Socket, które informuje serwer, że nasza gra się rozpoczęła, z wydarzeniem 'startGame'. Następnie pobieramy tablicę, którą zwraca serwer, bierzemy jej pierwszą wartość, a za pomocą Get Dictionary Value uzyskujemy różne atrybuty, których nasz obiekt będzie potrzebował. Oddzielne drzewo robi to samo, ale dla przeciwnika. Ostatnio przesyłamy wiadomość 'init' do naszego obiektu gracza, aby rozpocząć akcję.

Przyjrzyjmy się, co dzieje się po stronie serwera, gdy emitujemy zdarzenie 'startGame'.

mceclip20.png

Pierwsza połowa tego zdarzenia socket polega na ustawieniu początkowych wartości dla każdego klienta, a następnie dodaniu wszelkich klientów, którzy nie są emitującym klientem, do lokalnej tablicy 'others'.

mceclip21.png

W drugiej połowie tworzona jest lokalna lista słownikowa o nazwie 'ack'. Zawiera ona informacje o nas samych i innych klientach. Następnie przesyłamy te informacje z powrotem do klienta, przekazując nasz słownik 'ack' do funkcji zwrotnej, która staje się wynikiem wykonania zachowania Emit to Socket.

Następnie wykonywany jest wywołanie timeout na 5 sekund przed rozpoczęciem rundy, teraz gdy wszyscy mają wszelkie informacje potrzebne do grania. Funkcja 'beginRound' (linia 99) zarządza pewną logiką specyficzną dla tej gry. Nie przekroczymy zbytnio szczegółów, ale zasadniczo obsługuje to, gdzie umieścić graczy, sprawdzanie punktów oraz informowanie klientów, kto jest Myśliwym.

Jak wcześniej wspomniano, komunikat 'init' jest wywoływany w naszej etykiecie "Logika Gry" gdy wszystko jest gotowe. W obiekcie gracza, teraz przyjrzymy się zachowaniom go, które otrzymują komunikat 'init'.

mceclip22.png

Jak widać, mamy wiele drzew zachowań w naszym graczu. Najpierw zaczniemy od lewego górnego drzewa.

mceclip23.png

To jest zachowanie, które praktycznie uruchamia wszystko inne w naszej grze.

Najpierw odbieramy komunikat 'init' wysłany z etykiety "Logika Gry". Stąd pobieramy identyfikator serwera naszego obiektu i włączamy jedno z naszych zdarzeń gniazda, a także ustawiamy nasz ekran gry, aby można było dokładnie śledzić naszą postać.

Synchronizacja Ruchu

mceclip24.png

Oto jedno z naszych najważniejszych zachowań. To małe drzewo jest zaprojektowane, aby aktualizować pozycję naszego gracza na serwerze za każdym razem, gdy poruszamy joystickiem. Prawdopodobnie zauważyłeś, że także odnosi się do pewnych wartości słownika. Otrzymujemy te wartości z osobnego zachowania słownikowego, które zawiera pozycje X i Y naszego gracza.

Przyjrzyjmy się zdarzeniu 'moved' na serwerze.

mceclip25.png

data = JSON.parse(data);

Słowniki, gdy są wysyłane z klienta na serwer, muszą być sparsowane, aby łatwo odczytać dane. Dzieje się tak dlatego, że słowniki w hyperPad są kodowane w strukturze JSON gdy są emitowane do serwera. Ta linia analizuje strukturę JSON i przechowuje listę słownika z powrotem w tej samej lokalnej zmiennej 'data'.

socket.x = data.x;
socket.y = data.y;

Teraz aktualizujemy wartości X i Y przechowywane w gnieździe, nowymi wartościami podanymi przez klienta.

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
});
}

Następnie iterujemy przez wszystkich klientów, aby zaktualizować ich o naszej nowej pozycji i innych szczegółach, z wyjątkiem nas samych (tj. Emitujący klient nie musi znać swojej pozycji).

mceclip26.png

To drzewo kontroluje większość naszej gry. Używamy zdarzenia gniazda, gdy serwer sprawdza, który gracz jest oznaczony jako Myśliwy, co jest emitowane przez 'beginRound' (linia 99) na serwerze.

Następnie pobieramy identyfikator naszego obiektu z tablicy i używamy wartości słownika, aby rozbić go na różne dane, które zawiera. Stąd, zdobywamy nasz Punkty, jeśli jesteśmy oznaczeni jako Myśliwy, a także pozycje x i y naszego obiektu, a następnie stosujemy je do atrybutów naszego obiektu. Reszta zachowań ma na celu ustawienie i kontrolowanie UI w naszej grze.

Podsumowanie

Było tu dużo do przyswojenia, ale miejmy nadzieję, że jeśli dotarłeś tak daleko, zrozumiesz, jak wykorzystać zachowania Socket.io do stworzenia doświadczeń multiplayer dla swoich graczy.

Spróbuj sam! Weź istniejącą grę, którą stworzyłeś, i spróbuj nadać jej funkcjonalność online, na przykład scenę rankingu punktów, która łączy się z serwerem i żąda listy 10 najlepszych wyników z nazwami graczy do wyświetlenia.

Trudno jest nauczyć się języka skryptowego, takiego jak Javascript, w jednym artykule. Na szczęście, jeśli masz trudności, istnieje wiele innych zasobów, które mogą pomóc Ci w pisaniu aplikacji Javascript dla Node.JS i Socket.io;

Ucz się Javascript - https://developer.mozilla.org/bm/docs/Web/JavaScript

Ucz się Socket.io - https://socket.io/docs/

Ucz się Node.JS - https://nodejs.org/en/docs/