Java – Nastoletni Programiści Thu, 15 Nov 2018 14:35:06 +0000 pl-PL hourly 1 https://wordpress.org/?v=4.9.8 115968029 Zabezpieczenie antypirackie – aktywacja online z algorytmem RSA i BCrypt /zabezpieczenie-antypirackie-aktywacja-online-z-algorytmem-rsa-i-bcrypt/ /zabezpieczenie-antypirackie-aktywacja-online-z-algorytmem-rsa-i-bcrypt/#comments Mon, 27 Feb 2017 18:19:32 +0000 /?p=1307 Tworzycie aplikacje, które chcecie potem sprzedać? Za pewne chociaż przez chwilę pomyślałeś: „a co z piratami?”. W tym artykule chciałbym pokazać przykład zabezpieczenia aplikacji z aktywacją online, a także pokazać, jak szyfruje się algorytmem RSA i hashuje BCrypt’em.

Uwaga, aby artykuł nie miał setek stron, pominąłem dogłębne tłumaczenie kodu Javy/PHP/SQL. Co nie znaczy, że jeśli jesteś zaciekawionym tematem to nic nie zrozumiesz. Postarał się w miarę możliwości dobrze okomentować kod. W każdym razie czujcie się ostrzeżeni! 😉

Z nielegalnym oprogramowaniem spotykamy się na co dzień – w Polsce jest ono dosyć popularne . Tworząc aplikację, którą chcemy sprzedać komuś, chcielibyśmy zadbać o to, żeby inni nie mieli do niej dostępu. Dlatego programiści starają się wymyślać coraz to nowsze zabezpieczenia przeciw piratom. Chciałbym pokazać jedną z prostych metod zabezpieczenia aplikacji.

Typowe zabezpieczenia:

  1. Aktywacja offline – tzn. wpisujemy klucz i wszystko działa
    • Zalety:
      • Brak konieczności połączenia z internetem
      • Najmniejszy problem dla użytkownika
    • Wady:
      • Najgorsze zabezpieczenie, możliwość wpisania tego samego klucza na wielu urządzeniach
  2. Aktywacja online z kontem – wpisujemy klucz i zostaje on przypisany do konta
    • Zalety:
      • Klucz nie jest przypisany do urządzenia, lecz do konta (możliwość przeniesienia licencji na inne urządzenia)
      • Po pierwszej aktywacji, można korzystać z programu offline
    • Wady:
      • Konieczność jednorazowego połączenia z internetem
      • Każdy użytkownik musi zrobić konto
      • Serwer aktywacji musi stale być dostępny, lecz tylko na czas aktywacji
  3. Aktywacja urządzenia online – wpisujemy klucz i urządzenie jest zapisywane do bazy danych
    • Zalety:
      • Brak możliwości skopiowania pliku weryfikującego licencję
      • Po pierwszej aktywacji, można korzystać z programu offline
    • Wady:
      • Konieczność jednorazowego połączenia z internetem
      • Serwer aktywacji musi stale być dostępny, lecz tylko na czas aktywacji
      • Brak możliwości aktywacji na kolejnym urządzeniu (co jeśli komputer się zepsuje?)
  4. Stały wymóg online
    • Zalety:
      • Najlepsze zabezpieczenie, bez zmian w kodzie programu trudne do obejścia
      • Możliwość instalacji programu na różnych urządzeniach (z tym że jednocześnie można włączyć tylko jeden)
    • Wady:
      • Konieczność stałego połączenia z internetem.
      • Serwer aktywacji musi stale być dostępny! Inaczej użytkownik nie włączy programu.

Do każdego z tych punktów można dopisać jeszcze jedną wadę:

Żadne zabezpieczenie nie jest całkiem bezpieczne

Żadne z tych zabezpieczeń nie odeprze jednego: zmiany kodu programu. Dlatego warto jest korzystać z różnych zabezpieczeń, które modyfikują nam nazwy zmiennych/funkcji w kodzie, aby potem kod był trudny do rozczytania po dekompilacji.

Aktywacja systemu Windows

Przed zastosowaniem zabezpieczenia warto przemyśleć, które warto wybrać. Jak każdy wie, stały wymóg połączenia internetowego może być denerwujący dla użytkownika. Jeżeli nie robicie aplikacji, która operuje na połączeniu internetowym, to nie zawsze warto wybierać to zabezpieczenie. Na dzień dzisiejszy najlepszym sposobem wydawać się może jednorazowa aktywacja – ma ją np. Windows, Steam. W tym artykule chciałbym zaprezentować mój sposób na system aktywacji, który przedstawiłem jako nr. 3 w liście wyżej; czyli – aktywacja urządzenia online.

Dlaczego akurat to wybrałem? Gdy myślałem o zastosowaniu mojej aplikacji, stwierdziłem, że zależy mi, aby nie była ona wykorzystywana przez jedną osobę na kilku urządzeniach. Dlatego aktywacja urządzenia wydaje się być rozsądnym wyborem.

Jak to wygląda?

Identyfikator sprzętowy

Przy aktywacji urządzenia pierwszą rzeczą, jaka jest nam potrzebna, to pobranie jakiegoś identyfikatora urządzenia. I to jest drobny problem – co można wziąć? W większości przypadków znajdziecie na internecie wykorzystanie adresu MAC karty sieciowej, ale to rozwiązanie jest dosyć słabe – ponieważ w przypadku laptopów jest możliwość, iż aplikacja była instalowana gdy interfejs sieciowy WLAN był włączony, a potem wyłączony. Adres MAC w aplikacji zostanie zwrócony inny i aplikacja przestanie działać . Dlatego polecam np. wykorzystać identyfikator BIOS’u. W Windowsie pobiera się go wpisując komendę w konsoli: wmic bios get serialnumber , w przypadku Linuxa sprawa jest nieco trudniejsza, ale i tak są pewne rozwiązania.

Schemat aktywacji

Okej – mamy już identyfikator sprzętowy – teraz przydałoby się go jakoś wysłać do serwera i odebrać plik weryfikacyjny. Tak to będzie wyglądać:

  1. Urządzenie klienta wysyła do serwera informację z identyfikatorem urządzenia + kluczem produktu do serwera.
  2. Serwer sprawdza czy klucz jest w bazie danych, jeśli jest to zwiększa jego licznik użycia i dodaje identyfikator urządzenia do tabeli.
  3. Następnie szyfruje algorytmem RSA przy użyciu klucza prywatnego sam identyfikator, wpisuje go do bazy danych i
  4. odsyła klientowi.
  5. Gdy klient odbierze plik, odszyfrowuje go za pomocą klucza publicznego i sprawdza czy jest to ten sam identyfikator sprzętowy.
  6. Następnie zapisuje zaszyfrowaną wiadomość w pliku *.key, który znajduje się w lokalizacji instalacji programu.

Ogólny schemat pierwszej aktywacji prezentuje się tak:

Schemat będzie się nieco różnić w przypadku, gdy klient aktywuje drugi raz aplikację na tym samym urządzeniu. Wtedy gdy serwer wykryje że klucz jest już użyty (czyli used_num>=1 ) to będzie sprawdzać tabelę activations i będzie próbował znaleźć hardware_id taki, jaki wysłał klient. Jeśli znajdzie takowy w tabeli, to oznacza, że klucz jest poprawny i odsyła klientowi klucz weryfikacyjny z kolumny response_key .

Przejdźmy do implementacji. Potrzebny nam serwer + baza danych na tym serwerze. Ja wybrałem bazę danych MySQL. Stworzyłem bazę danych o nazwie Activation , a w środku dwie tabele:

  • activation_keys – w tej tabeli zawarte są wszystkie zaszyfrowane klucze programu i ich liczba użyć
    CREATE TABLE activation_keys (id int PRIMARY KEY NOT NULL AUTO_INCREMENT, activation_key varchar(72) NOT NULL, used_num int);
  • activations – w tej tabeli zwarte są wszystkie aktywacje, identyfikator sprzętowy i identyfikator klucza z tabeli activation_keys
    CREATE TABLE activations (id int PRIMARY KEY NOT NULL AUTO_INCREMENT, id_key int NOT NULL, hardware_id varchar(64), response_key TEXT);

Identyfikator urządzenia będziemy pobierać, korzystając z wyżej wymienionego kodu + hashowania SHA-256.

Klucze będziemy szyfrować algorytmem BCrypt , który generuje maksymalnie 72-znakowy kod. Na obrazku obok jest pokazany schemat BCrypt. Jest to bardzo zalecane , gdyż mając atak hakerów na bazę danych, będą oni w stanie sprawdzić wszystkie dostępne klucze . Ogólnie, BCrypt zamienia klucz do formy zhashowanej. Ten proces trudno odwrócić, tzn.posiadając hash, trudno odnaleźć taki klucz, który po zhashowaniu będzie mieć taką samą formę jak zhashowany. BCrypt często wykorzystuje się do zapisywania haseł użytkowników na wielu stronach internetowych. Klient wysyła do serwera hasło w formie zahashowanej , a następnie jest sprawdzana jego poprawność. W przypadku ataku na bazę danych z hasłami, haker będzie mieć tylko tabelę z hasłami zahashowanymi oraz będzie mieć problem z odwróceniem procesu hashowania. Biblioteki BCrypt znajdziecie w Google.

Uwaga! Np. SHA-1, SHA-256 przestał być bezpiecznym algorytmem i dziś z dobrym komputerem nie ma większych problemów z inwersją. Jeżeli zależy wam na zabezpieczeniu przeciw dobrym hakerom, to polecam poszukać w Google nowych, skuteczniejszych zabezpieczeń. Jednak jeśli chcecie się obronić przed „Script kiddie”, to wystarczy nawet MD5. (nie warto też wrzucać tysięcy kluczy do tabeli, lecz dodawać nowe przy zakupie przez daną osobę)

Do tworzenia kluczy polecam stworzyć program. Ja uznałem że moje klucze będą 16 znakowe. Tak prezentuje się kod w Javie do wygenerowania losowego klucza i zhashowania go. Potrzebna jest też biblioteka BCrypt dla Javy: link .

BCrypt.gensalt(10) – decyduje o złożoności hasła. Czym większa liczba w argumencie, tym trudniej będzie odszyfrować. Ale też sprawdzanie za pomocą serwera będzie dłuższe. BCrypt.gensalt() generuje optymalną wartość, wybraną przez twórcę biblioteki. Warto zauważyć, że duża wartość może być też problemem dla serwera przy sprawdzaniu poprawności klucza.

String AB – w tym łańcuchu znaków znajdują się wszystkie znaki, które będą mogły znaleźć się w kluczu niezaszyfrowanym.

funkcja randomString – zwraca losowy łańcuch znaków o długości len , z wykorzystaniem znaków z łańcucha znaków String AB .

Test generacji klucza

Dodajmy od razu testowy klucz 1234567890123456. Aby taki zrobić wystarczy w kodzie zamiast String key = randomString(16); dać String key = "1234567890123456"; Klucze w takiej formie będą przechowywane w tabeli do której nikt nie ma prawa mieć dostępu. Możecie zrobić bazę danych albo po prostu wklejać je do arkusza kalkulacyjnego. Otrzymujemy zaszyfrowany klucz – $2a$10$SjT/4zh1LwNrsiKCvhUlzec4bAilAsvhw3xb2vpJkRK6eTacCTJFy. Teraz wpisujemy go do tabeli na serwerze korzystając z komendy SQL INSERT INTO .

INSERT INTO `activation_keys`(`activation_key`,`used_num`) VALUES (" $2a$10$SjT/4zh1LwNrsiKCvhUlzec4bAilAsvhw3xb2vpJkRK6eTacCTJFy ",0);

Czyli zhashowane klucze dodajemy do tabeli SQL, a klucz niezhashowany wysyłamy klientowi. Aplikacja będzie hashować klucz wpisany przez klienta, następnie wysyła go do serwera, który będzie porównywać wszystkie zhashowane klucze.

Okej, więc mamy bazę danych z kluczami gotową. Przejdźmy teraz do pliku weryfikującego licencję. Aby program zadziałał, w swojej lokalizacji musi mieć plik activation.key , który po odszyfrowaniu kluczem publicznym RSA wskaże SHA-256 identyfikatora sprzętowego urządzenia, na którym jest odpalany właśnie ten program. Ten plik activation.key będzie tworzony podczas pierwszej aktywacji (serwer wyśle odpowiedni kod dla klienta, który następnie zostanie zapisany w owym pliku). Trudno stworzyć taki klucz, żeby po odszyfrowaniu kluczem publicznych, dał SHA-256 identyfikatora, dlatego to zabezpieczenie jest w miarę bezpieczne. Serwer, posiadając klucz prywatny RSA może z łatwością stworzyć klucz weryfikacyjny. Rysunek pomoże dobrze zinterpretować to co napisałem:

Tworzenie klucza RSA

Dla przypomnienia – schemat szyfrowania RSA . Wszystko pokazane na obrazku wyżej: kluczem publicznym szyfrujemy wiadomość; zaszyfrowanej wiadomości nie można rozszyfrować kluczem publicznym , potrzebny jest do tego klucz prywatny . Szyfrowanie to może też działać w drugą stronę – można szyfrować kluczem prywatnym, a odszyfrowywać publicznym, co dziś wykorzystamy. W naszym przypadku klucz weryfikacyjny będzie szyfrowany kluczem prywatnym na serwerze , a potem klucz będzie odszyfrowywany w aplikacji kluczem publicznym.

Jak stworzyć klucz RSA ? Do jego stworzenia wykorzystam narzędzie OpenSSL , które można pobrać tu dla Windowsa: http://gnuwins32.sourceforge.net/packages/openssl.htm
Program ten odpala się w trybie konsolowym. Potrzebne nam będzie parę komend:

  • openssl genrsa -out private_key.pem 2048 – tworzy 2048-bitowy klucz prywatny, o rozszerzeniu *.pem, które dobrze odczyta PHP. Jeśli chcecie lepszy poziom zabezpieczeń, to możecie użyć 3072 bitów.
  • openssl pkcs8 -topk8 -inform PEM -outform DER -in private_key.pem -out private_key.der -nocrypt – tworzy plik private_key.der w formacie PKCS#8, który lepiej jest odczytywany w Javie (bez kombinowania)
  • openssl rsa -in private_key.pem -pubout -outform DER -out public_key.der – tworzy plik public_key.der, który łatwiej odczytać w Javie (bez tworzenia przeróżnych funkcji które zmieniają format klucza)

Są klucze, więc przyszła część na PHP, do którego będzie się zwracał klient. PHP będzie wypisywać proste komunikaty typu „ERROR”, „OK”, „WRONGKEY”, które następnie program wczyta i wskaże odpowiedni komunikat.

Klucz i identyfikator urządzenia będą wysyłane metodą POST. Wynik będzie wypisany funkcją echo, a oto lista możliwych wyników:

  • OK;klucz_weryfikacyjny; – aktywacja przebiegła pomyślnie, pomiędzy średnikami znajduje się klucz weryfikacyjny (zaszyfrowany kluczem prywatnym)
  • OK_USED;klucz_weryfikacyjny; – aktywacja przebiegła pomyślnie, nie jest to pierwsza aktywacja na tym urządzeniu
  • WRONGKEY – klucz nie znajduje się w bazie
  • WRONGHARDWARE – identyfikator urządzenia jest pusty
  • USED – klucz jest już aktywowany dla innego urządzenia
  • ERROR – Błąd przy łączeniu z bazą lub inny

Po co te średniki w odpowiedziach? Są one po to aby wydzielić granice klucza weryfikacyjnego. Głębsze wyjaśnienie tego znajdziecie w dalszej części artykułu.

Program desktopowy będzie sprawdzać, jaki kod został otrzymany i wyświetli odpowiedni komunikat.

Część serwerowa – plik PHP

Na początku stworzyłem sobie dwie funkcje, które kończą wykonywanie pliku PHP i zwracają błąd:

Jeśli jakimś cudem nie ma parametrów HTTP POST lub identyfikator sprzętowy jest pusty to należy zakończyć działanie programu:

Klucz prywatny (private_key.pem) wrzucamy do poprzedniego katalogu niż jest plik PHP. Upewnijmy się, że prawa do pliku są tylko dla serwera i że nie można tego pliku otworzyć bezpośrednio z innego urządzenia.

Teraz należy pobrać klucz prywatny RSA z poprzedniego katalogu, który nie jest dostępny dla nikogo:

Połączenie się z bazą danych i ewentualne zwrócenie błędu:

Pobieramy rekordy z tabeli activation_keys i sprawdzamy, czy klucz jest dobry.

password_verify() – sprawdza czy klucz w formie niezaszyfrowanej, jest odpowiednikiem zaszyfrowanego podanego w drugim argumencie (BCrypt)

trim() – usuwa spacje na początku i końcu klucza. (zabezpieczenie, jeśli do bazy danych daliśmy zaszyfrowany klucz z niepotrzebną spacją – mi się to zdarzyło)

Jeżeli klucz nie został jeszcze użyty – czyli $used jest równe 0, to przygotujemy klucz weryfikacyjny dla klienta metodą openssl_private_encrypt($informacja_do_zaszyfrowania, $zmienna_gdzie_zapisze_sie_zaszyfrowany_tekst, $klucz_prywatny_RSA); Następnie należy powiększyć licznik użycia klucza i dodać identyfikator sprzętowy do tabeli activations . Na sam koniec wystarczy zwrócić klientowi klucz weryfikacyjny funkcją echo , który następnie klient zapisze do pliku.

Użyte jest to proste zabezpieczenie przed SQL Injection, więcej o tym możecie poczytać w innym artykule na tej stronie: Zabezpieczenia baz danych

Jeżeli klucz jest użyty to należy sprawdzić, czy urządzenie o identyfikatorze wysłanym w HTTP POST występuje w tabeli activations . Jeśli występuje to należy klucz weryfikujący wysłać klientowi.

Jeśli po zakończeniu pętli nie odnaleziono klucza, to należy zwrócić błąd – zły klucz.

Cały plik prezentuje się tak:

Możecie stworzyć też prosty plik HTML i porobić parę testów:

Aplikacja desktopowa

W tym fragmencie artykułu będę opisywał stworzenie aplikacji desktopowej Javie, co nie zmienia faktu, że tak samo da się zrobić w innych językach programowania.

Zacznijmy od stworzenia projektu i dodania biblioteki BCrypt. Link do biblioteki: http://www.mindrot.org/projects/jBCrypt/

Stwórzmy najpierw klasę pomocniczą Activation.java . Będzie ona pomagać w tworzeniu klucza identyfikatora urządzenia i do odszyfrowania klucza weryfikacyjnego:

Jak pewnie zauważyliście, do pobrania identyfikatora sprzętowego potrzebna jest magiczna klasa WindowsID. Wyżej napisałem, jak w przypadku systemu Windows pobierać identyfikator. Oto ta klasa:

W przypadku innych systemów też znajdziecie różne rozwiązania na pobranie identyfikatora sprzętowego.

Przejdźmy teraz do GUI aplikacji. Na początku można zrobić prosty layout w JavaFX (Java Scene Builder):

A tutaj przegląd obiektów, dla których przypisałem jakieś id. Jest to potrzebne po to, aby już w kodzie Javy móc pobrać te obiekty:

Do katalogu resources (jeśli nie wiecie jak dodawać to zapraszam tutaj: link ) wrzucamy plik public_key.der . Startowy plik Javy wygląda tak:

OnActivationListener zawiera funkcję, która zostanie aktywowana, jeśli aktywacja się powiedzie oraz funkcję pobrania klucza publicznego.

Teraz część kontrolera Aktywacji. W JavaFX, korzystając z kontrolera scen, można łatwo pobierać obiekty GUI dla których przypisaliśmy jakiś identyfikator. Wystarczy podać adnotację @FXML a następnie klasę obiektu i jego identyfikator. Bardzo ułatwia to pracę.

Po kliknięciu przycisku submitButton jest on wyłączany (na czas próby połączenia z serwerem) i są wykonywane czynności łączenia. Do funkcji connectToServer dodałem 3 argumenty:

  • keyInput.getText() – klucz produktu
  • id – identyfikator urządzenia
  • interfejs ActivationListener – informuje o rezultacie połączenia / zwraca wynik wykonania PHP

Interfejs ActivationListener wygląda tak:

W odpowiedzi serwera musimy sprawdzić wszystkie przypadki, które wymieniłem wcześniej, czyli: OK , OK_USED , WRONGKEY , WRONGHARDWARE , USED , ERROR i jeszcze przypadek, jak coś innego zostało zwrócone (nieznany błąd). Gdy wystąpi błąd połączenia z serwerm (brak internetu lub serwer jest wyłączony) to zostanie wywołana funkcja z interfejsu ActivationListener void error(); Błąd połączania wyskoczy jako wyjątek IOException , który łatwo wyłapać.

A tak wygląda sama funkcja łączenia z serwerem:

Warto zaznaczyć, że koniecznie trzeba tu użyć wątków. Bez nich aplikacja zacięłaby się na czas łączenia z serwerem. Tak nie może być! Dlatego też skorzystałem z prostego wątku Thread.

Wracamy do interfejsu ActivationListener . Co jeśli jest błąd połączenia? Należy wyświetlić odpowiedni komunikat i ponownie włączyć przycisk submitButton (bo użytkownik ma prawo ponowić próbę).

Warto zauważyć, iż aktualizując elementy interfejsu z innego wątku, należy skorzystać z funkcji Platform.runLater , która aktualizuje GUI z wątku interfejsu graficznego. Bez tego nie da się aktualizować interfejsu.

Przejdźmy teraz do drugiej funkcji interfejsu ActivationListener. Serwer zwraca pewien ciąg bajtów, które można zamienić na łańcuch znaków: String responseString = new String (response); Problem polega na tym, że PHP zwraca niepotrzebne spacje i nowe linie na końcu pliku, dlatego też użyłem średników do wyznaczania początku i końca klucza weryfikacyjnego. Do tego klucz weryfikacyjny jest zapisany w formie bitów i zawiera różne znaki, które po przekształceniu do String’a, zmienią swój format. Dlatego, w przypadku zwróconego klucza weryfikacyjnego przez serwer, trzeba na nim operować jako na tablicy bajtów . Funkcja trimKey obcina tablicę bajtów do pierwszej znalezionej liczby podanej w argumencie 2 oraz obcina tablicę od końca to ostatniej znalezionej liczby podanej w argumencie 2. Średnik to w kodzie ASCII numer 59, dlatego też taki wpisałem do funkcji trimKey . Dobry klucz jest pobierany do zmiennej goodKey , a do zmiennej keyFromServerDecrypted przypisywany jest odszyfrowany klucz weryfikacyjny. Jeśli oba te zmienne mają takie same wartości ( contentEquals ), to należy zapisać klucz zaszyfrowany w pliku activation.key . Starałem się okomentować kod, aby był w miarę możliwości zrozumiały:

A funkcja trimKey prezentuje się tak:

Na sam koniec wystarczy jeszcze dopieszczyć plik startowy, aby program nie wyświetlał okna z aktywacją przy każdym uruchomieniu. Klucz będzie sprawdzany tak samo jak w przypadku aktywacji, więc nie tłumaczę już kodu. Poniżej fragment kodu z funkcji startowej JavaFX:

Wygląd okienka aktywacyjnego

Nareszcie koniec ! Cieszę się, jeśli dotrwaliście do tego momentu. Od teraz możecie powiedzieć, że algorytm RSA nie taki straszny, a nasza aplikacja stała się w miarę bezpieczna! Jakbyście czegoś nie rozumieli to zapraszam do komentowania !

Podsumowanie

Tworząc aplikacje, które dotrą do wielkiej liczby użytkowników, nie jesteśmy w stanie całkiem przeciwdziałać ich niecnym zamiarom okradania nas. Jednakże systemy antypirackie takie jak aktywacja online pozwalają pozbyć się tego problemu nawet w większości przypadków. Warto łączyć różne systemy aktywacji, tworzyć własne, aby były trudniejsze do ogarnięcia .

W tym artykule przekazałem także informacje na temat szyfrowania – jest ono używane wszędzie: na stronach banku , sklepach, mediach społecznościowych. Dlatego kryptografia (utajnianie wiadomości) jest taka ważna. Korzystamy z niej na co dzień! Tworząc stronę internetową warto zadbać o to, aby hasła użytkowników nie były narażone na proste ataki hakerów. Zabezpieczenie haseł nie jest trudnym zajęciem, jednakże nadal sporo osób omija to szerokim łukiem lub używa przestarzałych algorytmów.

]]>
/zabezpieczenie-antypirackie-aktywacja-online-z-algorytmem-rsa-i-bcrypt/feed/ 3 1307
Wykorzystanie własnych możliwości do tworzenia gier /wykorzystanie-wlasnych-mozliwosci-do-tworzenia-gier/ /wykorzystanie-wlasnych-mozliwosci-do-tworzenia-gier/#comments Wed, 25 Jan 2017 09:56:45 +0000 http://nastoletni.pl/?p=895 Witajcie! Pewnie nieraz macie pomysł na ciekawą grę, jednak ograniczają was pewne kwestie – mianowicie jak zrobić grafiki i dźwięk do swojej gry? Jak to wszystko rozplanować? Programiści często nie są artystami (przynajmniej plastycznie i muzycznie) i zdają sobie z tego sprawę. Co więc zrobić?

Najlepszym rozwiązaniem jest oczywiście poprosić kogoś, aby wam zrobił wizualną część gry, lecz nie zawsze jest taka możliwość. Zwłaszcza gdy nie wiemy czy gra w ogóle coś zarobi. Ale szukajcie . Mi się udało znaleźć tylko gościa od muzyki w tle. Ale jeśli jednak nie ma to co? Może jakieś stronki z darmowymi grafikami/dźwiękami? Nie najgorsze rozwiązanie, ale często są to takie bardzo oklepane, typowe materiały wykorzystywane w wielu grach. Zresztą każdy woli być oryginalny i nie kopiować. W takim razie czemu by nie spróbować swoich sił w stworzeniu zarówno efektów dźwiękowych jak i grafik? Opowiem to na przykładzie mojej gry o nazwie kodowej Cyber Attack.

A więc polecam zaczynać od mądrego konceptu. Rozplanujcie sobie wszystko! Od mechaniki, fizyki aż do grafikę i dźwięki. Policz ile plansz chcesz stworzyć, czy aby na pewno tworzenie ich nie zajmie dużo czasu (zwłaszcza gdy są to gry logiczne, gdy często generator plansz nie wystarcza, potrzebne są mądrze skonstruowane przez człowieka plansze). Jako jeden programista musisz to wszystko ogarnąć, więc postaraj się żeby nie było tego za dużo . Ile to się spotyka ciekawych projektów, które giną przez to że po prostu nudzimy się tworzeniem ich, mamy już tego projektu dosyć! To zły nawyk !

Ja chciałem stworzyć grę o tematyce survivalu. Czy to oryginalne ? Wiadomo, temat survivalu jest bardzo oklepany, jednak bardzo wciągający i jest dużo możliwości, żeby był w miarę możliwości oryginalny. Nie chcę tutaj rozdrabniać na szczegóły, jak moja gra będzie wyglądać potem, bo to byłoby nie na temat. Moim (nie)skromnym zdaniem nie będzie to kolejna nudna gra survivalowa z craftingiem i po prostu przechodzeniem plansz z domieszką zombie.

Plan rozpisany, więc zaczynamy! Zacząłem od stworzenia algorytmu wyznaczania trasy. Zaimplementowałem algorytm A*, który wydaje się być łatwym i wydajnym dla mojej gry. Stworzyłem także prostą fizykę, która odpycha zombie jeśli są obok siebie, oraz wylicza kolizję obiektów ze ścianami.

Działa. Następnym elementem jaki zrobiłem to specjalną fizykę przeznaczoną dla pocisków, ponieważ są szybkie i w czasie jednej klatki mogą przeniknąć przez całą ścianę:

Działa! Wydaje się że wygląda to koszmarnie, ale szkielet przecież mamy zrobiony! Teraz najgorsza część dla programisty czyli upakowanie grafiki i dźwięków na tym szkielecie.

Grafika. Coś czego nienawidzę i nie mogę tego ogarnąć. Pamiętajcie – nie dawajcie sobie zbyt dużych celów. Gry z gorszą ale zrobioną z serca grafiką także zdobywają szacunek wśród graczy. Polecam pixelart lub coś w stylu flat design! Proste ale jak chociaż trochę popracujecie to wyniki będą zadowalające. Do tworzenia grafiki polecam Inkscape, lub jeśli macie pieniądze (lub klient Torrent) Adobe Illustrator. Inkscape zdecydowanie wystarczy, poszukajcie paru poradników, w których nauczycie się wielu operacji na ścieżkach, co znacznie przyspieszy pracę.

Plansza. Jako pierwsze chciałem zrobić planszę. Postanowiłem, że będzie ona… sześciokątem. Od tak – pierwsza plansza, więc powinna być prosta i dla gracza i dla mnie, aby ją dobrze zaprojektować. Oto etapy tworzenia planszy:

Jak widzicie zaczęło się od malutkiego sześciokąta, potem dodałem ich więcej aby dodać głębi planszy, a na sam koniec dodałem trójkąty, wpasowujące się w stylistykę gry oraz stanowiące miejsca „rodzenia się” przeciwników.

Główny bohater. Niełatwo czasem wymyślić fajnego bohatera. Czasem warto zapytać znajomych. Zwłaszcza tych co zazwyczaj mają „głupie” pomysły. Ja zapytałem i zaproponował mi zrobić klauna na wózku inwalidzkim który będzie uciekał przed policją . Na początku pomyślałem „Boże, co za debil”, ale to nie jest przecież zły pomysł. Zawsze lepiej być oryginalnym niż tworzyć kolejną zwykłą, typową, oklepaną setki razy postać.

Gorzej z rysowaniem owej postaci. Nie jestem w tym dobry . Na szczęście kamera w grze ma być dość oddalona, także gracz nie będzie się wpatrywać w tą beznadzieję, którą zrobiłem. Przynajmniej wózek w miarę ładnie wyszedł.

Bronie. Chyba jeden z prostszych elementów. Polecam znaleźć obrazek w Google i po prostu go przerobić. Efekt jest dobry:

Przeciwnicy. Planuję na każdą mapę dać innych przeciwników. To dużo roboty, jednak gracze doceniają takie rzeczy. Przeciwników oczywiście też zrobiłem w flat design, aby wszystko komponowało się w jedną całość. Pierwsza plansza ma przedstawiać współczesne + trochę fikcyjne elementy, do których flat design bardzo pasuje:

Bonusy. W tym stylu bardzo łatwo narysować różnorodne bonusy:

Edytor plansz. Grę piszę w frameworku LibGDX. Nie ma w nim żadnego edytora plansz czy coś na jego wzór, dlatego musiałem sobie stworzyć swój prosty edytor plansz. Napisałem go w Javie wykorzystując standardową bibliotekę GUI Swing. Nie wygląda on jakoś świetnie, ale sprawdza się w roli pomocy do tworzenia plansz. Wygląda on mniej więcej tak:

O edytorze nie będę za dużo mówić, bo to trochę nie na temat. Powiem tylko że jest bardzo pomocny, szczególnie gdy zamierzacie robić kilka map do gry – nie wyobrażam sobie umiejscawiać każdego obiektu bezpośrednio w kodzie, zwłaszcza jak mamy dużą mapę. Edytor pełni u mnie tylko funkcję pomocy, do umiejscawiania obiektów, cała reszta (skrypty itp.) dzieje się już w kodzie. Zerknijmy jeszcze na to, z czego składa się edytor:

  • Points – punkty, używane do tworzenia ścian, węzłów nawigacji,
  • Navigation Nodes – węzły nawigacji, potrzebne do algorytmu A* (wyznaczanie trasy przez przeciwników). Składają się z kilku punktów, tworzą wielokąt (koniecznie wypukły),
  • Walls – ściany. Po prostu ściany w grze – każdy taki obiekt składa się z dwóch punktów,
  • BackgroundTex / Tex – tekstury. Tex wyświetlają się nad graczem, BackgroundTex pod graczem,
  • Spawners – oznaczają punkt w którym mogą pojawić się przeciwnicy.

Całą resztę mam zrealizowaną w kodzie. Wszystkie elementy interakcji, Easter Eggi teraz można łatwo wyłapać, po prostu pobierając punkt z pliku edytora i na przykład sprawdzając jego odległość od gracza.

Okej, mamy już planszę. Nie wygląda najgorzej, co nie?

Ale brakuje dźwięków – i to bardzo. Przecież nie będę ogarniać FL Studio – zajęłoby mi to parę tygodni, zresztą i tak nie mam słuchu muzycznego. Ale Audacity chyba każdy zna. Mikrofon też wiele osób ma. Ja użyłem bezprzewodowych mikrofonów Singstar do nagrania efektów dźwiękowych (siostra je używała do gry na PS3, teraz się kurzą, więc czemu by im nie nadać drugiego życia?).

Brzmi mało profesjonalnie, co nie? Pamiętajcie, gracze to nie audiofile, nie potrzebują wszędzie formatu *.flac ani idealnej jakości dźwięku. W takim razie możemy przystąpić do nagrań!

  1. Wybuch
    Oryginalne nagranie (po prostu dmuchanie w mikrofon):

    Po drobnej edycji (m. in Paulstretch, zmiana tonu):
  2. Strzał z shotguna
    Oryginał (zrobione ustami + przeładowanie):

    Po drobnej edycji:
  3. Śmierć bohatera:Oryginał/po drobnej edycji:
  4. Uderzenie w jednego z robotów ( pistolet na kulki + strzelanie w garnek )

Jak widać efekty dźwiękowe można samemu zrobić w domu. Wystarczy chcieć !

Muzyka. Muzykę w tle ogarnęli moi koledzy. Pozdrawiam Marcina Marszałka i Huberta Rzeszótko ;-). Jednak jeśli nie macie kogoś, kto by wam zrobił utwór muzyczny, to możecie skorzystać z opengameart.org . Tylko szukajcie dosyć „głęboko”, aby utwór nie powtarzał się często w innych grach. Efekt końcowy wygląda tak:

Czy to już koniec? Nie.

W planach mam dużo bardziej skomplikowane plansze, których stworzenie będzie teraz już dużo szybsze dzięki edytorze oraz dobrze napisanym kodzie programu, w którym łatwo dodawać i oskryptowywać plansze. Zamierzam dodać fabułę, zmienić styl na jeszcze bardziej arcade i stworzyć masę Easter Eggów i pobocznych misji . Oczywiście multiplayer (lokalny + sieciowy) też by się przydał.

Podsumowanie

Samemu da się stworzyć przyzwoitą grę. Jeżeli nie masz ekipy to nie jesteś od razu skazany na porażkę. Wyliczcie dobrze swoje umiejętności, nie twórzcie dużych projektów, które od razu są skazane na porażkę, gdyż znudzicie się podczas ich tworzenia. Lepiej jest robić coś mniejszego , ale równie dobrego – zawsze są to też jakieś projekty które na pewno przydadzą się wam w waszym portfolio. Często spotyka się ambitnych młodych programistów, którzy nie mają tak naprawdę żadnego projektu skończonego . Czasami trzeba się wziąć w garść i dokończyć jakiś projekt mimo, iż wam się nie chce. Jak to mówił św. Benedykt z Nursji: Ora et labora (łac. módl się i pracuj). Przecież w pracy też nie będziecie mogli sobie wymyślać. Dzięki małym projektom nauczycie się wiele, dopełnicie portfolio oraz zyskacie szansę na zarobek (bo czemu niby takiej gry nie wrzucić do serwisu Google Play lub Steam?).

]]>
/wykorzystanie-wlasnych-mozliwosci-do-tworzenia-gier/feed/ 2 895
Ledy głosem sterowane – Rozpoznawanie mowy /ledy-glosem-sterowane-rozpoznawanie-mowy/ /ledy-glosem-sterowane-rozpoznawanie-mowy/#comments Sun, 04 Sep 2016 13:59:27 +0000 http://nastoletni.pl/?p=124 Wprowadzenie

Przeglądając ciekawe itemy z chin natrafiłem na dość tanią adresowaną taśmę led. Jak to już bywa czasem najpierw się kupuje, a potem myśli. Początkowo miało to służyć jako ambilight, lecz ostatecznie przykleiłem pod półką, która wisi nad biurkiem. Przewiduje 4 opcje sterowania: sterowanie głosowe, panel www, klaśnięcie, przyciski/switche. Całość będzie oparta o Raspberry Pi Zero. Przedstawiane tutaj kody mają jedynie pokazać drogę do stworzenia aplikacji, lecz nie podawać tego na tacy, więc nie nastawiajcie się na kopiuj-wklej 🙂

Sterowanie głosowe

Od początku całość miała służyć jako pole manewrowe przy nabywaniu nowych umiejętności, z tego powodu też postanowiłem sam wszystko napisać. Poza programowaniem potrzebna będzie też podstawowa znajomość praw fizyki przy budowie układu elektronicznego (dotyczyć tego będzie osobna część tego devloga).

Aplikacja na telefon

Jako iż posiadam telefon z Androidem, tylko pod ten system powstanie aplikacja. Jako IDE wybrałem IntelliJ Idea z powodu przyzwyczajenia oraz tego, że Android Studio jest jak dla mnie za bardzo przepełnione dodatkami. Do rozpoznawania mowy wykorzystana została odpowiednia klasa z API systemu . Nie przypadła mi do gustu wizja wyskakującego okienka, dlatego przeszukałem Internet w poszukiwaniu rozwiązania aby tego uniknąć. Pomijam tutaj kwestię stworzenia projektu z wygenerowanym Activity.

Aby skorzystać z opcji rozpoznawania mowy potrzebujemy zezwolenia systemu do nagrywania audio. W tym celu dodajemy do pliku AndroidManifest.xml w środku elementu manifest linijkę: <uses-permission android:name="android.permission.RECORD_AUDIO"/> .

Kolejnym krokiem jest przygotowanie wyglądu naszej aplikacji (w zależności od potrzeb jest to layout activity albo fragment). Ja postanowiłem nie dawać użytkownikowi możliwości wyłączenia nagrywania tylko ustawiłem sztywno ilość czasu na podanie polecenia, dlatego użyłem zwykłego przycisku (w przeciwnym przypadku można pokusić się o 2 przyciski ON/OFF albo ToggleButton).

Gdy już nasza aplikacja jako tako wygląda, należy jakoś tchnąć w nią życie. W metodzie onCreate należy stworzyć nową instancję wyżej wymienionego SpeechRecognizer’a:

Tworzymy klasę RecognitationListener implementującą metody z androidowego interfejsu RecognitionListener . Teoretycznie potrzebna nam jest tylko metoda onResult (fajnie by było zaimplementować też obsługę błędów, lecz to już zależy od waszych preferencji). Teraz w metodzie onCreateView musimy dopisać tuż przed return view; kod dodający listener do przycisku:

voice_activation_button to ID naszego przycisku przypisane w pliku xml wyglądu. W klasie należy stworzyć także metodę która jest wywoływana wewnątrz tego listenera, czyli onVoiceActivation :

Uruchamia to proces wykrywania mowy, a z opóźnieniem 5000 milisekund zatrzymuje go.

W klasie listenera rozpoznawania mowy, w metodzie onResults wszystkie uzyskane wyniki pobieramy np. tak: ArrayList<String> matches = results.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION); .

Wpadłem później także na pomysł aby zamiast przycisku dać ImageButton a za nim wstawić okrągły progressbar pokazujący ile czasu minęło. Do tego celu wykorzystałem bibliotekę CircleProgress .

Screen aplikacji na tym stadium rozwoju:

To by było na dziś tyle. W kolejnej części devloga przedstawie jakiś sposób na przypisywanie otrzymanych wyników do danego szablonu i wykonywanie w związku z nim jakiejś akcji.

]]>
/ledy-glosem-sterowane-rozpoznawanie-mowy/feed/ 1 124