Jestem niedoświadczonym programistą tworzącym grę typu „roguelike” w stylu FTL , używając Pythona (jak dotąd brak PyGame, ponieważ nadal interesuję się tylko tekstem).
Moja gra będzie zawierać dużą liczbę broni (około 50 na początek), które dają unikalne umiejętności. Staram się zrozumieć, jak ustrukturyzować kod obiektowy w sposób, który jest zarówno potężny (pod względem umożliwienia broni mieć radykalnie różne efekty), jak i rozszerzalny (tak, że mogę łatwo dodać więcej broni później, np. Upuszczając ją do folderu ).
Moim pierwszym instynktem było posiadanie klasy BasicWeapon i dziedziczenie innej broni od tej klasy. Wydaje mi się to jednak problematyczne: albo muszę sprawić, by klasa BasicWeapon była tak oklepana, że w zasadzie jest bezużyteczna (jedyne cechy wspólne wszystkich broni to nazwa i typ (pistolet, topór itp.)), Albo muszę przewidzieć każdy wyjątkowy efekt, jaki kiedykolwiek wymyślę i zakoduję w BasicWeapon.
Ten ostatni jest oczywiście niemożliwy, ale ten pierwszy można jeszcze wykorzystać. Pozostaje mi jednak pytanie: gdzie umieścić kod dla poszczególnych broni?
Czy mogę utworzyć plasmarifle.py, rocketlauncher.py, swarmofbees.py itp. Itd. I upuścić je wszystkie w folderze, z którego gra może je zaimportować?
A może istnieje sposób na utworzenie pliku w stylu bazy danych (może coś tak prostego jak arkusz kalkulacyjny Excel), który w jakiś sposób zawiera unikalny kod dla każdej broni - bez potrzeby uciekania się do eval / exec?
Jeśli chodzi o to ostatnie rozwiązanie (bazę danych), myślę, że podstawową kwestią, z którą walczę, jest to, że chociaż rozumiem, że pożądane jest utrzymanie separacji między kodem a danymi, wydaje mi się, że broń zaciera granicę między „kodem” i trochę „dane”; reprezentują ogromną różnorodność podobnych rzeczy, które można znaleźć w grze, w tym sensie, że są jak dane, ale większość z nich będzie wymagać co najmniej jakiegoś unikalnego kodu, który nie jest współdzielony z żadnym innym przedmiotem, w tym sensie są one oczywiście, kod.
Częściowe rozwiązanie, które znalazłem gdzie indziej na tej stronie, sugeruje nadanie klasie BasicWeapon kilku pustych metod - on_round_start (), on_attack (), on_move () itp. - a następnie zastąpienie tych metod dla każdej broni. W odpowiedniej fazie cyklu walki gra wywoła odpowiednią metodę dla broni każdej postaci, a tylko te, które mają zdefiniowane metody, faktycznie coś zrobią. To pomaga, ale wciąż nie mówi mi, gdzie muszę umieścić kod i / lub dane dla każdej broni.
Czy jest jakiś inny język lub narzędzie, którego mogę użyć jako pewnego rodzaju chimery typu half-data, half-code? Czy całkowicie rzeźnię dobrą praktykę programowania?
Moje rozumienie OOP jest co najwyżej szkicowe, więc doceniłbym odpowiedzi, które nie są zbyt informatyczne.
EDYCJA: Vaughan Hilts wyjaśnił w swoim poście poniżej, że zasadniczo mówię o programowaniu opartym na danych. Istota mojego pytania brzmi: w jaki sposób mogę wdrożyć projekt oparty na danych, aby dane mogły zawierać skrypty, umożliwiające nowym broniom robienie nowych rzeczy bez zmiany kodu głównego programu?
źródło
Odpowiedzi:
Chcesz podejścia opartego na danych prawie na pewno, chyba że twoja gra będzie całkowicie nieoczekiwana i / lub wygenerowana proceduralnie w rdzeniu.
Zasadniczo obejmuje to przechowywanie informacji o twojej broni w wybranym języku znaczników lub formacie pliku. Zarówno XML, jak i JSON są dobrymi, czytelnymi opcjami, których można użyć, aby edycja była dość prosta, bez potrzeby korzystania ze skomplikowanych edytorów, jeśli tylko chcesz szybko zacząć. ( A Python może także bardzo łatwo analizować XML! ) Ustawiłbyś atrybuty takie jak „moc”, „obrona”, „koszt” i „statystyki”, które są wszystkie istotne. Sposób, w jaki uporządkujesz swoje dane, zależy od Ciebie.
Jeśli broń wymaga dodania efektu statusu, nadaj mu węzeł efektu statusu, a następnie określ efekty efektu statusu za pośrednictwem innego obiektu opartego na danych. Dzięki temu Twój kod będzie mniej zależny od konkretnej gry, a edytowanie i testowanie gry będzie banalne. Dodatkową zaletą jest to, że nie trzeba przez cały czas kompilować.
Dodatkowa lektura jest dostępna poniżej:
źródło
(Przepraszam, że podałem odpowiedź zamiast komentarza, ale nie mam jeszcze przedstawiciela).
Odpowiedź Vaughana jest świetna, ale chciałbym dodać moje dwa centy.
Jednym z głównych powodów, dla których chcesz używać XML lub JSON i parsować je w środowisku wykonawczym, jest zmiana i eksperymentowanie z nowymi wartościami bez konieczności ponownej kompilacji kodu. Ponieważ Python jest interpretowany i, moim zdaniem, dość czytelny, możesz mieć surowe dane w pliku ze słownikiem i wszystko zorganizowane:
W ten sposób po prostu importujesz plik / moduł i używasz go jako zwykłego słownika.
Jeśli chcesz dodać skrypty, możesz skorzystać z dynamicznej natury funkcji Pythona i 1. klasy. Możesz zrobić coś takiego:
Chociaż uważam, że byłoby to sprzeczne z projektowaniem opartym na danych. Aby uzyskać 100% DDD, będziesz mieć informacje (dane) określające, jakie funkcje i kod będą używane przez określoną broń. W ten sposób nie łamiesz DDD, ponieważ nie łączysz danych z funkcjonalnością.
źródło
Projektowanie oparte na danych
Niedawno przesłałem coś takiego do recenzji kodu .
Po kilku sugestiach i ulepszeniach powstał prosty kod, który zapewniłby względną elastyczność w tworzeniu broni w oparciu o słownik (lub JSON). Dane są interpretowane w czasie wykonywania, a
Weapon
sama weryfikacja przeprowadzana jest przez samą klasę, bez konieczności polegania na całym interprecie skryptu.Projektowanie oparte na danych, mimo że Python jest językiem interpretowanym (zarówno pliki źródłowe, jak i pliki danych można edytować bez potrzeby ich ponownej kompilacji), wydaje się być właściwą rzeczą w takich przypadkach, jak ten, który przedstawiłeś. To pytanie zawiera więcej szczegółów na temat koncepcji, jej zalet i wad. Jest też ładna prezentacja na Cornell University na ten temat.
W porównaniu z innymi językami, takimi jak C ++, które prawdopodobnie używałyby języka skryptowego (takiego jak LUA) do obsługi danych i interakcji silnika x ogólnie ze skryptami oraz określonego formatu danych (takiego jak XML) do przechowywania danych, Python może faktycznie wszystko samo w sobie (biorąc pod uwagę standard,
dict
ale takżeweakref
ten ostatni, szczególnie w przypadku ładowania zasobów i buforowania).Niezależny programista może jednak nie stosować podejścia ekstremalnego opartego na danych, jak sugerowano w tym artykule :
Być może dzięki Pythonowi można skorzystać z najlepszego podejścia zarówno obiektowego, jak i opartego na danych, dążącego zarówno do produktywności, jak i rozszerzalności.
Proste przetwarzanie próbek
W konkretnym przypadku omawianym podczas przeglądu kodu słownik przechowywałby zarówno „atrybuty statyczne”, jak i logikę do interpretacji - gdyby broń zachowywała się w sposób warunkowy.
Na poniższym przykładzie miecz powinien mieć pewne umiejętności i statystyki w rękach postaci klasy „antypadykady” i nie mieć żadnych efektów, z niższymi statystykami, gdy są używane przez inne postacie):
Do celów testowych stworzyłem proste
Player
iWeapon
klasy: pierwszą do trzymania / wyposażania broni (w ten sposób nazywając jej warunkowe ustawienie on_equip), a drugą jako pojedynczą klasę, która pobierałaby dane ze słownika, na podstawie nazwy przedmiotu przekazywanej jako argument podczasWeapon
inicjalizacji. Nie odzwierciedlają prawidłowego projektu klas gier, ale nadal mogą być przydatne do testowania danych:Z pewnymi przyszłymi ulepszeniami, mam nadzieję, że pozwoli mi to nawet kiedyś mieć dynamiczny system rzemieślniczy, przetwarzający komponenty broni zamiast całej broni ...
Test
Lubię to:
Powinien wydrukować:
Dla barda
Dla antypaladyny
źródło