Postęp prac nad projektem – to działa!

Dzisiaj chciałem opisać w skrócie to, co udało mi się zrobić w ostatnim czasie. Jest to ostatnia aktualizacja przed końcem konkursu. Najważniejsze się udało, czyli możliwość gry dla dwóch drużyn! W najbliższym czasie postaram się przeprowadzić testy w gronie znajomych. Później udostępnię aplikację dla wszystkich. Zaczynamy!

Tryb gry i dostępne opcje

Z dwóch trybów gry, które były w założeniach, udało mi się zrobić jeden. Jest to standardowa gra dla dwóch drużyn, gdzie pierwsza, która odkryje swoje pola, wygrywa. Rozgrywka sterowana jest za pomocą jednego urządzenia, które należy do założyciela rozgrywki. Inni gracze mogą podglądać co się dzieje na swoich urządzeniach. Dzięki temu można np. odpalić grę w salonie na telewizorze. Jest także niezbędna dla kapitanów mapa, która wskazuje, które pola należą do poszczególnych drużyn.

Mapa dla kapitanów

Mapa gry z odkrytymi polami

Mapa gry jest identyczna do planszy, ale nie można na niej wykonywać żadnych operacji. Z poziomu planszy możemy zakończyć naszą turę. Na obu ekranach w górnym panelu wyświetla się informacja o tym, która drużyna wykonuje obecnie ruch.

Przycisk zakończenia tury

Zakończenie tury i zmiana drużyny

Na podstawie skojarzeń podanych przez kapitana gracze odgadują, które pola miał na myśli. W przypadku, gdy trafią na puste pole lub pole drużyny przeciwnej, ich tura dobiega końca. W przypadku, gdy odkryją własne pole, mogą kontynuować rozgrywkę. Po odkryciu wszystkich swoich pól, wygrywają. Gdy trafią na jedną z pułapek, gra dobiega końca i wygrywa przeciwny zespół. Oczywiście wszystkie akcje synchronizowane są na wszystkich podłączonych urządzeniach.

Odgadywanie pól

Odgadywanie pól i zmiana drużyny przy złym ruchu

Tym razem nie będę opisywał żadnych technicznych szczegółów. Wszystko z czego korzystałem było opisane w moich poprzednich postach w ostatnich tygodniach. Dotyczyły one takich technologii i bibliotek jak SignalR, Asp.Net Core, Angular lub Dapper.Net. Nowe technologie nie pojawiły się w ostatnim czasie, więc chyba nie ma potrzeby powielać tutaj tych informacji.

Co jest jeszcze do zrobienia?

Sporo. Tak mógłbym w skrócie napisać. Pomimo, że gra sama w sobie działa, bardzo dużo jest jeszcze do zrobienia. Tryby dla większej ilości graczy to na pewno jedna z pierwszych rzeczy które są w moich planach. Różne mniejsze poprawki które ułatwiają życie: możliwość wchodzenia do gier znajomych, social logins(Facebook, Google etc), uruchamianie nowej gry jednym kliknięciem po zakończeniu obecnej rozgrywki lub poprawki w interfejsie. To tylko część funkcjonalności, które także chciałbym żeby znalazły się w finalnej wersji. Tryb gry, gdzie każda drużyna steruje swoim urządzeniem to także coś, co chciałbym dodać. Niestety, to raczej będzie zaimplementowane w drugiej kolejności. Ostatnia rzecz to natywny klient na system android. Szczerze, to nie wiem czy ten pomysł wejdzie w życie, zobaczymy. Obawiam się, że prędzej zajmę się nowym projektem, ale kto wie!

Podsumowanie

Plan minimum został wykonany, chociaż po cichu liczyłem na więcej. Wiadomo, życie potrafi brutalnie zweryfikować to, co sobie założymy. Patrząc na całość, jestem w miarę zadowolony z tego co zostało zrobione. Za jakiś czas, już po konkursie, na pewno pochwalę się pozostałymi rzeczami które dodałem. Na razie mam nadzieję, że wśród moich znajomych znajdą się dobrzy ludzie, którzy zechcą ze mną przetestować moją grę!

WordHunt – plansza do gry przy użyciu Flex Layout

Dzisiaj pokażę jak wygląda plansza do gry. Przyznaję, że spodziewałem się że ten element zostanie skończony już dawno, ale wyszło jak wyszło. Zadanie to sprawiło mi więcej problemów niż się spodziewałem, ale jestem w miarę zadowolony z tego co otrzymałem.

Plansza i jej elementy

Jeżeli chodzi o styl, pozostawia ona jeszcze sporo do życzenia, ale pola na planszy zachowują się tak jak chciałem. Dostosowują się do wielkości ekranu, w pełni wypełniając całą dostępną przestrzeń.

Board_mobile

Plansza do gry 5×5 na małym urządzeniu

W przypadku, gdy plansza posiada więcej słów i pola nie będą się mieścić w całości, wyrazy zostaną ucięte. W przyszłości czcionka będzie zmieniać rozmiar w zależności od wielkości ekranu i długości słowa, dzięki czemu unikniemy takich sytuacji. Na troszkę większych urządzeniach np. tabletach wyświetlanie działa tak jak należy, nawet na największej możliwej planszy 8×8.

board2_tablet_8x8

Plansza do gry 8×8 na tablecie

Teraz, gdy plansza jest już w takim stanie, zaczynam dodawać pozostałe fragmenty. Na tą chwilę jest jeszcze menu boczne, gdzie będą dodatkowe informacje oraz opcje. Żeby nie marnować miejsca, jest ono ukryte. Gdy je otworzymy, menu pojawia się nad planszą.

board3_sidenav

Plansza do gry – menu boczne

Obecnie obsługuję jedynie przycisk pominięcia rundy, dzięki czemu kolejna drużyna może wykonać ruch. Jutro dodam obsługę „odsłonięcia” pola, czyli jedną z najważniejszych rzeczy do zrobienia.

Jak to wygląda pod maską?

Skoro już zademonstrowałem jak plansza wygląda, pokażę teraz jak to działa. Wygląd aplikacja zawdzięcza komponentom Angular Material Design, natomiast sam layout planszy działa na bazie Flex Layout. Obie biblioteki są w wersji beta, ale większych bugów nie znalazłem. Muszę jednak powiedzieć, że próg wejścia do tych technologi jest troszkę większy niż to, co spotyka nas przy pierwszym kontakcie z popularnym konkurentem, czyli poczciwym Bootstrapem. Nie ma jednak tragedii i coś nawet da się z tym zrobić.

Ekran gry podzielony jest na kilka elementów. Kod głównego komponentu, na którym umieszczone są wszystkie elementy wygląda następująco:

Kontener, w którym wyświetlane jest menu boczne, toolbar gdzie jest tytuł aplikacji, a będą także ikony drużyn oraz plansza do gry. Tutaj nie musiałem dużo majstrować przy stylach. Jedynie toolbar wymagał drobnych modyfikacji.

Ręcznie modyfikuję rozmiar górnego paska aplikacji, ponieważ zmieniał się on przy zmianie wielkości okna. Powodowało to, że plansza do gry odrobinę się przesuwała i wychodziła poza ekran.
W bazowej klasie ze stylami zdefiniowałem mixin, który wykorzystujemy w innych komponentach, tak jak powyżej. Dzięki temu dokładnie i w łatwy sposób sterujemy stylami w zależności od wielkości ekranu. Był on szczególnie potrzebny w przypadku planszy, ponieważ przesuwała się ona w dół o wysokość paska górnego.

Sam kod planszy jest dosyć krótki, ale powiedziałbym że intensywny. Mamy tutaj dwa layouty fxLayout: column i row. Kolumnowy sprawia, że każdy rząd zabiera odpowiednią ilość miejsca w pionie. Dzięki temu plansza wypełnia całą przestrzeń. Każdy rząd składa się z komponentu game-field, który wyświetla słowa i odpowiadać będzie za kliknięcie na element.

Wartość fxFlex, która definiuje nam jak dużo przestrzeni ma wykorzystać każdy blok, wyliczana jest w kodzie po załadowaniu danych gry. Na podstawie ilości rzędów i kolumn dodajemy odpowiednią wartość do każdego elementu. Wartość bazowa baseFlexValue, od której liczymy wielkość elementów sprawia, że otrzymamy małe odstępy pomiędzy polami, które będą zmieniały wielkość w zależności od wielkości ekranu. Gdyby było tam 100, nie mielibyśmy przerw. Można to oczywiście zaprogramować inaczej, np. poprzez margin pól gry, ale na razie zostawię to tak jak jest.

Jeżeli chodzi o stylowanie tego komponentu, trzeba było dodać obliczenie wielkości planszy z uwzględnieniem paska górnego. Bez tego plansza była przesunięta o jego wysokość.

Tutaj uwaga! calc nie przyjmuje zmiennych z SASSa, chyba że opakujemy je w nawiasy klamrowe poprzedzone hasztagiem. Trochę mi zajęło zanim na to wpadłem… Szczerze, to boję się tego ruszać, bo już raz coś zmieniłem, co sprawiło że zepsułem cały ekran gry i naprawiałem go dobrych kilka minut. Szybko nauczyłem się robić częste commity, o których zdarza mi się zapomnieć. Kodu nie jest wiele, ale jako że nie jestem mistrzem CSS, trochę mi zajęło znalezienie takiej konfiguracji.

Podsumowanie

Plansza w wersji beta gotowa, teraz dodanie obsługi wykrywania pól oraz mapy dla kapitanów. Gdy to będzie gotowe, MVP projektu będzie skończone. Mam nadzieję że przed końcem konkursu!

Postęp prac nad projektem WordHunt

Cześć, dziś chciałem podzielić się tym, co udało mi się zrobić do tej pory. Zauważyłem ostatnio, że robię wszystko żeby nie ukończyć tego projektu na czas… Rzeczy które powinienem zostawić na koniec, lub inne mniej ważne elementy programu zostały po części zrobione, a o samej rozgrywce ani widu, ani słychu. Skoro już zdałem sobie z tego sprawę, może teraz uda się pójść w dobrym kierunku. No, ale do rzeczy.

Model bazy danych

Zaczynam od rzeczy która jest akurat bardzo ważna. Dane oczywiście muszą być gdzieś przechowywane. Tutaj nie wymyślałem niczego interesującego, używam Microsoft SQL Server. Użytkownicy przechowywani są w tabelach, które generowane są automatycznie gdy używamy ASP.NET Core Identity. Wszystko byłoby pięknie, ale gdy projektowałem własne tabele, jako identyfikatora użyłem typu danych bigint, czyli long w C#. Domyślnym identyfikatorem użytkownika generowanym przez system był string, a w bazie danych nvarchar(450). Nie podobało mi się to, że w systemie będę miał dwa typy identyfikatora. Musiałem więc to zmienić. Zadanie to nie jest specjalnie trudne, ale zajęło mi trochę więcej czasu niż się spodziewałem. Myślę że napiszę o tym osobny post, dlatego nie będę się rozpisywał na ten temat tutaj. Tabele zostały zmienione, wszystko gra. Możemy przejść dalej. W systemie naturalnie muszą się znaleźć także moje tabele, nie tylko te domyślne. Tak więc usiadłem i zacząłem je rozpisywać. Zmieniałem je kilkukrotnie, ale mniej więcej udało mi się ustalić jak będą wyglądały, przynajmniej te podstawowe dotyczące rozgrywki.

Schemat tabel bazy danych związanych z rozgrywką

Tabele związane z rozgrywką.

Jak widać, wszystko sprowadza się do powiązania z tabelą Games, gdzie będą przechowane ustawienia gry. GameStatuses odpowiadać będzie za aktualny stan rozgrywki, czyja tura aktualnie trwa, czy została już zakończona etc. GameTeams, jak nazwa wskazuje, przechowuje dane drużyn które biorą udział w grze. W przypadku, gdy grą steruje tylko jedna osoba, nie jest ona powiązana z użytkownikiem. GameFields to pola na których znajdują się słowa, ich rodzaj, czy są przypisane do któregoś zespołu i czy zostało one odkryte. GameMoves to historia wszystkich ruchów, a GameClients to urządzenia które są połączone z rozgrywką. Jestem pewny że tabel przybędzie, ale na razie tyle powinno wystarczyć, wyjdzie w praniu.

Kolejne tabele to takie przechowujące słowa. W grze chce mieć możliwość wyboru języka, więc dodałem tabelę z językami, oraz kategorie żeby łatwiej było nimi zarządzać. Tabele te są dosyć proste, nic szczególnego.

Schemat tabel powiązanych ze słowami.

Tabele związane ze słowami.

WebAPI do zarządzania słowami

Tak, bardzo ważna rzecz w grze która jeszcze nie działa. Dodałem możliwość zarządzania językami, kategoriami i słowami. Jest ono zabezpieczone w taki sposób, że jedynie użytkownicy będący administratorami mają do nich dostęp. Tabele ze słowami są pomocne od początku, ale zarządzanie nimi mogło poczekać… Już się zabierałem za robienie interfejsu użytkownika do tego, ale na szczęście się powstrzymałem. Dalszy rozwój tej części aplikacji odkładam na później.

Routing aplikacji webowej

W poprzednich postach opisywałem routing w Angularze. Na tej samej zasadzie zrobiłem go w mojej aplikacji. Na stronę główną mają dostęp wszyscy, ale żeby zobaczyć coś więcej trzeba się zalogować. Do panelu administracyjnego dostęp mają tylko administratorzy. Wykorzystałem tutaj mechanizm o którym nie wspomniałem w tamtym poście, RouteGuards. Na pewno opiszę go w drugiej części posta dotyczącej routingu. A poniżej przykład:

Podsumowanie

Rozwój aplikacji nie przebiegał ostatnio w najlepszym tempie. Wydaje mi się jednak że zrobiłem dużo nudnych rzeczy, które i tak trzeba by było prędzej czy później dodać (może nawet bardzo później…). Dzięki temu teraz będę mógł przejść do konkretów i development przyśpieszy. Kolejne krotki to dodanie możliwości uruchomienia gry, czyli strona z wyborem ustawień, następnie zapisanie ich do bazy danych. Później przyjdzie czas na system rozgłaszania wykonanego ruchu przez użytkownika do innych klientów. Ale o tym w kolejnych postach.