Marek Winiarski

Unity3d QuickTip #50.2 – Komunikacja z serwerem www

Unity3d QuickTip – czyli szybkie porady, rozwiązania częstych problemów i sztuczki w Unity3d!

Dzisiejszy odcinek: Komunikacja z serwerem www, część druga.

Uwaga! Jest to poradnik typu QuickTip. Zatem skupia się on na osiągnięciu założonego celu. Zatem zakładamy że użytkownik zna na tyle program Unity3d, aby samodzielnie wykonać najprostsze czynności, jak np. dodanie modelu kostki do sceny czy dodanie modelowi jakiegoś komponentu. Jeżeli brakuje Ci tej podstawowej wiedzy, zapraszam do tutoriala:
Unity Tutorial – Podstawy

Teoria

Teorii zasadniczo nie ma na dziś, bo została przedstawiona w poprzedniej części poradnika, którą polecam przeczytać każdemu, kto nie miał z nią styczności. Omawiam tam teorię i pokazuję jak stworzyć serwer, z którym będziemy się łączyć dzisiaj.

Ponownie zaznaczę, że utworzone połączenie nie będzie bezpiecznie. Tworzymy jedynie samo połączenie. Zabezpieczeniem połączenia zajmiemy się w części trzeciej. Na razie przyświeca nam zasada: jak najprostszymi metodami do osiągnięcia celu.

Tworzymy formularz

Jak pamiętacie z poprzedniej części, tworzyliśmy tam sobie prosty formularz do logowania i rejestracji. Dzisiaj przygotujemy sobie tylko jeden, bo znów tworzenie obu będzie analogiczne i tylko zmienią się pola. Zakładam, że w ramach testowania stworzyłeś jakichś użytkowników. Dlatego zbudujemy formularz logowania.

Oprzemy go sobie na obiekcie UI. Będziemy potrzebować dwa obiekty Input Field [GameObject -> UI -> Input Field], obiekt button [GameObject -> UI -> Button] oraz puste pole tekstowe do wypisania wyników [GameObject -> UI -> Text].

Wszystkie wyklikane tak obiekty, powinny umieścić się automatycznie w obiekcie Canvas. Całość powinna reprezentować się tak:

Przygotowany formularz
Przygotowany formularz

To co ja jeszcze zrobiłem, to zmiana położenia obiektów, żeby były u góry zamiast na samym dole, gdzie umieszczają się domyślnie. Wpisanie sensownej treści do placeholderów (wystarczy wybrać obiekt Placeholder dla odpowiedniego pola i w oknie inspektor wpisać sobie co tam chcemy). Dodatkowo zmieniłem nazwy Input Fieldów na Login i Passowrd, żeby odpowiadały faktycznym polom – tylko dla własnej wygody. Ostatni obiekt Text, jest tekstem gdzie wypiszę wynik działania i zamieściłem go pod całością, uprzednio usuwają tekst, dlatego w oknie game nie jest widoczny. Zmieniłem też napis wyświetlany na przycisku.

Konsternację może jeszcze budzić obiekt InternetConnector. Jest to zwykły pusty GameObject (Ctrl + Shift + N). Do niego dodałem utworzony na potrzeby tutoriala skrypt: InternetConnection.cs. Teraz czas utworzyć sobie taki skrypt, przypisać go do tego obiektu oraz wejść w jego edycję.

Kodujemy połączenie

Skrypt mamy bardzo prosty.

Po pierwsze wzbogacamy nasze zaimportowane biblioteki:

using UnityEngine;
using System;
using System.Collections;
using UnityEngine.UI;

Bez System nie skorzystamy z Serializacji, która może się przydać, ale nie jest konieczna, a bez UnityEngine.UI nie skorzystamy z komponentów UI, czyli nie odczytalibyśmy jakie wartości wprowadził użytkownik. Czas zadeklarować zmienne:

public Text login_input;
public Text password_input;
public Text output;

Trzy pola tekstowe, login użytkownika, hasło i nasz wyjściowy Text, gdzie wprowadzimy wynik operacji. Można teraz przeskoczyć do Unity i poprzeciągać odpowiednie obiekty Text na odpowiednie zmienne.

Tutaj uwaga, np. dla login_input, odpowiedni obiekt to: Canvas -> Login -> Text. Przeciągnięcie obiektu Login, nie da poprawnego rozwiązania!

Teraz tworzymy sobie strukturę wynikową:

[Serializable]
public class ResponseClass
{
	public int status;
	public string msg;
}

Serializacja nie jest tutaj konieczna, ale pozwoli nam wyświetlać dane w inspektorze, gdybyśmy tego potrzebowali. Mamy dwie zmienne, których nazwa jest kluczowa. Przypomnę tutaj strukturę jaką wypisywaliśmy na wyjściu naszego kodu w PHP:

array('status' => 1, 'msg' => "Success!");

Teraz chyba widać o co chodzi? Nazwy zmiennych status i msg, są nazwami naszych pól w strukturze. Tą zasadę trzeba zachować – później się wyjaśni dlaczego.

Tworzymy sobie teraz prostą funkcję:

public void performLogin()
{
	StartCoroutine(makeLogin ());
}

Już tłumaczę czemu taka śmieszna konstrukcja. Potrzebujemy funkcji, która będzie zwracać IEnumerable, aby móc wykonać połączenie ze stroną www. Natomiast gdy wywołujemy funkcję na kliknięcie przycisku w UI, funkcja nie może zwracać IEnumerable. W efekcie, stworzyliśmy funkcję z typem zwracanym void, która wykonuje współprogram.

No to czas na funkcję makeLogin. Najpierw dam w całości, a potem omówię fragmentami:

private IEnumerator makeLogin() {

	WWWForm form = new WWWForm();
	form.AddField( "user_login", login_input.text );
	form.AddField( "user_pass", password_input.text );

	string url = "http://adresTwojegoFormularza.pl";

	WWW www = new WWW(url, form);
	yield return www;

	if (!string.IsNullOrEmpty (www.error)) {
		print (www.error);
	} else {
		string json = www.text;
		ResponseClass response = JsonUtility.FromJson<ResponseClass> (json);

		output.text = response.msg;
	}
}

Prawdopodobnie sporej części można się domyślić, ale lecimy:

WWWForm form = new WWWForm();
form.AddField( "user_login", login_input.text );
form.AddField( "user_pass", password_input.text );

Znów kod powinien coś przypominać. Mamy tutaj w zasadzie kopię formularza, który stworzyliśmy w PHP. Pierwsza linijka inicjuje nam formularz, a kolejne dwie dodają nam pole, podając najpierw jego nazwę, a potem wartość. Nazwa oczywiści musi się pokrywać z nazwami, które stworzyliśmy w PHP.

[stextbox id=”info” defcaption=”true”]W zasadzie musi się pokrywać nie tyle z formularzem, co z pobieraną z tablicy $_GET lub $_POST nazwą zmiennych. Jednak ta nazwa pokrywa się z nazwami pól w formularzu, więc tutaj na jedno wychodzi. Jednak kod działałby i bez formularza. Innymi słowy, jeśli po stronie PHP login pobieramy kodem $_POST[“MojLogin”], to w Unity dodając pole, musimy napisać: AddField(“MojLogin”, zmienna); Formularz po stronie PHP jest opcjonalny. My go stworzyliśmy do celów testowych.[/stextbox]

Wartości pobieramy z podanych publicznie pól tekstowych. Dodatkowo jeśli byłaby taka potrzeba, można za jego pomocą przesłać dane binarne, czyli np. obrazek czy plik tekstowy. My tego nie potrzebujemy.

string url = "http://adresTwojegoFormularza.pl";

W zasadzie nic do tłumaczenia. Kropka w kropkę adres do miejsca gdzie odbywa się logowanie. Można skopiować z przeglądarki. (Nie wiem czy będzie działało na localhost).

WWW www = new WWW(url, form);
yield return www;

Tworzymy sobie obiekt, który będzie w stanie wykonać zapytanie do serwera. Ma dwa parametry w konstruktorze. Adres strony do której wykonujemy zapytanie, oraz parametry jakie trzeba przesłać. Dla uproszczenia jest to nazwane zmienną typu WWWForm, czyli formularza, ale w zasadzie są to po prostu przesyłane dane w sposób, jak gdyby były przesyłane normalnym formularzem na stronie WWW. Nie przesyłamy tutaj faktycznego formularza.

Druga linia wykonuje zapytanie i wprowadza do zmiennej www wynik.

if (!string.IsNullOrEmpty (www.error)) {
	print (www.error);
} else {
	string json = www.text;
	ResponseClass response = JsonUtility.FromJson<ResponseClass> (json);

	output.text = response.msg;
}

If sprawdza czy zmienna error dla naszego www nie jest nullem albo pusta. Gdyby coś zawierała, to wypisujemy sobie w konsoli błąd, który jest zapisany w tym atrybucie. Normalnie warto by to było jakoś bardziej obsłużyć, aby użytkownik wiedział, że trafił na błąd w połączeniu – np. można go wypisać w tym polu tekstowym, które przygotowaliśmy.

W else do stringa wstawiamy parametr text zmiennej www. Wypisywaliśmy na stronie JSONa czyli odpowiedni format tekstowy i właśnie to mamy w zmiennej text.

[stextbox id=”info” defcaption=”true”]Warto sobie sprawdzić w dokumentacji klasę WWW, ponieważ ma ona kilka różnych zmiennych, które mogą zwierać różne dane w zależności od tego co wyświetli nasza strona. Gdy my wyświetlaliśmy tylko tekst, to zadowala nas zmienna text. Ale możemy również w ten sposób przesłać teksturę czy klip audio.[/stextbox]

Kolejna linia to odczytanie formatu JSON. W większości języków funkcja czytająca z JSONa sprowadza go do postaci obiektu lub tablicy. Tutaj mamy sprowadzenie do obiektu, który musimy jawnie zdefiniować, co zrobiliśmy na samym począku. Przez to tutaj tylko podajemy ResponseClass jako typ. Dlatego nazwy zmiennych musiały się pokrywać. Inaczej odczytanie z JSONa nie odbyłoby się poprawnie.

Ostatnia linijka to wypisanie do naszego dodatkowego pola tekstowego odpowiedzi.

Została nam jedna rzecz do zrobienia. Musimy po kliknięciu w przycisk wywołać nasz skrypt. Dlatego w Hierarchy wybieramy przycisk. Teraz w oknie Inspektor w panelu OnClisk (na samym dole) klikamy plusa. W paneliku wybieramy nasz obiekt InternetConnector, a następnie w dużym panelu rozwijanym naszą funkcję PerformLogin.

Wywołanie funkcji na kliknięcie przycisku

Zostało uruchomić grę i wszystko przetestować.

Podsumowanie

Tak jak wspomniałem nie zajmowaliśmy się tutaj ani trochę zabezpieczaniem połączenia. Nasze hasło i login lecą sobie do serwera luzem, odpowiedź również leci do nas jawnym tekstem. Takie dane może łatwo przechwycić haker i wykorzystać na niekorzyść gracza, albo co gorsza naszego serwera. Jednak dwie pierwsze części tego mini kursu miały pokazać jak w najprostszy sposób połączyć program Unity z serwerem WWW. Zabezpieczeniem połączenia zajmiemy się w części trzeciej.

Exit mobile version