Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

Prosty System Dialogów

Ostatnio zmodyfikowano 2024-06-06 02:26
Autor Wiadomość
tBane
Temat założony przez niniejszego użytkownika
» 2024-06-03 21:37:43
dobra spróbuję.
nie wiem jak to dalej napisać...

Konstruktor DialogueOption:
C/C++
DialogueOption( std::wstring text, int nextDialogueID, std::function < bool() > condition = { } ) {
   
this->text = text;
   
this->nextDialogueID = nextDialogueID;
   
// this->visible = true/false
}


Dialogi:
C/C++
Dialogue * dial5 = new Dialogue( 5, L"Czego chcesz?" );
dial5->options.push_back( DialogueOption( L"Kim jesteś?", 6 ) );
dial5->options.push_back( DialogueOption( L"Co tutaj robisz?", 7 ) );
dial5->options.push_back( DialogueOption( L"Nauczysz mnie czegoś o polowaniu?", 10 ) );
dial5->options.push_back( DialogueOption( L"Mam skórę wilczura, chcesz ją odkupić?", 8,[ ] { return player->bag->hasItemsInInventory( "wolf skin", 1 ); } ) );
dial5->options.push_back( DialogueOption( L"Żegnaj", - 1 ) );
dialogues.push_back( dial5 );

Dialogue * dial6 = new Dialogue( 6, L"Jestem myśliwym. Poluję na zwierzynę." );
dial6->options.push_back( DialogueOption( L"Kim jesteś?", 6 ) );
dial6->options.push_back( DialogueOption( L"Co tutaj robisz?", 7 ) );
dial6->options.push_back( DialogueOption( L"Nauczysz mnie czegoś o polowaniu?", 10 ) );
dial6->options.push_back( DialogueOption( L"Mam skórę wilczura, chcesz ją odkupić?", 8,[ ] { return player->bag->hasItemsInInventory( "wolf skin", 1 ); } ) );
dial6->options.push_back( DialogueOption( L"Żegnaj", - 1 ) );
dialogues.push_back( dial6 );

Dialogue * dial7 = new Dialogue( 7, L"Obecnie odpoczywam, ale gdy tylko zbiorę siły, ruszę na polowanie." );
dial7->options.push_back( DialogueOption( L"Kim jesteś?", 6 ) );
dial7->options.push_back( DialogueOption( L"Co tutaj robisz?", 7 ) );
dial7->options.push_back( DialogueOption( L"Nauczysz mnie czegoś o polowaniu?", 10 ) );
dial7->options.push_back( DialogueOption( L"Mam skórę wilczura, chcesz ją odkupić?", 8,[ ] { return player->bag->hasItemsInInventory( "wolf skin", 1 ); } ) );
dial7->options.push_back( DialogueOption( L"Żegnaj", - 1 ) );
dialogues.push_back( dial7 );

Dialogue * dial8 = new Dialogue( 8, L"Bardzo chętnie, dam ci za nią miksturę leczniczą." );
dial8->options.push_back( DialogueOption( L"Proszę, oto skóra wilczura", 9 ) );
dial8->options.push_back( DialogueOption( L"Może innym razem ... ", 5 ) );
dialogues.push_back( dial8 );

Dialogue * dial9 = new Dialogue( 9, L"A to Twoja mikstura." );
dial9->options.push_back( DialogueOption( L"Kim jesteś?", 6 ) );
dial9->options.push_back( DialogueOption( L"Co tutaj robisz?", 7 ) );
dial9->options.push_back( DialogueOption( L"Nauczysz mnie czegoś o polowaniu?", 10 ) );
dial9->options.push_back( DialogueOption( L"Mam skórę wilczura, chcesz ją odkupić?", 8,[ ] { return player->bag->hasItemsInInventory( "wolf skin", 1 ); } ) );
dial9->options.push_back( DialogueOption( L"Żegnaj", - 1 ) );
dialogues.push_back( dial9 );

Dialogue * dial10 = new Dialogue( 10, L"Niestety, nie jestem w stanie Cię niczego nauczyć." );
dial10->options.push_back( DialogueOption( L"Kim jesteś?", 6 ) );
dial10->options.push_back( DialogueOption( L"Co tutaj robisz?", 7 ) );
dial10->options.push_back( DialogueOption( L"Nauczysz mnie czegoś o polowaniu?", 10 ) );
dial10->options.push_back( DialogueOption( L"Mam skórę wilczura, chcesz ją odkupić?", 8,[ ] { return player->bag->hasItemsInInventory( "wolf skin", 1 ); } ) );
dial10->options.push_back( DialogueOption( L"Żegnaj", - 1 ) );
dialogues.push_back( dial10 );
P-181176
pekfos
» 2024-06-03 21:49:59
Nagłówek <functional>.

Taka myśl: Ja tylko zgaduję jaką grę chcesz zrobić, a wydajesz się skupiać na rozwiązywaniu przykładowych problemów które podaję. Co powiesz na to żeby jednak zrobić to jako jeden wielki switch-case który robi wszystko manualnie zamiast używać obiektowej reprezentacji drzewa dialogów? I tak nadajesz identyfikatory ręcznie, mając switch kompilator przynajmniej sprawdzi ich unikalność. To będzie spaghetti i będzie mało skalowalne, ale: możesz zrobić szybko cokolwiek, bez potykania się o ograniczenia własnego języka skryptów i obiektowej struktury. Zrób tak trochę reprezentatywnego contentu do gry, jaką chcesz zrobić. Wtedy odpowiedz sobie na pytanie "czy chcę to zrobić bardziej profesjonalnie?" i jeśli tak, "co musi potrafić mój system dialogów/zadań by przeportować istniejący content". Teraz lecisz z rozwiązaniem nie mając wymagań, co wróży refaktor. Robiąc content i (w miarę potrzeb) system równolegle, każda zmiana systemu będzie robiona z myślą "jak to zrobić najprościej" i "jak nie zepsuć wszystkiego innego", co zwykle oznacza hack i prowizorkę.
P-181177
tBane
Temat założony przez niniejszego użytkownika
» 2024-06-03 21:55:42
Skrypty i tak będą, bo zamierzam trochę więcej opcji dodać, ale zaintrygowałeś mnie tą lambdą i jednak chcę się jej nauczyć.

Dodałem nagłówek <functional>.
Jak teraz przetworzyć tę lambdę ?
P-181178
pekfos
» 2024-06-03 22:12:09
Po pierwsze, jak menu ma być zduplikowane, to wcale nie znaczy że kod musi być zduplikowany. Łatwiej będzie to zmieniać (i mi na to patrzeć).
C/C++
std::vector < DialogueOption > menuHandlarza;
menuHandlarza.emplace_back( L"Kim jesteś?", 6 ); // emplace_back(XXX) równoważne do push_back(TypElementu(XXX))
menuHandlarza.emplace_back( L"Co tutaj robisz?", 7 );
menuHandlarza.emplace_back( L"Nauczysz mnie czegoś o polowaniu?", 10 );
menuHandlarza.emplace_back( L"Mam skórę wilczura, chcesz ją odkupić?", 8,[ ] { return player->bag->hasItemsInInventory( "wolf skin", 1 ); } );
menuHandlarza.emplace_back( L"Żegnaj", - 1 );

Dialogue * dial9 = new Dialogue( 9, L"A to Twoja mikstura." );
dial9->options = menuHandlarza;
dialogues.push_back( dial9 );

Dialogue * dial10 = new Dialogue( 10, L"Niestety, nie jestem w stanie Cię niczego nauczyć." );
dial10->options = menuHandlarza;
dialogues.push_back( dial10 );

Utwórz pole typu std::function<bool()> w DialogueOption i po prostu przypisz w konstruktorze.
C/C++
DialogueOption( std::wstring text, int nextDialogueID, std::function < bool() > condition = { } ) {
   
this->text = text;
   
this->nextDialogueID = nextDialogueID;
   
this->condition = condition;
   
// this->visible = true/false
}
Kod na sprawdzenie warunku już podałem wcześniej:
C/C++
for( auto & option: dial->options )
{
   
if( option.condition && !option.condition() )
       
 continue; // Warunek ustawiony i zwraca false = pomiń
   
   
effectiveOptions.push_back( option );
}
Sugeruję jednorazowo utworzyć kopię opcji które mają być wyświetlone, bo wtedy łatwo jest stwierdzić na przykład ile ich jest, bez konieczności sprawdzania warunków bez przerwy. Nie wiem jak wygląda kod obsługi tych dialogów, ale przefiltrowane effectiveOptions powinno dać się łatwo zintegrować.

Jak teraz przetworzyć tę lambdę ?
Nie musisz wiedzieć nic o niej. Wszystko to jest ukryte przez std::function<>.
C/C++
#include <iostream>
#include <functional>

int four() { return 4; }

void test( std::function < int() > f = { } )
{
   
if( f )
       
 std::cout << "returned: " << f() << '\n';
   
else
       
 std::cout << "not set\n";
   
}

int main()
{
   
test( four );
   
test([ ] { return 123; } );
   
test();
}
returned: 4
returned: 123
not set
P-181179
tBane
Temat założony przez niniejszego użytkownika
» 2024-06-03 22:23:49
Dobra. Działa. Funkcja renderująca chooseBox.

C/C++
void renderChooseBox( sf::RenderWindow * window ) {
   
sf::Texture dialogBoxTexture;
   
if( !dialogBoxTexture.loadFromFile( "assets/GUI/DialogBoxTexture.png" ) ) {
       
return;
   
}
   
   
sf::Sprite background;
   
background.setTexture( dialogBoxTexture );
   
background.setOrigin( dialogSize.x / 2.0f, dialogSize.y / 2.0f );
   
background.setPosition( view.getCenter().x, view.getCenter().y + screenHeight / 2.0f - dialogSize.y / 2.0f );
   
window->draw( background );
   
   
   
availableOptions.clear();
   
for( auto & o: currentDialogue->options ) {
       
if( o.condition && !o.condition() )
           
 continue;
       
       
availableOptions.push_back( o );
   
}
   
   
for( int i = 0; i < availableOptions.size(); i++ ) {
       
       
sf::Text text = sf::Text( availableOptions[ i ].text, dialogBoxFont, characterSize );
       
if( i == chooseOption )
           
 text.setFillColor( sf::Color( 255, 201, 14 ) );
       
else
           
 text.setFillColor( textColor );
       
       
textPosition.x = background.getPosition().x - dialogSize.x / 2.f + padding;
       
textPosition.y = background.getPosition().y - dialogSize.y / 2.f + float( i ) * lineHeight + padding;
       
text.setPosition( textPosition );
       
       
window->draw( text );
       
   
}
   
   
}

P-181180
pekfos
» 2024-06-03 22:49:55
Po tym jako konstruujesz dialogi w kodzie spodziewałem się że to co mówi NPC będzie widoczne razem z opcjami do wyboru. Miałoby sens dla kontekstu, a nie że gracz niecierpliwie pomija dialogi i nagle ma wybór "TAK/NIE" i wtedy uuhh... ;) Ale skoro wyświetlasz albo jedno, albo drugie, tym bardziej bym rozdzielił mowę NPC od wyborów gracza by nie duplikować rzeczy. Przykładowo konkretny numer "dialogu" mógłby się odnosić albo do
  • wyboru gracza, gdzie każda opcja prowadzi do stanu o innym (lub nie) numerze,
  • mowy NPC na którą gracz musi kliknąć "dalej" i jest jeden numer następnego stanu,
  • albo do czegoś zupełnie nie od parady. Meteor uderza w ziemię w czasie rozmowy. Jak się animacja skończy to przechodzimy do następnego stanu.
To ubrać w hierarchię klas by można było wszystko wrzucić do jednego kontenera.

No ale wychodzę tu trochę w system zadań, bo uważam że to ściśle powiązany temat. No chyba że dialogi to dwóch aktorów stojących na baczność i wymieniających zdania, a cokolwiek ciekawszego będzie musiało zaczekać na koniec rozmowy i być zaimplementowane zupełnie niezależnym mechanizmem?
P-181181
tBane
Temat założony przez niniejszego użytkownika
» 2024-06-03 23:11:58
Mogę wyświetlać ostatni fragment wypowiedzi boota, z tym nie będzie problemu. Ale uważam, że obecna opcja jest wystarczająca :-)
P-181182
pekfos
» 2024-06-03 23:13:42
To ty masz wizję na tą grę, ja tylko zgaduję :)
P-181183
1 « 2 » 3 4
Poprzednia strona Strona 2 z 4 Następna strona