System dialogów i odpowiedzi

System dialogów i odpowiedzi
tBane
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Poznań
  • Postów: 538
0

Witam.
Piszę system dialogów. Program ma działać tak, że wyświetla daną opcję dialogową i odpowiedzi do nie, następnie można wybrać jedną z odpowiedzi i wtedy wyświetla się następna opcja dialogowa z odpowiedziamij. Niestety mój program wypisuje tylko 3 z 4 odpowiedzi i nie wiem dlaczego. Dodam, że to pierwszy mój program gdzie operuję na stosie (co nie jest takie łatwe :-/). Pomożecie mi ?

Co wyświetla:

Kopiuj
"Witaj! :-)" (option)
-Kim jestes? (answer)
-Co tutaj robisz? (answer)
-Czy jest cos co moglbym dla ciebie zrobic? (answer)

Co powinno wyświetlać:

Kopiuj
"Witaj! :-)" (option)
-Kim jestes? (answer)
-Co tutaj robisz? (answer)
-Czy jest cos co moglbym dla ciebie zrobic? (answer)
-Zegnaj. (answer)

Dialog

Kopiuj
"Witaj! :-)" (option)
-Kim jestes? (answer)
	"Jestem rybakiem i nazywam sie John." (option)
-Co tutaj robisz? (answer)
	"Lowie ryby na tej plazy." (option)
	-"I jak biora?" (answer)
		"Biora, biora. A co maja nie brac :P" (option)
	-"W porzadku." (answer)
-Czy jest cos co moglbym dla ciebie zrobic? (answer)
	"Tak. Jest w sumie jedna rzecz. Potrzebowalbym zanety na ryby, bo juz mi się konczy." (option)
		-"Przyniose Ci zanete." (answer)
		-"Sam sobie idz po te zanete! :D" (answer)
-Zegnaj. (answer)

Algorytm

Kopiuj
#include <iostream>
#include <vector>
#include <string>
#include <fstream>
#include <windows.h>
using namespace std;

std::wstring ConvertUtf8ToWide(const std::string& utf8Str) {
    // TO-DO
    short wideCharCount = MultiByteToWideChar(CP_UTF8, 0, utf8Str.c_str(), utf8Str.size(), nullptr, 0);
    if (wideCharCount == 0) {
        throw std::runtime_error("Error in MultiByteToWideChar");
    }

    std::wstring wideStr(wideCharCount, 0);
    MultiByteToWideChar(CP_UTF8, 0, utf8Str.c_str(), utf8Str.size(), &wideStr[0], wideCharCount);
    return wideStr;
}

short count_tabs(const std::string& line) {
    short count = 0;
    for (char c : line) {
        if (c == '\t') count++;
        else break;
    }
    return count;
}

short count_tabs(const std::wstring& line) {
    short count = 0;
    for (char c : line) {
        if (c == L'\t') count++;
        else break;
    }
    return count;
}

void test_dialogues_system() {

    class DialogueOption {
    public:

        class DialogueAnswer {
        public:
            std::wstring text;
            DialogueOption* next_option;

            DialogueAnswer(std::wstring text) {
                this->text = text;
                next_option = nullptr;
            }
        };

        std::wstring text;
        std::vector < DialogueAnswer* > answers;

        DialogueOption(std::wstring text) {
            this->text = text;
            answers.clear();
        }
    };

    class Dialogue {
    public:
        short id;
        std::vector < DialogueOption* > options;

        Dialogue(short id) {
            this->id = id;
            load();
        }

        ~Dialogue() { }

        void load() {

            std::vector < DialogueOption* > stack;
            std::string line;

            std::ifstream file("dialogues\\new\\000.txt");

            while (std::getline(file, line)) {

                if (line.empty())
                    continue;

                if (stack.empty()) {
                    stack.push_back(new DialogueOption(ConvertUtf8ToWide(line)));
                    options.push_back(stack.back());
                }
                else {
                    short tabs = count_tabs(line);
                    short stack_tabs = count_tabs(stack.back()->text);

                    std::wcout << L"t: " << tabs << " st: " << stack_tabs << L"\n";

                    if (tabs == stack_tabs) {
                        if (line[tabs] == '-') {
                            // is Dialogue Answer
                            DialogueOption::DialogueAnswer* answer = new DialogueOption::DialogueAnswer(ConvertUtf8ToWide(line));
                            answer->next_option = stack.back();
                            stack.back()->answers.push_back(answer);
                        }
                    }
                    else if (tabs > stack_tabs) {
                        // is new Dialogue Option
                        DialogueOption* option = new DialogueOption(ConvertUtf8ToWide(line));
                        stack.push_back(option);
                    }
                    else if (tabs < stack_tabs) {
                        // undo
                        options.push_back(stack.back());
                        stack.pop_back();

                        // dopisane jak radził pekfos
                        DialogueOption::DialogueAnswer* answer = new DialogueOption::DialogueAnswer(ConvertUtf8ToWide(line));
                        answer->next_option = stack.back();
                        stack.back()->answers.push_back(answer);


                    }
                }


            }
            file.close();

            options.push_back(stack.back());

        }
    };


    Dialogue* dialogue = new Dialogue(0);
    DialogueOption* current_option = dialogue->options.front();

    while (true) {
        std::wcout << current_option->text << L"\n";
        for (auto& answer : current_option->answers) {
            std::wcout << answer->text << "\n";
        }

        std::wcout << L"\n";

        short choice = 0;
        std::cin >> choice;

        if (choice >= 0 && choice < current_option->answers.size()) {
            current_option = current_option->answers[choice]->next_option;
        }
    };


}

int main() {

    test_dialogues_system();
    return 0;
}
tBane
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Poznań
  • Postów: 538
0

Poprawiłem nieco kod i teraz dialogi działają lepiej, tylko odpowiedzi źle wczytuje tzn. nie wczytuje ostatniej odpowiedzi "-Zegnaj. (answer)"

Kopiuj
"Witaj! :-)" (option)
-Kim jestes? (answer)
-Co tutaj robisz? (answer)
-Czy jest cos co moglbym dla ciebie zrobic? (answer)

0
        "Jestem rybakiem i nazywam sie John." (option)
"Witaj! :-)" (option)
-Kim jestes? (answer)
-Co tutaj robisz? (answer)
-Czy jest cos co moglbym dla ciebie zrobic? (answer)

1
        "Lowie ryby na tej plazy." (option)
        -"I jak biora?" (answer)
        -"W porzadku." (answer)

0
                "Biora, biora. A co maja nie brac :P" (option)
"Witaj! :-)" (option)
-Kim jestes? (answer)
-Co tutaj robisz? (answer)
-Czy jest cos co moglbym dla ciebie zrobic? (answer)
Kopiuj

#include <iostream>
#include <vector>
#include <string>
#include <fstream>
#include <windows.h>
using namespace std;

std::wstring ConvertUtf8ToWide(const std::string& utf8Str) {
    // TO-DO
    short wideCharCount = MultiByteToWideChar(CP_UTF8, 0, utf8Str.c_str(), utf8Str.size(), nullptr, 0);
    if (wideCharCount == 0) {
        throw std::runtime_error("Error in MultiByteToWideChar");
    }

    std::wstring wideStr(wideCharCount, 0);
    MultiByteToWideChar(CP_UTF8, 0, utf8Str.c_str(), utf8Str.size(), &wideStr[0], wideCharCount);
    return wideStr;
}

short count_tabs(const std::string& line) {
    short count = 0;
    for (char c : line) {
        if (c == '\t') count++;
        else break;
    }
    return count;
}

short count_tabs(const std::wstring& line) {
    short count = 0;
    for (char c : line) {
        if (c == L'\t') count++;
        else break;
    }
    return count;
}

void test_dialogues_system() {

    class DialogueOption {
    public:

        class DialogueAnswer {
        public:
            std::wstring text;
            DialogueOption* next_option;

            DialogueAnswer(std::wstring text) {
                this->text = text;
                next_option = nullptr;
            }
        };

        std::wstring text;
        std::vector < DialogueAnswer* > answers;

        DialogueOption(std::wstring text) {
            this->text = text;
            answers.clear();
        }
    };

    class Dialogue {
    public:
        short id;
        std::vector < DialogueOption* > options;

        Dialogue(short id) {
            this->id = id;
            load();
        }

        ~Dialogue() { }

        void load() {

            std::vector < DialogueOption* > stack;
            std::string line;

            std::ifstream file("dialogues\\new\\000.txt");

            while (std::getline(file, line)) {

                if (line.empty())
                    continue;

                if (stack.empty()) {
                    stack.push_back(new DialogueOption(ConvertUtf8ToWide(line)));
                    options.push_back(stack.back());
                }
                else {
                    short tabs = count_tabs(line);
                    short stack_tabs = count_tabs(stack.back()->text);

                    std::wcout << L"t: " << tabs << " st: " << stack_tabs << L"\n";

                    if (tabs == stack_tabs) {
                        if (line[tabs] == '-') {
                            // is Dialogue Answer
                            DialogueOption::DialogueAnswer* answer = new DialogueOption::DialogueAnswer(ConvertUtf8ToWide(line));
                            answer->next_option = stack.front();
                            stack.back()->answers.push_back(answer);
                        }
                        else {
                            DialogueOption* option = new DialogueOption(ConvertUtf8ToWide(line));
                            if (!stack.back()->answers.empty())
                                stack.back()->answers.back()->next_option = option;

                            stack.push_back(option);
                        }
                    }
                    else if (tabs > stack_tabs) {
                        // is new Dialogue Option
                        DialogueOption* option = new DialogueOption(ConvertUtf8ToWide(line));
                        if(!stack.back()->answers.empty())
                            stack.back()->answers.back()->next_option = option;

                        stack.push_back(option);
                    }
                    else if (tabs < stack_tabs) {
                        // undo
                        options.push_back(stack.back());
                        stack.pop_back();

                        // dopisane jak radził pekfos
                        DialogueOption::DialogueAnswer* answer = new DialogueOption::DialogueAnswer(ConvertUtf8ToWide(line));
                        answer->next_option = stack.back();
                        stack.back()->answers.push_back(answer);


                    }
                }


            }
            file.close();

            options.push_back(stack.back());

        }
    };


    Dialogue* dialogue = new Dialogue(0);
    DialogueOption* current_option = dialogue->options.front();

    while (true) {
        std::wcout << current_option->text << L"\n";
        if (current_option->answers.empty()) {
            current_option = dialogue->options.front();
            continue;
        }

        for (auto& answer : current_option->answers) {
            std::wcout << answer->text << "\n";
        }

        std::wcout << L"\n";

        short choice = 0;
        std::cin >> choice;

        if (choice >= 0 && choice < current_option->answers.size()) {
            current_option = current_option->answers[choice]->next_option;
        }
    };

}

int main() {
test_dialogues_system();
return 0;
}
tBane
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Poznań
  • Postów: 538
0

Dobra. Napisałem i działa. :-)

Kopiuj
"Witaj! :-)" (option)
-Kim jestes? (answer)
-Co tutaj robisz? (answer)
-Czy jest cos co moglbym dla ciebie zrobic? (answer)
-Zegnaj. (answer)

1
"Lowie ryby na tej plazy." (option)
-I jak biora? (answer)
-W porzadku. (answer)

1
"Witaj! :-)" (option)
-Kim jestes? (answer)
-Co tutaj robisz? (answer)
-Czy jest cos co moglbym dla ciebie zrobic? (answer)
-Zegnaj. (answer)

2
"Tak. Jest w sumie jedna rzecz. Potrzebowalbym zanety na ryby, bo juz mi sie konczy." (option)
-Przyniose Ci zanete. (answer)
-Sam sobie idz po te zanete! :D (answer)

0
"Witaj! :-)" (option)
-Kim jestes? (answer)
-Co tutaj robisz? (answer)
-Czy jest cos co moglbym dla ciebie zrobic? (answer)
-Zegnaj. (answer)

1
"Lowie ryby na tej plazy." (option)
-I jak biora? (answer)
-W porzadku. (answer)

0
"Biora, biora. A co maja nie brac :P" (option)
"Witaj! :-)" (option)
-Kim jestes? (answer)
-Co tutaj robisz? (answer)
-Czy jest cos co moglbym dla ciebie zrobic? (answer)
-Zegnaj. (answer)
Kopiuj
#include <iostream>
#include <vector>
#include <string>
#include <fstream>
#include <windows.h>
using namespace std;

std::wstring ConvertUtf8ToWide( const std::string & utf8Str ) {
    // TO-DO
    short wideCharCount = MultiByteToWideChar( CP_UTF8, 0, utf8Str.c_str(), utf8Str.size(), nullptr, 0 );
    if( wideCharCount == 0 ) {
        throw std::runtime_error( "Error in MultiByteToWideChar" );
    }
   
    std::wstring wideStr( wideCharCount, 0 );
    MultiByteToWideChar( CP_UTF8, 0, utf8Str.c_str(), utf8Str.size(), & wideStr[ 0 ], wideCharCount );
    return wideStr;
}

short count_tabs( const std::string & line ) {
    short count = 0;
    for( char c: line ) {
        if( c == '\t' ) count++;
        else break;
       
    }
    return count;
}

short count_tabs( const std::wstring & line ) {
    short count = 0;
    for( char c: line ) {
        if( c == L'\t' ) count++;
        else break;
       
    }
    return count;
}

void test_dialogues_system() {

    class DialogueOption {
    public:

        class DialogueAnswer {
        public:
            std::wstring text;
            DialogueOption* next_option;

            DialogueAnswer(std::wstring text) {
                this->text = text;
                next_option = nullptr;
            }
        };

        std::wstring text;
        std::vector < DialogueAnswer* > answers;

        DialogueOption(std::wstring text) {
            this->text = text;
            answers.clear();
        }
    };

    class Dialogue {
    public:
        short id;
        std::vector < DialogueOption* > options;

        Dialogue(short id) {
            this->id = id;
            load();
        }

        ~Dialogue() { }

        void load() {

            std::vector < DialogueOption* > stack;
            std::wstring line;

            std::wifstream file("dialogues\\new\\001.txt");

            while (std::getline(file, line)) {

                if (line.empty())
                    continue;

                if (stack.empty()) {
                    stack.push_back(new DialogueOption(line));
                }
                else {
                    short tabs = count_tabs(line);
                    short stack_tabs = count_tabs(stack.back()->text);

                    std::wcout << L"t: " << tabs << " st: " << stack_tabs << L"\n";

                    if (tabs == stack_tabs) {
                        if (line[tabs] == '-') {
                            // is Dialogue Answer
                            DialogueOption::DialogueAnswer* answer = new DialogueOption::DialogueAnswer(line);
                            answer->next_option = stack.front();
                            stack.back()->answers.push_back(answer);
                        }
                        else {
                            DialogueOption* option = new DialogueOption(line);
                            if (!stack.back()->answers.empty())
                                stack.back()->answers.back()->next_option = option;

                            stack.push_back(option);
                        }
                    }
                    else if (tabs > stack_tabs) {
                        // is new Dialogue Option
                        DialogueOption* option = new DialogueOption(line);
                        if(!stack.back()->answers.empty())
                            stack.back()->answers.back()->next_option = option;

                        stack.push_back(option);
                    }
                    else if (tabs < stack_tabs) {
                        // undo
                        for(short i = tabs; i< stack_tabs; i++)
                            stack.pop_back();

                        // dopisane jak radził pekfos
                        DialogueOption::DialogueAnswer* answer = new DialogueOption::DialogueAnswer(line);
                        answer->next_option = stack.front();
                        stack.back()->answers.push_back(answer);
                    }
                }


            }
            file.close();

            options.push_back(stack.back());

        }
    };


    Dialogue* dialogue = new Dialogue(0);
    DialogueOption* current_option = dialogue->options.front();

    std::cout << "options: " << dialogue->options.size() << "\n";

    while (true) {

        std::wstring text = current_option->text;
        text.erase(0, text.find_first_not_of(L'\t'));

        std::wcout << text.c_str() << L"\n";
        if (current_option->answers.empty()) {
            current_option = dialogue->options.front();
            continue;
        }

        for (auto& answer : current_option->answers) {
            std::wstring text = answer->text;
            text.erase(0, text.find_first_not_of(L'\t'));
            std::wcout << text << "\n";
        }

        std::wcout << L"\n";
        std::wcout << "your answer: ";

        short choice = 0;
        std::cin >> choice;

        if (choice >= 0 && choice < current_option->answers.size()) {
            current_option = current_option->answers[choice]->next_option;
        }
    };

}

int main() {
   
    test_dialogues_system();
    return 0;
}
Spine
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 6965
4

Lepiej by było te dialogi trzymać w jakimś JSONie itp., a nie tak luzem.
No i może by tak zrobić/zastosować jakiś edytor drzewek dialogowych?
https://pl.wikipedia.org/wiki/Drzewko_dialogowe

tBane
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Poznań
  • Postów: 538
1

json jest niewygodny moim zdaniem. Planuję zrobić edytor tekstowy dialogów w Edytorze RPG2D. Docelowo pliki mają być zapisywane binarne w formacie *.dlg

Spine
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 6965
0
tBane napisał(a):

json jest niewygodny moim zdaniem.

Niewygodny w implementacji, czy niewygodny dla człowieka, który chce edytować plik JSON ręcznie?

obscurity
  • Rejestracja: dni
  • Ostatnio: dni
0
tBane napisał(a):

json jest niewygodny moim zdaniem

to może yaml, to teoretycznie bardziej ludzki json choć ja osobiścię yamla nie znoszę a jsona kocham
ewentualnie TreeStructInfo... 😉

no i to nie takie proste bo takie jałowe dialogi które nic nie zmieniają są bezsensowne, zapewne będziesz chciał zmieniać nieco wynik gry i odpalać jakieś akcje przy przejściu przez konkretne ścieżki dialogowe i aktywować niektóre opcje dialogowe zależnie od zawartości ekwipunku

tBane
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Poznań
  • Postów: 538
0

Już kiedyś spotkałem się z json'em, ale w moim przypadku to przerost formy nad treścią. Przez nawiasy klamrowe dialogi były by mniej czytelne. A zmienne są niepotrzebne i tak musiałbym je jakoś parsować.

Przykładowy kod json (źródło: https://github.com/daltonbr/OurHeroesJourney)

Kopiuj
{
  "dialogues":
  [
        {
          "characterType": 0,
          "name": "Hero",
          "atlasImageName": "Hero_Default",
          "dialogueText": "Time to address the Call to Adventure."
        },
        {
          "characterType": 1,
          "name": "Ally",
          "atlasImageName": "Heroine_Surprised",
          "dialogueText": "Are you sure? I might need some coffee first."
        }
  ]
}

Kod jednego z dialogów - rozmowa z Zielarką

Kopiuj
"W czym mogę Ci pomóc?"
-"Kim jesteś?"
	"Jestem Grita, miejscowa zielarka."
	-"Czym się zajmujesz?"
		"Zajmuję się wytwarzaniem past, mikstur i proszków. Jeśli czegoś Ci potrzeba to mów."
		-"Zapamiętam."
	-"Dobrze wiedzieć."
-"Co robisz?"
	"Wytwarzam niezwykle trudną miksturę."
	-"Jaką miksturę wytwarzasz?"
		"Magiczną miksturę. Miksturę leczniczą. Po jej wypiciu natychmiastowo organizm rozpoczyna regenerację. Niestety obecnie nie udało mi się jeszcze uważyć tej mikstury, a zioła mi się skończyły. Może przydaj się na coś i przynieś mi je."
		-"W porządku mogę pomóc. Powiedz tylko jakie zioła potrzebujesz."
			"Potrzebuję pięć ziół leczniczych."
			-"Postaram się je przynieść jak najszybciej."
		-"Nie mam czasu, by uganiać się za jakimiś ziołami."
	-"W takim razie nie przeszkadzam."
		"I tak już przeszkodziłeś."
-"Pokaż mi co tam masz."
-"Żegnaj."

obscurity
  • Rejestracja: dni
  • Ostatnio: dni
1

no to yaml się wydaje idealny, może to wyglądać np tak

Kopiuj
dialog:
  question: "W czym mogę Ci pomóc?"
  answers:
    - answer: "Kim jesteś?"
      response: "Jestem Grita, miejscowa zielarka."
      answers:
        - answer: "Czym się zajmujesz?"
          response: "Zajmuję się wytwarzaniem past, mikstur i proszków. Jeśli czegoś Ci potrzeba to mów."
          answers:
            - answer: "Zapamiętam."
        - answer: "Dobrze wiedzieć."
    - answer: "Co robisz?"
      response: "Wytwarzam niezwykle trudną miksturę."
      answers:
        - answer: "Jaką miksturę wytwarzasz?"
          response: "Magiczną miksturę. Miksturę leczniczą. Po jej wypiciu natychmiastowo organizm rozpoczyna regenerację. Niestety obecnie nie udało mi się jeszcze uważyć tej mikstury, a zioła mi się skończyły. Może przydaj się na coś i przynieś mi je."
          answers:
            - answer: "W porządku mogę pomóc. Powiedz tylko jakie zioła potrzebujesz."
              response: "Potrzebuję pięć ziół leczniczych."
              answers:
                - answer: "Postaram się je przynieść jak najszybciej."
            - answer: "Nie mam czasu, by uganiać się za jakimiś ziołami."
        - response: "W takim razie nie przeszkadzam."
          answers:
            - response: "I tak już przeszkodziłeś."
    - answer: "Pokaż mi co tam masz."
    - answer: "Żegnaj."

(wygenerowane przez chatgpt)
i nie musisz tego parsować ręcznie tylko normalnie libką do yamla od razu uzyskujesz gotowy obiekt, bardzo łatwo do tego dołożyć zależności i akcje, emocje na twarzy rozmówcy, albo choćby kolory czy prawdopodobieństwo odpowiedzi w zależności od np poziomu skilla negocjacji gracza. Można też zrobić referencje do innej ścieżki dialogowej, gdy różne odpowiedzi kierują do tego samego miejsca.

tBane
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Poznań
  • Postów: 538
0

@obscurity dzięki. Może w przyszłości mi się przyda :-)

Boski
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 142
2

Nie ma znaczenia jaki format pliku, jeśli używa się wygodnego edytora do drzewek/dialogów. Edytowanie takich rzeczy w notatniku to strzał w kolano, zwłaszcza jeśli są rozbudowane.

flowCRANE
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Tuchów
  • Postów: 12269
1

Format wybrany przez @tBane jest na tyle prosty i przejrzysty, że w praktyce tworzenie i modyfikowanie tych dialogów jest trywialne, a w dodatku format ten nie wymaga skomplikowanego parsowania (i żadnych dodatkowych bibliotek). Geniusz tkwi w prostocie.

Boski
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 142
0

Czyli wygodniej patrzy się na taki flat tekst niż na takie drzewo? Obrazek poglądowy. OP decyduje tak a nie inaczej, okej, ale nie wydaje mi się, żeby takie drzewko dialogowe, otwarte w notatniku, dało się wygodnie edytować. Zmienisz tekst, jasne, ale nie widzisz powiązań, musisz sobie w głowie wyobrazić lub na kartce rozpisać, co do czego prowadzi screenshot-20250430105012.png

flowCRANE
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Tuchów
  • Postów: 12269
1
Boski napisał(a):

Czyli wygodniej patrzy się na taki flat tekst niż na takie drzewo? Obrazek poglądowy.

To zależy do czego jesteśmy przyzwyczajeni, co nam się łatwiej analizuje i edytuje. Szczerze pisząc, drzewko które pokazałeś jest trudniejsze do zrozumienia niż prosty tekst na kilkanaście linijek, sformatowany wcięciami. Drzewko im większe, tym trudniej je ogarnąć, a z tekstem z wcięciami — jako programiści — mamy przecież znacznie więcej do czynienia, to dla nas chleb powszedni. 😉

Przyjdzie czas, to i w swoim silniku będę robił system dialogów. Tyle że u mnie dialog to nie będzie tylko lista tekstów do wyświetlenia w dymkach — będzie również wymagał odpalania konkretnych animacji konkretnych postaci, w konkretnym momencie, tak aby postacie (oprócz gadania) mogły robić absolutnie wszystko (chodzić, coś podnosić/rzucać, gestykulować itd.). Do tego na pewno stworzę osobne narzędzie, tak abym mógł ustawić wszystko co potrzeba dla każdej klatki z osobna (coś jak narzędzia do TAS-ów), być może dla każdego języka gry z osobna.

OP decyduje tak a nie inaczej, okej, ale nie wydaje mi się, żeby takie drzewko dialogowe, otwarte w notatniku, dało się wygodnie edytować.

A kod źródłowy, jak sobie otworzysz w notatniku, to dasz radę go edytować, czy potrzebujesz IDE i jego wizualizacji w formie diagramów? IMO to trochę szukanie problemu na siłę.

Zmienisz tekst, jasne, ale nie widzisz powiązań, musisz sobie w głowie wyobrazić lub na kartce rozpisać, co do czego prowadzi

Zdaje się, że w tym systemie dialogów nie ma powiązań/zapętleń — wygląda jak drzewko katalogów/plików, więc łatwo się je analizuje, patrząc na wcięcia (wszystko jest jak na dłoni). Nie wnikałem głębiej w to jak ten system działa, ale wydaje się być bardzo prostym i najwyraźniej stąd brak potrzeby używania wyrafinowanych rozwiązań.

Boski
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 142
2
flowCRANE napisał(a):

Szczerze pisząc, drzewko które pokazałeś jest trudniejsze do zrozumienia niż prosty tekst na kilkanaście linijek

Jeśli porównujesz do tych fragmentów wyżej, które zawierają może z 3-4 node'y, to troche nie fair. Natomiast jak zapiszesz te same drzewo w formie tekstowej, to powodzenia, że wersja graficzna jest trudniejsza do zrozumienia.

A kod źródłowy, jak sobie otworzysz w notatniku, to dasz radę go edytować, czy potrzebujesz IDE i jego wizualizacji w formie diagramów? IMO to trochę szukanie problemu na siłę.

Na siłę wygląda Twój argument IMO. Po to jest IDE, żeby łatwiej było pracować. Pewnie, że da radę w notatniku - ale robisz tak? Znasz projekt tworzony w ten sposób? Po co? Żeby udowodnić, że się da? Bo na pewno nie żeby sobie przyspieszyć i tak ciężki i czasochłonny proces.

Zdaje się, że w tym systemie dialogów nie ma powiązań/zapętleń

Nie wyobrażam sobie sensownych dialogów w grze w ten sposób.

  • Cześć jestem kowalem w czym mogę Ci pomóc
    a) jakieś plotki w mieście?
    b) chcę kupić miecz
    c) szukam pracy
  • Jakieś plotki w mieście?
  • Nie, nie ma plotek
    [KONIEC] - co, żeby kupić miecz muszę jeszcze raz klikać w postać? Nie może mnie przenieść do początku dialogu? Już abstrahując od warunków i przechodzenia do innych node'ów jeśli gracz posiada jakiś przedmiot czy wypełnił quest
tBane
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Poznań
  • Postów: 538
0

@Boski: jest i na to sposób

Kopiuj
DialogueOption::DialogueAnswer* answer = new DialogueOption::DialogueAnswer(line);
answer->next_option = stack.front();
stack.back()->answers.push_back(answer);
Kopiuj
if (current_option->answers.empty()) {
        current_option = dialogue->options.front();
        continue;
}
Spine
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 6965
1

XML, JSON itp. pomagają zachować integralność wprowadzanych danych.
Graficzny edytor drzewka dodatkowo dba o to, żeby dane były wprowadzane w prawidłowy sposób. Wizualizuje i ustawia relacje między wpisami.

@tBane też mógłby sobie edytować swoją mapę w notatniku albo arkuszu kalkulacyjnym, ale wolał zrobić edytor graficzny.

flowCRANE
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Tuchów
  • Postów: 12269
0
Boski napisał(a):

Jeśli porównujesz do tych fragmentów wyżej, które zawierają może z 3-4 node'y, to troche nie fair.

Ten dialog „na 3-4 node'y” podał sam twórca tej gry — nie wyjąłem go z kapelusza.

Natomiast jak zapiszesz te same drzewo w formie tekstowej, to powodzenia, że wersja graficzna jest trudniejsza do zrozumienia.

”to samo drzwo”, bo chodzi o liczbę pojedynczą. A wracając do tematu — diagram, który pokazałeś, nie reprezentuje dialogu z projektu OP, ani nie realizuje założeń projektu OP. Ot wybrałeś sobie diagram losowego JSON-a z ładnymi strzałeczkami, kompletnie niezwiązanego z omawianym projektem i jego wymaganiami, i próbujesz nim argumentować. Trochę to niepoważne.

Na siłę wygląda Twój argument IMO.

Ależ nie — to prosta analogia do Twojej. Jeśli proste narzędzie spełnia oczekiwania, to nie ma żadnego powodu, aby go zmieniać. Pixel art też robi się w różnych narzędziach — jedni robią w Photoshopie, inni w Aseprite, a jeszcze inni w systemowym Paincie.

Po to jest IDE, żeby łatwiej było pracować. Pewnie, że da radę w notatniku - ale robisz tak?

Ja tak nie robię (jak wiesz, Lazarusa używam), ale znam wielu, którzy tak robią — używają prymitywnych edytorów zamiast IDE. Ba, nawet sam Jonathan Blow nie korzysta z IDE. Dla mnie IDE jest wygodnym narzędziem i np. wciśnięcie F9 żeby skompilować i odpalić projekt jest wydajnym rozwiązaniem. Ale dla innych wygodnym środowiskiem pracy jest terminal z nakładką przypominającą IDE, w której trzeba się przełączać pomiędzy widokami i do wszystkiego trzeba pisać komendy, w tym do kompilacji i osobno do uruchomienia projektu.

Jeśli ktoś woli terminalowy edytor kodu i pisanie (nierzadko złożonych) komend to nic mi do tego — jego projekt, jego styl pracy, nie wtrącam się. @tBane nie przeleżał ostatnich 500 lat pod lodem — gdyby chciał skorzystać z któregokolwiek znanego formatu, to by z niego skorzystał. Ale z jakiegoś powodu go nie wybrał i obojętne mi to z jakiego.

Nie wyobrażam sobie sensownych dialogów w grze w ten sposób.
[…]

Ja też nie, i @tBane jak widać też nie — dlatego wybrał formę drzewiastą, a nie płaską, którą tutaj pokazujesz.

Pierwszy raz namieszałeś z diagramem realizującym odmienne założenia, a teraz wypaczyłeś strukturę danych dialogu, podając wersję liniową (płaską), zamiast drzewiastej. czyli takiej jaką używa OP. Już zrobiłeś swoje — przedstawiłeś swoje zdanie, podałeś argumenty, pozwól OP zdecydować co robić, zamiast wpychać mu do gardła swoje racje, w dodatku za pośrednictwem manipulacji.

Mi to obojętne czego użyje. A że jest to projekt głównie hobbystyczny, to może te dialogi robić nawet w Paincie i testować WinAmpem — niech się bawi, eksperymentuje, w końcu to jego projekt. Głupi nie jest, będzie chciał znany format to zawsze może sobie zapytać Google albo Geppetta. A skoro tego nie zrobił, to widać ma wyraźny powód.

Spine
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 6965
1
flowCRANE napisał(a):

Już zrobiłeś swoje — przedstawiłeś swoje zdanie, podałeś argumenty, pozwól OP zdecydować co robić, zamiast wpychać mu do gardła swoje racje, w dodatku za pośrednictwem manipulacji.

Mi to obojętne czego użyje. A że jest to projekt głównie hobbystyczny, to może te dialogi robić nawet w Paincie i testować WinAmpem — niech się bawi, eksperymentuje, w końcu to jego projekt. Głupi nie jest, będzie chciał znany format to zawsze może sobie zapytać Google albo Geppetta.

Ty bronisz własnego "nietypowego" podejścia do tworzenia gier, pochwalając poczynania @tBane.
W wielu kwestiach da się odczuć jaki ten projekt jest rozklekotany i często widać, że @tBane potrzebuje, żeby go przekonać do implementacji tego i owego.
Ciebie nie da się przekonać, ale dla @tBane jeszcze jest nadzieja, bo czasem słucha...

Boski
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 142
1

Nie będę się odnosić do całego posta, bo to jakaś kpina.
Ale odnośnie manipulacji i wpychaniu racji:
"OP decyduje tak a nie inaczej, okej, ale nie wydaje mi się, żeby takie drzewko dialogowe, otwarte w notatniku, dało się wygodnie edytować."
Tak napisałem wcześniej i tak dalej uważam. Wybrał jak wybrał, szanuję. Natomiast uznałem, że odpiszę na głupoty, które piszesz, argumentami.
Nic nie dociera, masz swoją rację i jesteś najmądrzejszy, ok, nic na siłę. Miłego dzionka

WeiXiao
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 5226
1

@flowCRANE

To zależy do czego jesteśmy przyzwyczajeni, co nam się łatwiej analizuje i edytuje. Szczerze pisząc, drzewko które pokazałeś jest trudniejsze do zrozumienia niż prosty tekst na kilkanaście linijek, sformatowany wcięciami. Drzewko im większe, tym trudniej je ogarnąć, a z tekstem z wcięciami — jako programiści — mamy przecież znacznie więcej do czynienia, to dla nas chleb powszedni. 😉

Ale docelowo to nie programiści pisza dialogi 😀

Anyway, wcięcia są mniej czytelne niż sekcje {} jeżeli chodzi o scoping

Kopiuj
{
  ...
}

Oraz o wiele lepiej jest używać standaryzowanych formatów typu json/xml, bo wtedy łatwiej robić jakiś tooling do tego.

Ktoś będzie chciał zrobić jakąś automatyzację, zrobić jakiś większy text-processing w pajtonie, javie, etc, etc. to po prostu wczyta sobie jsona i tyle, nie bedzie musial reimplementować parsera.

Są przypadki gdzie warto jest pisać swoj język i parser np. tworząc jezyk programowania, ale przechowywanie dialogów nie jest takim przypadkiem imo.

Ale z jakiegoś powodu go nie wybrał i obojętne mi to z jakiego.

A może nie ma dobrego powodu i po prostu zaklepał kod.
No i jak ten wątek udowadnia - były pewne komplikacje których by uniknął biorąc jsona + libke.

Jest w tym oczywiście wartość edukacyjna (pisanie parserów) oraz wrzucając to na forum pewnie liczył że dostanie jakiś feedback, no to dajemy mu negatywny feedback

PS:

Czy ten parser OPa w ogóle obsługuje tekst z newlines/tabami?

overcq
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 402
1

A nie prościej utworzyć i edytować dialogi w jakimś znanym formacie, a następnie przekonwertować do postaci binarnej do użycia w grze?

flowCRANE
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Tuchów
  • Postów: 12269
1

@WeiXiao: zgoda, tylko że pomijasz jeden, zasadniczy fakt — OP sam pracuje nad tą grą, w dodatku w ramach hobby, więc on sam te dialogi pisze i głównie dlatego pojawił się tutaj własny format.

Śledzę wpisy blogowe @tBane, czytałem je wszystkie, tak samo jak wszystkie jego wątki na forum. Stąd wiem, że ten projekt to w praktyce plac zabaw do eksperymentowania, wiele rzeczy powstaje w dziwny sposób lub w dziwnej kolejności, dlatego naciskanie na cokolwiek nie ma sensu (też próbowałem!), bo i tak zrobi po swojemu. To zabawa, nie korpo. 😉

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.