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

Brak komentarzy:

Prześlij komentarz

Tu możesz wstawić swój komentarz