Efekt jest łatwy do przewidzenia. Wraz z pierwszym push na bitbucket poleciał cały cache projektowy jaki tworzy silnik oraz środowisko programistyczne. Oczywiście po jakiejkolwiek zmianie kodu git chciał zaktualizowane wysyłane i śledzone pliki cache listując przy okazji wszystkie wykryte zmiany. Rejestrował ich setki co powodowało skuteczne zamulenie zwykłego commita. Po dłuższej walce udało się to ogarnąć i utworzyć plik .gitignore wycinający zbędne zasoby oraz przeczyścić repo.
Strefa Zero
20 listopada 2024
Pisałem, groziłem
Efekt jest łatwy do przewidzenia. Wraz z pierwszym push na bitbucket poleciał cały cache projektowy jaki tworzy silnik oraz środowisko programistyczne. Oczywiście po jakiejkolwiek zmianie kodu git chciał zaktualizowane wysyłane i śledzone pliki cache listując przy okazji wszystkie wykryte zmiany. Rejestrował ich setki co powodowało skuteczne zamulenie zwykłego commita. Po dłuższej walce udało się to ogarnąć i utworzyć plik .gitignore wycinający zbędne zasoby oraz przeczyścić repo.
17 listopada 2024
Ładniejsze okna dialogowe
Jak wszyscy wiemy wygląd okien dialogowych w grze to bardzo ważna sprawa. Fajnie, aby były czytelne przejrzyste i nie wymagały analizy o co w nich w ogóle chodzi. W mojej gierce okienka były raczej... brzydkie. Musiałem coś z tym zrobić. Wymagało to zrozumienia jak pracować w Godot z elementami, z których robi się gui.
Po przewinięciu kilku (słabych) tutoriali oraz obejrzeniu jednego dobrego (link na końcu wpisu) wyszedłem od takiego menu aktualnych questów
do nowego okienka
Jak widać nadal jest true/false dla postępu w pod-zadaniach, ale całość wygląda już dużo lepiej i nie zachodzi na pół ekranu |
A tutaj nowe menu dialogowe:
Mam jeszcze problem ze skalowaniem wielkości okna dialogowego względem jego zawartości, ale przynajmniej już nie straszy jak wersja bez tekstur. |
Do wykonania teksturowanych grafik dla okien dialogowych wykorzystałem: Kenny ui-pack-adventure
Najlepiej wytłumaczone zasady tworzenia menu znalazłem u CocoCode na youtubie pod linkiem Create MAIN MENU for your Godot game
Inne tutoriale z tego kanału także są bardzo dobre i jeśli siedzisz przy godot oglądając znajdujące się tam wrzutki możesz zyskać sporo wiedzy.
15 listopada 2024
Quest Completed
Niemal udało mi się skończyć kod służący obsłudze questów. Na tę chwilę bohater nadal jest w stanie zająć się tylko jednym zadaniem, ale kod jest już przygotowany pod to, aby mógł ich otrzymać więcej. Poprawiłem mechanizm rozpoznawania, którą aktualnie kwestię powinien otrzymać od NPC w efekcie zmienił się format jsona, z którego czytam dane dla zadań przekazywanych graczowi. Dla urozmaicenia dorzuciłem muzyczkę grającą w pętli w tle podczas rozgrywki i zmieniającą się przy zakończeniu questa u NPC. Mechanizm umożliwia łatwe przełączanie utworów podczas wydarzeń z gry (przejścia między obszarami, wejścia w dany obszar czy np. walki)
Aktualny wygląd jsona opisującego sam quest z możliwościami dialogowymi poniżej. Nie do końca jestem przekonany w trzymaniu wskazania na startowy dialog dot. questa w tym pliku (pole EntryDialog) ale na razie spełnia on swoją funkcję.
Jak łatwo można się domyślić RevisitDialog odpowiada za to co mówi NPC jeśli odwiedzimy go przed zakończeniem zadnia zaś CompletedDialog za dialog gdy wszystkie podzadania są zakończone i otrzymamy nagrodę.
Tutaj widać, że pod-zadania dla questa zostały wykonane - oba oznaczone jako TRUE. |
Kolejno podsumowanie questa |
I kolejne podejście - brak zadań u tego NPC. |
Z ciekawych rzeczy które musiałem znaleźć to przypięcie się z poziomu kodu c# do sygnału przypisanego do węzła z godot.
13 listopada 2024
I naprawiłem
Pisanie poprzedniego postu pomogło mi w zrozumieniu co schrzaniłem w kolizjach. W sumie na plus. Okazało się, że miałem kilka błędów w sposobie definiowania warstw kolizyjnych na moich obiektach. No dobra, było poplątanie z pomieszaniem i działało tylko dlatego, iż praktycznie wszystkie elementy kolidowały ze czymkolwiek co było na ekranie.
Z tego też powodu musiałem utworzyć nową scenę testową ponieważ poprzednia do niczego się nie nadawała - były min. błędy w definicji kolizji TileMapy które uniemożliwiały poprawienie nieprawidłowo działających zderzeń z postacią.
Aktualna scena testowa jest trochę biedna, ale działa jak trzeba. A i zmieniłem rysunki dla skrzynek oraz jak widać moje gui wyświetlające aktualny quest potrzebuje sporo miłości.
Obszar testowy gry w wersji 2.0. |
p.s.
Bohater nadal nie potrafi podnosić przedmiotów z ziemi, a od tego problemu rozpocząłem zabawę.
11 listopada 2024
Popsułem grę ...
Trochę popatrzyłem w kod i podczas implementacji podnoszenia przedmiotów (w kontekście realizacji questa) zacząłem analizować w jaki sposób mój bohater wykrywa kolizję z przedmiotami w grze. No i wyszło mi, że coś dziwnie. Stąd wyłączyłem kolizyjność na poszczególnych warstwach oraz maskach (czymże są krok dalej) i nie potrafię jej teraz poprawnie włączyć!
Aktualnie postać przenika przez ściany, nie umie już otworzyć drzwi, pułapka inicjuje się na warstwie do której nie należy. Totalnie poplątałem warstwy kolizji i maski kolizji, a teraz mozolnie próbuję to naprawić.
Trochę teorii
Model kolizji w godot opiera się na warstwach. Mamy warstwy, w której przedmiot z którym możemy wejść w interakcje istnieje w grze czyli tzw. CollisionLayer oraz warstwę, z którą przedmiot powinien kolidować tzw. Collision Mask. Do wyboru są możliwe 32 warstwy (!) stąd wynika, że ta funkcjonalność może być niebywale skomplikowana.
Tak wygląda to w oknie edytora |
I teraz, aby nasz bohater potrafił kolidować np. ze ścianami, musi posiadać w definicji węzła CollisionShape2D dopasowane maski CollisonMask odpowiadające za CollisionLayer warstwy ścian.
Dla ścian definiujemy iż są w grze na pierwszej warstwie kolizyjnej CollisionLayer :
Warstwy kolizji można samodzielnie nazywać stąd też WALL |
A nasz bohater (poniżej) musi mieć odpowiednio zdefiniowaną CollisionMask:
Aby kolidować ze ścianą w ramach CollisionMask wybieramy warstwę pierwszą. Zaznaczona CollisionLayer o numerze 2 to warstwa na której znajduje się gracz |
Tyle teorii
W ramach sprawdzenia napisałem projekcik gdzie postać po prostu łazi i koliduje. Okazało się, że czasem dziwacznie reaguje na zdefiniowane na ekranie obszary pewnie przez moją niewiedzę. Cóż walka o zrozumienie tematu trwa ponieważ dotyczy to całej rozgrywki, a nie chciałbym aby postać kolidował z każdym elementem na ekranie i każdy ekran zawsze był aktywny jako kolidujący z wszystkimi warstwami.
Tutaj kilka przykładów jak działają kolizje.
Gracz (CharactedBody2d) nie jest przypisany do żadnej warstwy kolizyjnej (CollisionLayer) ani nie sprawdza warstw z którymi może się zderzać (MaskLayer) - trzeba przyznać że dupne to nazewnictwo ale nie przeskoczę tego.
Efekt widać poniżej -
Gracz (postać w ciemnym kolorze) wpada POD postać NPC |
NPC ma CollisionLayer w warstwie 5 (warstwę opisałem jako NPC) |
Teraz podobnie dla ścian - gracz nie koliduje ze ścianami (warstwa 1 w CollisionLayers) dla TileMap |
Gracz przenika ścianę |
W edytorze to warstwa MaskLayer = 1 opisana jako WALL |
Po odznaczeniu maski dla warstwy ścian (widać odznaczony checkbox na ekranie) gracz przestaje przenikać przez ściany |
Ale za to pojawił mi się problem z wykrywaniem kolizji z użyciem tzw RayCast2D czyli promienia który powinien wykrywać kolizje z poszczególnymi warstwami. I o ile wykrywa kolizję z warstwą MUR lub NPC ... to nie wykrywa mi kolizji z elementami, które są niby tak samo zdefiniowane a podpięte są pod węzły o typie Area2D posiadające węzeł kolizyjny CollisionShape. I nie wiem dlaczego. Cóż posiedzę nad tym i może na coś wpadnę.
A bohater miał tylko podnosić przedmioty, a teraz to ... szkoda gadać
p.s.
Już wiem dlaczego RayCast2D nie wykrywał kolizji z Area2D ... ma na to specjalną opcję do odklikania nazwaną Areas. Bez niej nie wykrywa takich obszarów. Poniżej widać ją niemal na samym dole zrzutu ekranowego
I teraz przykład co było nie tak
I tutaj poprawna sytuacja - RayCast2D wykrył kolizję na warstwie 4 z elementem Area2D po odklikaniu opcji Areas w konfiguracji promienia. Widać to po tym jak świeci się na czerwono. |
07 listopada 2024
Co u Sladuma cd - Quest
Mozolnie pisze (zbyt) skomplikowany mechanizm questów. Kod wygląda na tworzony do poważnej gry, a nie jako element gry prostej. Ale cóż zrobić. Tak wyszło.
Aktualny poziom zaawansowania pozwala na wyświetlenie zadania głównego, jego pod-zadań do wykonania oraz obsługa taska o roboczym typie DISCOVERY, którego celem jest udanie się do określonego na mapie obszaru.
Kolejny podtask, który posiada prawie całą obsługę jest FETCH - czyli zbieractwo przedmiotów. W tym celu będę musiał stworzyć podstawową implementację plecaka postaci, a ta nie istnieje nawet w powijakach.
A tak powyższe wygląda po zaimplementowaniu :
Po dotarciu do interesującego nas obszaru - tutaj nazwanego Świątynią Seta, status zadania o typie DISCOVERY zmienia się na true , czyli zostało wykonane! |
06 listopada 2024
Jak zatrzymać gracza w godot, ale nie grę?
01 listopada 2024
C# i JSON grafy skierowane i konwersacje w grze
Podczas pisania kodu do dowolnej gdy trzeba zrobić system odczytu plików związanych z jej logiką. Mogą to być zapisane stany gry, statystyki potworów, opisy postaci, pliki lokalizacji, struktura mapy etc... Możliwości jest multum. Stąd też potrzebny jest odczyt oraz odpowiednie formatowanie przechowywanych danych. Ze względu na składnię i lekkość oczywiście najlepiej użyć plików tekstowych w formacie json.
Taki też problem pojawił się u mnie gdy chciałem utworzyć pierwszą konwersację Sladuma ze zleceniodawcą. W skrócie co trzeba było zrobić:
- przypomnieć sobie czym są grafy skierowane, z grafów łatwo wyjść do konwersacji...
- stąd stworzyć plik w formacie json zawierający opis pojedynczej konwersacji powiązany z daną postacią gdzie wstępna struktura obejmuje możliwość dodania tekstu do postaci i odpowiedzi jakie ma przed sobą gracz
- podstawowy system eventów, które generują kliknięcia w opcje odpowiedzi na ekranie np. przyjęcie questa czy zamknięcie okna konwersacji
- treść konwersacji kod powinien automagicznie :) przekształcać w klasy
- te powinny zostać przeniesione na ekran w odpowiednim momencie (gracz podchodzi do postaci z którą można rozmawiać i ma możliwość zainicjowania rozmowy)
- gracz klika po opcjach podczas trwania konwersacji
- przechodzi konwersacje i przyjmuje (lub nie) zadanie od NPC, a gra działa dalej
29 października 2024
Co u Sladuma ...
- otwierać skrzynki
- otwierać drzwi
- dla testu dodałem pułapkę podłogową z dwoma stanami (no w sumie mam tylko dwa rysunki animacji) i potrafi zadać obrażenia bohaterowi (ten nie umiera bo tego jeszcze nie obsługuję)
- i chyba najfajniejsze - od nowa zrobiłem okno dialogowe bo poprzednie nie działało dobrze
- dialog wczytywany jest z pliku json
- tekst wyświetla się w okienku
Zrzucik z opisami |
27 października 2024
Czy ten Node2D to na pewno Player.cs?
using Godot;
using System;
public static class VariantTester<T>
{
public static T GetType(Variant unknown)
{
switch (unknown.VariantType)
{
case Variant.Type.Nil:
break;
case Variant.Type.Int:
break;
case Variant.Type.Object:
return CheckIsT(unknown);
}
return default;
}
private static T CheckIsT(Variant unknown)
{
try
{
if (unknown.AsGodotObject() is T matching)
{
return matching;
}
else
{
return (T)Convert.ChangeType(unknown.AsGodotObject(), typeof(T));
}
}
catch (InvalidCastException)
{
return default;
}
}
}
Wywołanie tego kawałka kodu jest proste jak drut - tak jak i późniejsze użycie
// jakas tam klasa gdzie nam wpada Node2D do funkcji
public void OnBodyIn(Node2D node)
{
if (node.IsInGroup("Player")) // ulatwijmy sobie
{
// proba rzutowania // sprawdzenie null-safe w c# i wywolanie metody na znalezionej klasie :)
VariantTester<Player>.GetType(node)?.AddItemToInventory(itemResource);
}
}
Na razie na coś wiele mądrzejszego nie wpadłem (w sumie C# używam jakieś 3 tygodnie także miejcie litość) , a jak macie lepszy pomysł to zapraszam do komentarzy