25 listopada 2024

Źródła wiedzy

Doszedłem do wniosku, iż podzielę się z wami informacją skąd biorę podpowiedzi lub rozwiązania problemów związanych z Godot. Nie jestem zbyt zaawansowany w programowaniu gier stąd nie wszystkie użycia wbudowanych w silnik funkcji są dla mnie jasne. W efekcie szukam informacji wszędzie, ponieważ o ile budowanie klas, struktur czy szablony projektowe nie sprawiają mi problemów tak normalizacja wektorów czy ruch w oparciu o przeliczenie delty już tak.

Jak wspominałem w pierwszym od lat wpisie na blogu bazą do kolejnego podejścia do programowania gier i godota w szczególności stała się:


Czyli platforma e-learning Zeneva Academy, którą znajdziecie tutaj: https://academy.zenva.com/

Kursy kupiłem z ciekawość, w dwóch pakietach z HumbleBundle, wszystkie wymagają znajomości języka angielskiego na zdecydowanie wyższym niż podstawowy poziome. Zapłaciłem za nie coś ok 220 pln za 69(!) kursów podzielonych na naukę Godota, trochę do Unity i chyba jakieś pojedyncze pythona. Tych do Unity na razie nie tknąłem palcem.
Dla osoby stawiającej pierwsze kroki w nauce silnika zawartość kursów będzie przydatna i pomaga oswoić się z podstawowymi pojęciami oraz sposobem poruszania się po gui silnika. Przykłady są stosunkowo proste i czytelne, a sam efekt na zakończenie lekcji zadawalający ze względu na to iż otrzymujemy działający prototyp-grę opartą o język skryptowy Godota (trochę podobny do pythona). Tutaj duży plus.
ALE
patrząc na przedstawione w lekcjach przykłady pod kątem jakości produkowanego w ten sposób kodu to ten oceniam słabo. Osoba, która nie miała wcześniej styczności z programowaniem, bardzo szybko nauczy się grupy złych nawyków poczynając od losowego umieszczania plików w projekcie przez nie dzielenia odpowiedzialności w kodzie na funkcje po wielopiętrowe ify będące zmorą wśród junior developerów. Tutaj Zenwa i jej lektorzy powinni lepiej przygotować pisany kod pod zastosowanie najprostszych zasad clean code,.  Minus! 
Czasami problemem dla słuchającego bywa sposób w jaki opisywane jest podejście do rozwiązań zastosowanych w kodzie: zdawkowe i pobieżne. Warto przedstawić słuchaczowi dlaczego musimy napisać tak, a nie inaczej zamiast po prostu pisać kod.. bo tak działa i już.
Kilkukrotnie odniosłem też wrażenie, że przykłady wymyślane są na żywo w czasie lekcji i pisania kodu. Skutecznie psuje to odbiór lekcji, a sama nauka traci na atrakcyjności.

Jeśli jednak szukacie czegoś na początek, aby poznać podstawy silnika oraz z czym to się je, kursy polecam, ale nie jako samodzielny zakup na Zenvie lecz raczej tak jak mi się udało - jakiś pakiet HumbleBundle czy coś podobnego.

YouTube

Większość z rzeczy pokazywanych na Zenvie jest dostępnych na youtube. Zenva ma tę przewagę iż uczeń dostaje ścieżkę od elementów podstawowych do tych zaawansowany. Na youtube czegoś takiego nie uświadczysz bo i nie jest to stricte platforma szkoleniowa, zaś właściciele kanałów (zwykle) nie zaprzątają sobie głowy przygotowaniem materiałów ze wskazaniem od czego najlepiej zacząć pierwsze kroki, a co zostawić sobie na moment kiedy umiemy chodzić.
Musze przyznać bez bicia, że bez lekcji z Zenvy część nagrań była by dla mnie nieczytelna lub nie zwróciłbym uwagi na elementy ważne dla rozwiązywanych problemów (np. kolejność węzłów w edytorze czy wybrane dla nich konfiguracje parametrów).

Niżej podaję linki do kanałów, które obserwuję:

https://www.youtube.com/@rapidvectors

https://www.youtube.com/@Gwizz1027

https://www.youtube.com/@ShapedByRainStudios

https://www.youtube.com/@RafaFiedo

https://www.youtube.com/@ChrisTutorialsYT

https://www.youtube.com/@dev-worm


A i jeszcze jedna rzecz na koniec jeśli nie zauważyliście po poprzednich wpisach. Swój kod w całości pisze w c# i nie używam języka skryptowego godota. 

20 listopada 2024

Pisałem, groziłem

że przerzucę kod do git-a i w końcu to zrobiłem. Był moment zwątpienia ponieważ menu bitbucket trochę się zmieniło od czasu gdy ostatni raz go używałem. Koniec końców głównym problemem po utworzeniu repozytorium był błąd w plik .gitingore, do którego nie dodałem ignorowania plików projektowych godota oraz projektu VSStudio

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.
Nie chcę tu wklejać .gitignore i po prostu wskażę skąd pobrałem bazę dla zakresu ignorowanych plików. Jak tworzycie projekt skopiujcie po prostu zakres podany w poniższym pliku: 

https://github.com/godotengine/godot/blob/master/.gitignore

Przy okazji musze przyznać, że menu plugina gita do VSStudio jest lekko nieczytelne. Niby trzy zakładki, ale po latach używania Intellij Idea jestem przyzwyczajony do innego formatu pokazywania zmian. 


Kończąc - projekt jest bezpieczny na zewnętrznym zasobie. Kamień milowy osiągnięty! (gdybym jakiekolwiek założył :) ).




p.s.
W międzyczasie musiałem zmienić klawiaturę ponieważ stara (mocno przesadzone stwierdzenie, miała może z rok) uległa skutecznemu uszkodzeniu po zalaniu z użyciem posłodzonej herbaty. Niemal wszystkie klawisze, których używa się pod małym palcem prawej dłoni, uległy zalepieniu.
Odbijający po 2s enter czy shift nie pomagają w pisaniu. 

Trzymajcie napoje z dala od klawiatur!

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ę.

[
    {
        "Id": "CANDLES_RECOVER",
        "Title": "Odzyskaj lichtarze dla tajemniczej nieznajomej",
        "Description": "W swiatyni seta znajduja sie lichtarze,
nizeznajoma chce je odzyskac",
        "Giver": "Tajemnicza nieznajoma z karczmy",
        "GiverId": "MYSTERIOUS_STRANGER",
        "Status": "ACCEPTED",
        "EntryDialog": "res://assets/dialog/levels/taverupperlevel/
PrincessTalkOne.json",
        "RevisitDialog": "res://assets/dialog/levels/taverupperlevel/
PrincessTalkTwo.json",
        "CompletedDialog": "res://assets/dialog/levels/taverupperlevel/
PrincessTalkCompleted.json",
        "Tasks": [
            {
                "TaskCompleted": false,
                "TargetLocation": "SET_TEMPLE",
                "Type": "DISCOVER"
            },
            {
                "TaskCompleted": false,
                "Type": "FETCH",
                "TargetLocation": "SET_TEMPLE",
                "TargetNumber": "1",
                "TargetName": "CANDLE_01"
            }
        ],
        "Reward": {
            "Xp": 100,
            "Gold": 1000
        }
    }
]


Z innych ważnych zmian: 
- mocno skomplikował się plik zarządzania questami, 
- podobnie ilość sygnałów (events) jakie są do tego przypisane
- postać niezależna nie steruje już tak mocno samym oknem dialogowym (chociaż... trochę musi) oraz - - nie jest zależna od pliku z questami (a była co akurat było bez sensu)
- bez problemu mogę też dodawać dialogi do npc-tów, którzy nie przekazują żadnych questów.
- Poprawnie działa (raczej :) ) podsumowanie zadania i późniejsze interakcje z npc gdy nie ma już żadnego zadania dla gracza.


A poniżej kilka zrzutów

Dialog przy pierwszym spotkaniu, po którym możemy otrzymać zadanie.



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. 
Poniżej jest to linia gdzie do węzła o typie AudioStreamPlayer2D podpinam się w sygnał Finished  (czyli zakończenia się grania dźwięku) i uruchamiam funkcję RemoveChildNode()


    private void QuestDone(String questDone)
    {
        AudioStreamPlayer2D questCompletedPlayer = new AudioStreamPlayer2D();
        questCompletedPlayer.Stream = ResourceLoader.Load<AudioStreamWav>("res://assets/audio/music/questcompleted.wav");
        questCompletedPlayer.SetVolumeDb(-30);
        questCompletedPlayer.Autoplay = true;
        questCompletedPlayer.Name = "questcompleted";
        AddChild(questCompletedPlayer);
        questCompletedPlayer.Play();
        backgroundPlayer.Stop();

        questCompletedPlayer.Finished += () => RemoveChildNode();
    }

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

Przykładowa kolizja ze ścianą

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)



Odznaczamy CollisionMask na 5 warstwie dla postaci gracza... 

I gracz traci możliwość wejścia za 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


RayCast2D ze źle zdefiniowanym wykrywaniem kolizji - mimo tego, że w konfiguracji powinien kolidować z tzw. GROUND_AREA (moja nazwa dla warstwy 4 CollisionLayer  na której umieściłem Arae2Dkolizji jednak nie ma.


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 odebraniu zadania od NPC wyświetla się ono jako aktywny w lewym górnym rogu wraz z pod-zadaniami oraz stopniem ich wykonania.
Stan wykonania zadań roboczo opisany jest etykietką z napisem: true/false


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ę?

Tym razem krótki poradnik :)
Zdarza się iż gracz powinien czekać na działania NPC lub po prostu stać w miejscu do momentu np.: zakończenia rozmowy z NPC, odpalenia menu czy zwyczajnej pauzy. 
Stąd pojawia się powód do odebrania możliwość ruchu i zamiast skazywać się na mozolne blokowanie  klawiatury i myszy można to zrobić poprzez wywołanie trzech prostych funkcji wbudowanych w godot dostępnych dla wszystkich obiektów rozszerzających klasę Node

Poniżej przykład jak zablokować gracza

    public void Disable()
    {
        SetPhysicsProcess(false);
        SetProcess(false);
        SetProcessInput(false);
    }

Oczywiście aby go odblokować robimy to samo tylko na odwrót :)

    public void Enable()
    {
        SetPhysicsProcess(true);
        SetProcess(true);
        SetProcessInput(true);
    }

Po wywołaniu metod gra działa samodzielnie - nie wyłącza się muzyka, NPC mogą spokojnie dalej łazić po planszy (o ile to zaprogramowaliśmy), ale za to gracz nie ruszy się z miejsca.

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
A teraz trochę kodu:

Plik jsona z przykładową konwersacją (na razie jest to wstępny format pliku, pewnie dojdzie tu trochę opcji). Plik leży sobie gdzieś w assetach gry. To wstępny format i zakres pól, ale w pełni umożliwiający przeprowadzenie konwersacji

[
    {
        "Id": "A1",
        "DialogDescription": "Tajemnicza nieznajoma",
        "Dialog": "Mikołaju! W tej karczmie wyglądasz mi na jedynego który
jest w stanie podjąć się niezwykle ważnego dla mnie zadania. Nie chcę ,
żebyś narażał się za darmo, jeżeli zgodzisz mi się pomóc otrzymasz
sutą nagrodę. Czy chcesz wysłuchać o co chodzi?",
        "Event": "PROCEED",
        "Targets": [
            {
                "SourceId": "A1",
                "DestinationId": "A2",
                "Dialog": "Oczywiście masz rację",
                "Event": "PROCEED"
            },
            {
                "Dialog": "Nie chce mi się z tobą gadać",
                "Event": "END_DIALOG"
            }
        ]
    }, ...... i tam dalej są kolejne opcje konwersacji

Tutaj przykład do jakiej klasy przekształcany jest powyższy json:

namespace Dialog
{

    using Godot;
    using System;

    public partial class Conversation : Node
    {
        public string Id { get; set; }
        public string DialogDescription { get; set; }
        public string Dialog { get; set; }
        public string Event { get; set; }
        public ConversationChoice[] Targets { get; set; }
    }
    public enum DialogEvent
    {
        PROCEED, END_DIALOG, QUEST_ADD, QUEST_ACCEPTED
    }
}


i pochodna ConversationChoice

namespace Dialog
{

    using Godot;
    using System;

    public partial class ConversationChoice : Node
    {

        public string SourceId { get; set; }
        public string DestinationId { get; set; }
        public string Dialog { get; set; }
        public string Event { get; set; }
        public string QuestId { get; set; }

    }
}


Tutaj elegancki (zauważ generyczny) konwerter jsonów

public class JsonReader<T>
    { 
        public T ReadJsonFile(string fileName)
        {
            using FileAccess acces = FileAccess.Open(fileName,
                                 FileAccess.ModeFlags.Read);
            var text = acces.GetAsText();
            T readedJson = JsonSerializer.Deserialize<T>(text);
            return readedJson;
        }
    }

Zainicjowanie konwertera dzejsońów jako dialogReader  i wywołanie w kodzie (tu akurat inicjowanie dialogu w ramach klasy opisującej NPC). Trochę drewniane jest zaszywanie ścieżek do plików w głównym pliku npc-ta, ale to się poprawi :)


    private String DialogFilePathOne =
        "res://assets/dialog/levels/taverupperlevel/PrincessTalkOne.json";
    private JsonReader<List<Dialog.Conversation>> dialogReader = new();
   
    public void InitDialog()
    {
        convesations = dialogReader.ReadJsonFile(DialogFilePathOne);
    
.... reszta kodu dalej....


i efekt wizualny na ekranie po dodaniu tekstu do okienek dialogowych (nie umieszczonych :D ) 




A tu pod okienkiem gry,  w konsoli VS studio widać, że postać przyjęła zadanie zakceptowana misja QUESTS ID: CANDLES_RECOVER  Nad obsługą questów dopiero pracuję...




p.s.
Jakby ktoś chciał jak wygląda samo okno dialogowe i co tam się dzieje ... to niech da znać.