Czy warto wdrażać mechanikę / zasady gry oddzielnie od głównego kodu?

25

Nie jestem pewien, czy pasuje do zakresu tej społeczności, czy też powinien przejść do Stackoverflow.

Załóżmy, że chcę, aby moja gra była łatwa do rozszerzenia w jej rdzeniu, tzn. Chcę, aby wiele osób, nawet bez przyzwoitej wiedzy programistycznej, mogło nie tylko modyfikować istniejące zasady, ale nawet dodawać do niej zupełnie nową mechanikę. Sam nie jestem dobrym programistą, ale jestem gotowy do nauki, potrzebuję tylko kilku wskazówek i zapewniam, że da się to zrobić.

Myślałem o tym, czy jest możliwe / wykonalne w jakiś sposób zaimplementować mechanikę gry niezależnie od głównego kodu narzędzia? Mam na myśli, że w grach stołowych mamy te zasady, które nie zawierają faktycznych algorytmów wszystkich działań, ale raczej opisują niektóre protokoły i ograniczenia, silnie odwołując się do kontekstu każdego takiego elementu. Czy można zrobić coś podobnego do gry na PC, np. Opisać wszystkie zasady w bardzo wysokim poziomie, łatwym do odczytania (i zmiany) przez ludzki język programowania, który jest następnie „konsumowany” i analizowany przez kod narzędzia w działająca instancja mechaniki gry?

To naprawdę wydaje się, że muszę napisać własny język i kompilator)), czego oczywiście nie będę w stanie zrobić. Ale może jest łatwiejsze podejście do problemu?

Do Twojej wiadomości: moim językiem wyboru dla kodu narzędzia będzie Python 3.x

tis
źródło
Ponieważ nie masz doświadczenia i pracujesz jako pojedynczy programista, sugeruję, abyś nie próbował wszystkiego wdrażać samodzielnie. Korzystanie z bibliotek SDL2 może być bardzo pomocne w wielu obszarach i mają one powiązania w języku Python, dzięki czemu można pracować w języku, który jest dla Ciebie wygodny. Aby osiągnąć to, czego oczekujesz, projektowanie Twojej architektury powinno być wykonane bardzo, bardzo ostrożnie i oczekiwałbym co najmniej jednego pełnego przepisania nawet dla doświadczonego zespołu. Ponadto, Python nie jest tak naprawdę łatwiejszy do nauczenia się niż jakikolwiek inny język i moim zdaniem ma duże trudności na poziomie pośrednim.
ttbek
Koncepcja, której szukasz, jest powszechna poza twórcami gier. Istnieje zbiór narzędzi programowych zwanych silnikami reguł biznesowych, które właśnie to robię. W grze deweloper możesz również wykorzystać te narzędzia. Na przykład GameplayKit firmy Apple zawiera klasy GKRuleSystem i GKRule do podobnego celu. Rozbudowa tego wymagałaby trochę wysiłku, ale mogłaby być tak skonstruowana, aby zmienić zachowanie bez ponownej kompilacji kodu.
Sandy Chapman

Odpowiedzi:

34

Mówiąc ogólnie, łatwość rozbudowy dowolnego systemu zależy od stopnia, w jakim jego podsystemy są ściśle lub luźno sprzężone . Zazwyczaj im luźniej sprzężone są podsystemy, tym łatwiej jest je modyfikować, ponieważ są odizolowane i niekoniecznie wymagają pełnego zrozumienia systemu jako całości.

Nic nie jest za darmo - taki system zazwyczaj wymaga więcej zasobów (różne kombinacje czasu, pieniędzy i umiejętności) do zbudowania. Stopień rozciągliwości, który opisałeś, uderza mnie bezpośrednio w poziom umiejętności, które przypisałeś sobie. Można to zrobić, ale tworzenie oprogramowania, które opisałeś, jest bardzo trudnym przedsięwzięciem.

Najbliższe mi rzeczy, o których wiem, to Vassal (który jest o wiele bardziej programistyczny niż opisałeś) lub tworzenie modu do tabletop Simulator (który w większości zależy od interakcji człowieka w celu interpretacji i egzekwowania dowolnych reguł gry).

Pikalek
źródło
Solidna rada. Staram się utrzymywać mój kod tak luźno sprzężony, jak to możliwe, aby ułatwić rozszerzalność, ale zawsze będziesz mieć przypadki, w których o wiele łatwiej jest na stałe powiązać / powiązać coś. Nawet jako profesjonalny programista nie jest to łatwe zadanie, a na pewno nie jest przyjazny początkującym.
angarg12
TTS obsługuje teraz Luę, a egzekwowanie reguł nie zależy teraz całkowicie od ludzkich interakcji.
Ave
17

Myślałem o tym, czy jest możliwe / wykonalne w jakiś sposób zaimplementować mechanikę gry niezależnie od głównego kodu narzędzia?

Jest to absolutnie możliwe. Jednym ze sposobów często używanych w grach jest skryptowanie Lua .

Z powiązanego artykułu:

Lua został pierwotnie zaprojektowany w 1993 roku jako język do rozszerzania aplikacji, aby zaspokoić rosnące zapotrzebowanie na dostosowywanie w tamtym czasie.

Wiele gier korzysta z Lua. (Chciałbym utworzyć link, ale reputacja nowego użytkownika ogranicza liczbę moich linków).

Skrypty Lua można skompilować w czasie wykonywania. Oznacza to, że mogą to być (na przykład) pliki tekstowe znajdujące się w katalogu „skryptów” gry, które można łatwo edytować za pomocą moddera. Twoja gra ładuje je i uruchamia.

Widziałem skrypty Lua określające właściwości jednostek w grze. Oto przypadkowy przykład z TA Spring.

Ale chcesz „opisać wszystkie zasady”. Jest to teoretycznie możliwe, ponieważ Lua jest pełnym językiem, ale problem polega na tym, że musisz być wystarczająco przewidziany, aby podstawowy kod gry wiedział, że szuka skryptów rozszerzających jego zachowanie.

Na przykład możesz opracować grę karcianą, która umie szukać kart w katalogu „skryptów / kart”. To świetnie nadaje się do dodawania nowych kart lub edytowania istniejących. Ale jeśli później zechcesz rozszerzyć swoją grę o miniatury na siatce, będziesz musiał edytować kod podstawowy - żadna ilość błahostek Lua nie doprowadzi cię tam sama.

Uwaga: wychowuję Luę, ponieważ wiem, że jest powszechnie stosowana zarówno w grach, jak i oprogramowaniu do dostosowywania. Nie sugeruję, że jest to jedyne rozwiązanie, ani najlepsze dla potrzeb pytającego.

Nichevo
źródło
7
Ta odpowiedź sugeruje, że Lua jest jedynym językiem skryptowym używanym w ten sposób. Istnieje wiele innych języków skryptowych, które można osadzić w silnikach gier. Niektóre silniki również podążają własną drogą i implementują własny język skryptowy specyficzny dla domeny. Zwykle nie polecałbym tego (ponieważ budowanie języka to mnóstwo pracy), ale należy o tym wspomnieć.
Philipp
3

Istnieje ciągłość podejść i to, czy któreś z nich jest warte, będzie zależeć od tego, co próbujesz zrobić. W szczególności, jaką kontrolę chcesz zaoferować tweakerowi?

Na krańcowym końcu kontroli możesz po prostu pozwolić ulepszającemu zmodyfikować kod całej gry. W tym momencie zasadniczo opublikowałbyś kod narzędzia i co najmniej jeden przykład tego, jak go używać. Tweaker może zużywać tyle lub tyle kodu, ile chce.

Podejście, które oferuje nieco mniejszą kontrolę, to jakoś „zamrozić” kod narzędziowy (powiedzmy, kompilując go wcześniej) i pozwolić tylko ulepszającym wypełniać określone funkcje (wywołania zwrotne), ograniczając to, co mogą zrobić. W zależności od tego, co chcesz zrobić, to podejście może przybierać różne formy. Powszechną metodą byłoby umieszczenie całej części wyświetlacza w kodzie narzędziowym / głównym i wszystkich elementów mechanicznych w części podlegającej modyfikacji. Możesz też chcieć zachować część mechaniki w części „zamrożonej”, ponieważ gracze raczej nie chcą ich zmieniać, lub ich zmiana jest zbyt skomplikowana.

Na niskim kontinuum kontinuum pozwala tylko modyfikatorom zmieniać wartości w ustalonym zakresie. Przykładem może być plik danych, który pozwala wybrać kolory rzeczy w grze. Możesz jednak skorzystać z tego podejścia i nadal oferować wiele możliwości dostosowywania. Byłoby możliwe zdefiniowanie wyboru funkcji i umożliwienie komponentom dostosowywania ich tworzenia wywołań zwrotnych z poprzedniego podejścia, ale nie zezwalając im na definiowanie nowych. A może pomijasz część kompozycji i po prostu oferujesz skończony wybór.

Szczegóły wszystkich tych podejść i tego, który z nich pasuje do twojego przypadku użycia, zależą od rodzaju podstawowej gry, którą chcesz stworzyć, i od tego, ile kontroli chcesz zrezygnować. Należy pamiętać, że gry komercyjne zazwyczaj używają tylko drugiej i trzeciej metody, ponieważ pierwsza pozwala graczom tworzyć całkowicie oddzielne gry, co powoduje skomplikowane problemy z licencjonowaniem. Należy zauważyć, że ponieważ podejścia te stanowią kontinuum, drugie podejście może również wprowadzić te problemy.

Ryan1729
źródło
Właściwie to udostępnienie go jako open source. Moim zdaniem problem polega na tym, jak w najprostszy sposób wdrożyć tę rozszerzalność, aby inni gracze (czyli gra MP, btw) mogli tworzyć własne rozszerzenia podstawowego zestawu zasad i udostępniać je każdemu inne, w jakiś sposób unikając konfliktów w tym samym czasie. Więc jeśli jeden użytkownik opracuje jakieś rozszerzenie zastępujące pewne reguły, a inny opracuje inne rozszerzenie, jak zapewnić, że trzeci użytkownik, który chce korzystać z obu tych gier, nie będzie miał problemów ze zgodnością? Oprócz zmuszania ich do ścisłej współpracy.
dniu
... i zaimplementować go w taki sposób, aby nie wymagał modyfikacji żadnego kodu narzędziowego, a tym samym zbyt dużej wiedzy na temat programowania.
dniu
1
Nie sądzę, aby istniał sposób na zapobieganie problemom ze zgodnością (nawet ustawienie koloru czegoś może to powodować!) Myślę, że lepszym rozwiązaniem jest wykrycie możliwych problemów ze zgodnością i dobry sposób dla użytkowników reagować. W zależności od projektu interfejsu kodu narzędziowego i danych rozszerzeń może istnieć sposób zamawiania wywołań zwrotnych itp., Który daje pożądany rezultat. Z drugiej strony może nie być. Ostrzeganie użytkownika, że ​​mogą wystąpić problemy i dlaczego wydaje się, że jest to lepsze doświadczenie niż załamanie się bez wyjaśnienia.
Ryan1729,
2

Jest absolutnie możliwe oddzielenie reguł systemu od kodu, który je stosuje. Wolę tak ustrukturyzować swój kod w przypadku złożonych projektów, ponieważ ułatwia to dodawanie nowych reguł lub zmianę reguł później bez wprowadzania błędów w systemie bazowym. Błędy w silniku reguł można znaleźć szybciej niż błędy w systemie, w którym reguły i inny kod są mieszane razem higgledy-piggledy, ponieważ ten sam silnik reguł jest używany w kółko przez każdą regułę.

To, czy warto, zależy od złożoności systemu. Jakbym nie zawracał sobie głowy Pac-Manem, ale nie wyobrażam sobie pisania Dwarf Fortress w jakikolwiek inny sposób.

Robyn
źródło
Dzięki, @Robyn. Wszelkie porady dotyczące czytania dla kogoś, kto nie wie, jak właściwie podejść do tego projektu? Czy istnieją jakieś ugruntowane wzorce projektowe dla takich aplikacji? Jakieś książki na ten temat?
dniu
Przepraszam, brak książek. Ale podstawowa idea prostego silnika reguł: stwórz klasę reguł, która ma właściwość dla każdej informacji potrzebnej do opisania reguły. Jeśli niektóre z tych właściwości posiadają funkcje lambda, możesz przypisać im złożone zachowania. Stwórz klasę, która tworzy instancję listy Reguł, aby wszystkie reguły były w jednym miejscu. Utwórz kolejną klasę, która ma metodę, która pobiera listę reguł jako dane wejściowe i stosuje je w systemie.
Robyn
2

To zdecydowanie wykonalne. Jeśli warto, zależy od twoich celów.

Nie musisz wymyślać własnego języka ani pisać kompilatora, aby to działało.

Jeśli chcesz, aby twoja gra była łatwa do rozszerzenia, prawdopodobnie dobrym pomysłem jest skorzystanie z niej.

Prawdopodobnie dla ciebie jest więcej pracy, przynajmniej krótkoterminowo, aby stworzyć zrozumiałe systemy i ułatwić modyfikację.

Jedną z gier, która to robi, jest Rimworld (nie mam przynależności) i możesz zobaczyć i nauczyć się z tego, jak to zrobili, po prostu umieszczając wiele danych gry i mechanikę w plikach XML, które są w folderach gry, aby każdy mógł je zobaczyć i modyfikować. Rdzeń / silnik gry został wykonany przy użyciu Unity.

Istnieje również możliwość dalszego rozszerzenia / pogłębienia gry przez faktyczne kodowanie, wiem o tym mniej, ale możesz się dowiedzieć, patrząc na forum modów.

Możliwość modyfikacji sprawia, że ​​gra jest bardziej interesująca dla wielu osób i myślę, że przyczyniła się do jej sukcesu. Pozwala także twórcom oprogramowania na wprowadzanie dowolnych modyfikacji do podstawowej wersji gry, co przyspiesza rozwój i ulepsza grę, ponieważ otrzymują pomoc od wielu osób i mogą zdecydować się na rzeczy oparte na co jest popularne, co wydaje się działać itp.

I oczywiście, szczególnie dla małego niezależnego studia, mają setki ludzi wymyślających i testujących je, co jest pracą, której nie mogliby wykonać sami, ani prawdopodobnie nie zatrudnić do pracy.

użytkownik985366
źródło
Chociaż widzę, jak dane gry mogą być definiowane za pomocą xml, nie wyobrażam sobie, jak można ich użyć do zdefiniowania mechaniki / reguł gry. Czy mógłbyś podać jakiś przykład / artykuł na ten temat?
dniu
@Tis Rules to po prostu inny rodzaj danych. Jeśli mój silnik ma „If A do B”, mogę załadować A i B z pliku XML, teraz użytkownicy mogą wprowadzić dość dowolne reguły, jednym dodatkowym bitem, który może pomóc, jest zapewnienie listy wszystkich możliwych as i Bs, które wspierasz per se , np. „Jeśli status statusu to następnie prędkość 100”, „Jeśli status statusu i czas> x to status statusu” Więc w swoim projekcie zastanów się, jakie reguły chcesz zezwolić na jakie obiekty (status, czas, prędkość ruchu) i z jakim rodzajem kompozycji na to pozwolić (i / lub itp.). Jeśli stan pod wodą i czas> 5 zdrowia -10.
ttbek
1

Możesz przyjrzeć się projektowaniu obiektowemu . Python ma na to dobre wsparcie.

Grube książki są o tym pisane, co może być przerażające, gdy jesteś nowy, ale główne zasady są dość łatwe.

Chodzi przede wszystkim o to, że identyfikujesz, z jakimi obiektami pracujesz. Nie mówisz, o jakiej grze myślisz, ale takie rzeczy jak gracz, potwór, przedmiot, wyposażenie, broń, zbroja i tak dalej są typowymi przedmiotami.

Jeśli chcesz różnych typów gier, prawdopodobnie będziesz chciał obiektu gry, który dba o warunki zwycięstwa i tym podobne. Być może także obiekt mapy?

Czasami nie jest jasne, czy coś zasługuje na przedmiot, czy nie, np. Uszkodzenie. Jeśli nie spowodujesz uszkodzenia obiektu, kod będzie prostszy, ale uczynienie go obiektem ułatwia dostosowanie.

Podklasa: Zarówno broń, jak i pancerze są wyposażeniem. Urządzenia są przedmiotami. Prawdopodobnie istnieją inne rodzaje przedmiotów. Prawdopodobnie przyda ci się zdefiniowanie klasy Combatant, której gracze i potwory są podklasami.

Chodzi o to, że na przykład broń będzie miała wiele cech wspólnych ze wszystkimi innymi typami przedmiotów, będzie miała wagę, rozmiar i inne podobne właściwości.

Tak więc podklasowanie pozwala powiedzieć, że „Broń jest podobna do innych Przedmiotów, ale dodatkowo można ją wykorzystywać, wpływają na zadawane przez nią obrażenia itp.”

Podklasowanie pozwala także twórcom modów powiedzieć: „Mój nowy typ broni jest podobny do broni standardowej, tyle że ...”

Następnie musisz zdecydować, który obiekt jest odpowiedzialny za co. To nie jest tak proste, jak się wydaje, i powinieneś się nad tym zastanowić. Niewłaściwe wybory nie wpłyną znacząco na podstawową grę, ale utrudnią dostosowanie.

Dopóki majsterkujesz samemu, możesz zmieniać rzeczy, ale w momencie, gdy udostępnisz coś publicznie, wprowadzanie zmian staje się znacznie trudniejsze! Ludzie będą tworzyć mody, które zależą od tego, że rzeczy są takie, jakie są teraz. Nawet błędy. Ludzie będą pisać mody, które zależą od błędów pozostających w kodzie. Jeśli zmienisz rzeczy, te mody się zepsują, a moby linczu pojawią się w twoim domu.

Na przykład:

Gracz uzbrojony w Broń atakuje Potwora noszącego wiele Pancerzy. Odbywa się to w określonym trybie gry i na określonej mapie.

Obaj Walczący mogą posiadać Umiejętności takie jak Trafienie Krytyczne i Unik.

Który obiekt jest za co odpowiedzialny?

Nie ma na to jednej właściwej odpowiedzi. Wiele zależy od tego, na jakie dostosowanie chcesz zezwolić.

Jeśli nigdy nie wołasz obiektu (np. Mapy), obiekt ten nie może w żaden sposób zmienić ataku.

Po podjęciu wszystkich tych decyzji udokumentuj je . Napisz „Podręcznik Moddersa”, który dokładnie wyszczególnia, jakie metody można modyfikować w każdym obiekcie, jakie parametry przyjmują, co powinny zwrócić itd., Itd., Itd.

Powodzenia!

Stig Hemmer
źródło
Twój wkład jest naprawdę doceniany i wkładasz w to wiele wysiłku, więc zdecydowanie warto dać +1, ale ogólnie zdaję sobie sprawę z OOP (choć brakuje w tym doświadczenia). Moje obecne problemy dotyczą najbardziej ogólnego podejścia projektowego, które powinienem przyjąć. Moja gra nie jest tak wielka jak D&D według skali, ale mimo to jest BARDZO głęboko osadzona w mechanice. To oznacza wiele skomplikowanych, przeplatanych na różnych poziomach reguł. Które chciałbym pozwolić użytkownikom znacznie rozszerzyć, nie tylko nieznacznie dostosować niektóre wartości. Może to być brak jest łatwiejsze rozwiązanie, rzeczywiście ..
tis
@tis Kluczem do tego jest podjęcie właściwej decyzji o tym, czym tak naprawdę są obiekty w twoim modelu gry OO. „Oczywiste” miejsce na początek to „rzeczowniki”, takie jak broń, zbroja itp., To nie jedyny sposób, który może doprowadzić do splątania nieporządnego kodu. Jeśli reguła mówi „o północy strzelasz tylko potworami srebrnymi kulami w kościelny cmentarz”, to gdzie egzekwujesz tę zasadę w kodzie? W klasie Gun (lub Bullet), w klasie Monster, w klasie Fighter użytkownika broni, w Churchyard czy w klasie Time? Najlepszą odpowiedzią może być „żadne z powyższych” ...
alephzero,
... i przemyśleć cały projekt pod kątem klasy Rules (która prawdopodobnie jest tak naprawdę bazą danych) i klasy Conflict Resolution. Teraz inne zasady, takie jak „jeśli nie masz pocisków, nadal możesz używać swojej broni jako kija” i „jeśli Alice rzuci zaklęcie, które częściowo (ale nie całkowicie) chroni Boba przed ranami postrzałowymi, podczas gdy Charlie próbuje zastrzelić Boba, wtedy .... „mają pojedyncze i„ oczywiste ”miejsce do zaimplementowania w kodzie. Może się okazać, że twoja oryginalna klasa broni niewiele teraz robi, z wyjątkiem niektórych efektów audiowizualnych - a nawet tego, jeśli jest to gra tekstowa!
alephzero
1

Prostym sposobem na uzyskanie podstawowego wsparcia dla tego byłoby rozdzielenie większości wartości liczbowych na jeden lub więcej osobnych plików tekstowych, aby umożliwić zainteresowanym osobom dostosowanie ich do zapomnienia.

Na przykład wspomniałeś o grach stołowych; jeśli masz grę opartą na D&D, możesz mieć weapon_damageplik zawierający linie takie jak Battleaxe: 1d12. Twój kod narzędziowy odczytuje ten plik i za każdym razem, gdy zostanie wyrządzony przez Battleaxekod szkody, generate a number from 1-12, 1 time(s)doda go. Battleaxe: 4d6Zamiast tego poprawiono wiersz do przeczytania generate a number from 1-6, 4 time(s)i dodałem go. Podobnie, możesz mieć folder, Creaturesa wewnątrz masz plik dla każdego stworzenia, w tym linie takie jak AC: 12; następnie dodanie nowych plików do tego folderu stworzyłoby nowe stworzenia. Można to zrobić nawet dla klas postaci, typów terenu, mnóstwa rzeczy.

Ten styl dostosowywania bez kodu może być nadal bardzo wydajny i obejmować wiele elementów Twojej gry. Jednak tak naprawdę nie pozwala użytkownikowi na wprowadzanie zmian, które nie zostały wyraźnie określone. Na przykład, możesz pozwolić Sneak Attack: [damage]na to, aby otrzymać dowolną Istotę lub Klasę w celu dodania [damage]do dowolnego ataku, który spełnia warunki Ataku Podstępnego. Możesz nawet podać sposoby zmiany warunków, na przykład „za każdym razem, gdy atakujesz z ukrycia” lub „za każdym razem, gdy flankujesz” lub „kiedy masz przewagę”. Jeśli jednak użytkownik zdecyduje, że chce, aby ataki typu „ukradkiem” były „Kiedy wykonujesz rzut ataku, możesz również rzucić ukrycie wbrew postrzeganiu celu. Jeśli oba rzuty się powiodą, dodaj obrażenia ataku podstępnego”

Jeśli chciałeś, aby użytkownik mógł dodać do gry zupełnie nowe zachowanie bez potrzeby kodowania na tym samym poziomie co programista, to jak wspomnieli ludzie, zasadniczo szukasz silnika gry lub osobnego języka programowania. W przypadku modyfikacji, które nie wymagają znajomości kodowania, tekstowe pliki danych i struktury folderów mogą nadal zapewniać wiele opcji. Jeśli chcesz, aby użytkownicy zmodyfikowali więcej, musisz poprosić ich o naukę lub znajomość języka programowania.

Kamil Drakari
źródło
Tak, muszę pozwolić im również dodawać nowe mechaniki, po prostu poprawianie niektórych wartości nie wystarczy. Pomyślałem również, że może to wymagać osobnego języka, ale pomyślałem, że może istnieć już taki, który można zastosować w kodzie narzędziowym Python.
to
@tis Czy zastanawiałeś się już, co Wikipedia ma na ten temat? pl.wikipedia.org/wiki/Logic_programming
ttbek
0

[Jestem programistą od dziesięcioleci, ale nie mam doświadczenia w tworzeniu gier, więc może branża gier przyjmuje inne podejście, może z dobrych powodów ...]

Twoje podejście absolutnie ma dla mnie sens. Rdzeń zapewnia podstawowe funkcje, na których opiera się mechanika i reguły, więc jest to interfejs API, który ma być używany przez komponenty wyższego poziomu.

Podczas projektowania interfejsu API moją ulubioną wskazówką jest stworzenie języka, w którym chcesz wyrazić kod wyższego poziomu (oczywiście z ograniczeniami składniowymi twojego języka programowania).

Dobrym podejściem byłoby więc napisanie hipotetycznych reguł i mechaniki w taki sposób, w jaki chcesz je wyrazić (oczywiście przy użyciu składni Pythona), a tym samym dowiedzieć się, co chcesz, aby podstawowy interfejs API zapewniał regułom i mechanice warstwy.

I oczywiście polecam zajrzeć do skryptów istniejących gier, aby dowiedzieć się, co robią.

Ralf Kleberhoff
źródło