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!

SignalR w ASP.NET Core i Angular – przykładowa implementacja

Niedawno opublikowałem wpis na temat instalacji biblioteki SignalR w projekcie ASP.NET Core z wykorzystaniem Angulara. Dzisiaj chciałem kontynuować ten temat na przykładzie z mojego projektu. Zademonstruję działającą implementację po stronie serwera.

Link do poprzedniego wpisu: SignalR w ASP.NET Core – instalacja i uruchomienie

Konfiguracji ciąg dalszy

Chociaż konfiguracja jaką podałem w ostatnim wpisie wystarczała do działania, w moim rozwiązaniu musiałem dodać jeszcze kilka ustawień. Na pierwszy ogień poszedł CamelCasePropertyNamesContractResolver, dobrze znany wszystkim, którzy tworzyli kiedyś API w Asp.Net. Dzięki niemu, obiekty zwracane są z API w notacji CamelCase. Jest to domyślne ustawienie w .NET Core, ale jako że my mamy podpiętą bibliotekę ze starszej wersji frameworka, musimy ustawić to ręcznie. Przykładowa implementacja poniżej.

Następnie rejestrujemy nasz serializator w pliku Startup.cs.

Rozsyłanie eventów z poziomu backendu aplikacji

Wysłanie zdarzenia z serwera może odbyć w dwóch przypadkach:

  • Bezpośrednio z naszej klasy, dziedziczącej po Hub, w momencie gdy klient wywołuje zdalną metodę na serwerze
  • Po wywołaniu zwykłej metody HTTP w API, pośrednio poprzez Hub

O ile w pierwszym przypadku jest to bardzo proste, to ten drugi wymaga dodatkowego nakładu pracy. Załóżmy że nasz Hub ma tylko jedną metodę.

Klient, w momencie gdy wywoła metodę Subscribe, zapisze się do grupy odpowiadającej identyfikatorowi gry. Zwrócone mu zostanie id jego połączenia. Wewnątrz klasy Broadcaster możemy wysłać zdarzenie o nowej subskrypcji do wszystkich klientów. Taki przykład podałem w poprzednim poście, dlatego tutaj to pomijam (nie jest to potrzebne w moim przypadku). Efekt który chcemy tutaj osiągnąć to wysłanie do każdego klienta informacje o tym, że gracz wykonał ruch. Jako przykład posłuży tutaj zakończenie tury przez gracza.

Podczas wywołania tej akcji kontrolera, wywołujemy metodę SkipRound. Wewnątrz niej, po tym jak nastąpi zmiana kolejki, chcielibyśmy poinformować wszystkich graczy o tym fakcie. Nasz Hub to nie jest zwykła klasa, którą można po prostu wstrzyknąć do naszych obiektów. W tym celu musimy odwołać się do klasy ConnectionManager dostarczonej przez SignalR.

Dodałem klasę EventBroadcaster, która będzie odpowiedzialna za przekazywanie informacji do klientów bezpośrednio z serwera. Posiada ona obiekt HubContext, który umożliwia takie działania. Dostępny jest po wywołaniu metody GlobalHost.ConnectionManager.GetHubContext. Ponieważ jej wywołanie jest dosyć czasochłonne, klasę EventBroadcaster rejestrujemy w systemie jako singleton.

Dzięki temu, w dowolne miejsce aplikacji możemy dodać obiekt implementujący IEventBroadcaster i wysyłać do klientów interesujące nas zdarzenia. Przykład poniżej.

Podsumowanie

Dzisiaj zademonstrowałem przykładową implementację rozsyłania zdarzeń w aplikacji po stronie serwera aplikacji. W kolejnym wpisie zajmiemy się implementacją po stronie klienta w Angularze. Dzięki za uwagę i zapraszam ponownie!

Co nowego w projekcie – aktualizacja

Od ostatniej aktualizacji minęły prawie dwa tygodnie, więc pora na krótkie podsumowanie tego, co udało mi się zrobić przez ten czas. Niestety dla projektu, przez dwa ostatnie weekendy byłem poza domem, więc nie wszystko co sobie zaplanowałem zostało zrealizowane. Jakieś postępy jednak są, dlatego zapraszam do lektury!

post-background

Exception handling w WebAPI

Przechwytywanie błędów to było coś, co mi się nie podobało w moim rozwiązaniu. Wiadomo, na początku nie było to istotne, ale w miarę jak ilość elementów aplikacji rosła, trzeba było pomyśleć nad czymś sensownym. Domyślnie, gdy w aplikacji pojawiał się błąd, wyświetlana była strona z jego treścią, standardowe zachowanie. Jako, że w API nie jest to pożądane, dodałem do aplikacji middleware który przechwytuje wszystkie wyjątki. W ten sposób nie trzeba się bawić w przekazywanie statusów z metod domeny do kontrolera – gdy trzeba rzucamy wyjątek odpowiedniego typu, a resztą zajmie się właśnie ten fragment kodu.

Teraz tylko dodajemy tą klasę do pipeline w ASP poprzez metodę UseMiddleware<ExceptionHandlingMiddleware>() i gotowe. Wszystkie wyjątki zostaną przechwycone i zwrócone do klienta. W zależności od rodzaju wyjątku, zwrócony zostanie inny status błędu.

Obsługa SignalR

Mój poprzedni post jest w całości poświęcony tej tematyce, a w szczególności wspominam tam o wszystkich problemach jakie mnie spotkały podczas dodawania tej biblioteki. Warto jednak o tym tutaj wspomnieć. Dzięki temu moja aplikacja będzie mogła działać w czasie rzeczywistym, rozsyłając komunikaty pomiędzy wszystkich klientów. O tym, jak dokładnie zostało to zaimplementowane po stronie klienta i serwera opowiem w kolejnych postach.

Praca nad widokiem aplikacji

W miarę jak dodaje kolejne elementy, powoli buduje interfejs użytkownika. Na razie ten element wyraźnie kuleje, ale nie jest to najistotniejsze w tym momencie. Wygląd na tą chwilę nie jest istotny, ważne żeby dokładać kolejne elementy funkcjonalne. Obecnie pracuje nad planszą do gry. Jest to dosyć problematyczne zadanie, ponieważ staram się żeby była możliwość gry na tabletach i smartfonach. Ciężko pomieścić tak wiele pól na małych ekranach tak, żeby były one czytelne. Do tego CSSy to nie jest moja mocna strona… Na razie poprawki tego fragmentu zostawiam na później. Nie będę się także chwalił obecnym wyglądem aplikacji, myślę że w przyszłym tygodniu coś już pokażę.

Dalsza praca z Dapperem

Tej technologii też poświęciłem już cały wpis, ale chciałem pokazać kolejną metodę którą udostępnia. Jest to QueryMultipleAsync. Tak bardzo mi się to spodobało, że postanowiłem o tym tutaj wspomnieć. Po wejściu na planszę klient chce pobrać informacje o obecnej grze: pola, status, drużyny etc. W tym celu odpytuje API. Zamiast wielu zapytań do bazy danych, całą tą operację da się zrobić w jednym zapytaniu. Można by to wydzielić na wiele mniejszych zapytań, ale stwierdziłem że to dobry moment na wykorzystanie tej metody. Poniżej implementacja.

AccessQueries w powyższym przykładzie to po prostu treści zapytań SQL. Z wielu małych zapytań generowane jest jedno wielkie. Wyniki są mapowane do poszczególnych elementów obiektu Game. W SQL Server Profilerze wyraźnie widać, wszystko wykonane w jednej operacji, bardzo miła sprawa.

Sql Server Profiler - wynik zapytania QueryMultipleAsync

Zrzut ekranu z SQL Server Profiler

Podsumowanie

Podkręciłem ostatnio tempo pracy nad projektem, dodatkowo zapowiada mi się pierwszy wolny weekend w tym miesiącu, dlatego mam spore oczekiwania względem tego, co uda mi się zrobić w najbliższych dniach! Stay tuned!