Tworzę więc grę, w której możesz wysyłać statki do lokalizacji, aby sprzedawać lub kupować surowce, takie jak drewno, żelazo, złoto itp.
Teraz zastanawiałem się, w jaki sposób należy tworzyć zasoby w grze. Wymyśliłem 2 opcje
Utwórz klasę dla każdego zasobu:
public class ResourceBase { private int value; // other base properties } public class Gold : ResourceBase { public Gold { this.value = 40 // or whatever } }
Utwórz instancje klasy zasobów
public class Resource { string name; int value; public Resource(string name, int value) { this.name = name; this.value = value; } } // later on... Resource gold = new Resource("Gold",40);
Dzięki drugiej opcji możliwe jest wypełnienie zasobów gry z pliku resources.json, podoba mi się ta struktura.
Nowe pomysły / wzory / struktury projektowe są zawsze mile widziane!
EDYCJA: To trochę jak towarzysząca aplikacja Assassins Creed Black Flag. Zobacz pasek zasobów na obrazku poniżej
EDYCJA 2: Zrobiłem trochę więcej badań nad „ładowaniem przedmiotów / zasobów z pliku JSON” i znalazłem tego bloga: Moc JSON w tworzeniu gier - Przedmioty . Pokazuje najlepsze opcje 1 i 2. Nadal możesz dodawać funkcje do każdego zasobu :)
źródło
enum
Zamiast tego użyj .Odpowiedzi:
Idź z drugim podejściem, po prostu ze względu na fakt, że możesz wprowadzić nowe typy zasobów lub elementy w dowolnym momencie bez konieczności przepisywania lub aktualizacji kodu ( programowanie oparte na danych ).
Edytować:
Aby wyjaśnić nieco więcej, dlaczego jest to ogólnie dobra praktyka, nawet jeśli masz 100% pewności, że jakaś wartość nigdy się nie zmieni.
Weźmy przykład gry na konsolę wspomniany w komentarzach, ponieważ stanowi to bardzo dobry powód, dla którego nie powinieneś niczego programować na sztywno, chyba że naprawdę masz (lub chcesz) np. Klucze szyfrujące, własne imię itp.
Wydając grę na konsolach, zwykle muszą one przejść proces przeglądu, w którym własna kontrola jakości producenta konsoli przetestuje grę, zagra w nią, wyszuka problemy itp. Jest to obowiązkowe i kosztuje, dużo pieniędzy. Wydaje mi się, że kiedyś przeczytałem, że wydanie może kosztować 30 000-50 000 USD tylko na samą certyfikację.
Teraz wyobraź sobie, że naciskasz na wydanie gry, płacąc 30 000 $ i czekaj. I nagle zauważasz, że niektóre z twoich wartości gry są dosłownie złamane. Załóżmy, że możesz kupić żelazne sztabki za 50 sztuk złota i sprzedać je za 55 sztuk złota.
Co robisz? Jeśli na stałe zapisałeś te rzeczy, będziesz musiał utworzyć nową wersję aktualizacji / wydania, która będzie musiała ponownie przejrzeć recenzję, więc w najgorszym przypadku zapłacisz jeszcze raz za ponowną certyfikację! Założę się, że Ubisoft nie będzie miał nic przeciwko, ale dla twojej małej kieszeni niezależnego producenta gier… ouch!
Ale wyobraź sobie, że gra od czasu do czasu sprawdza zaktualizowane definicje gier (np. W postaci pliku JSON). Twoja gra może pobrać najnowszą wersję i korzystać z niej w dowolnym momencie, bez konieczności nowej aktualizacji. Możesz naprawić tę nierównowagę / wykorzystać / błąd w dowolnym momencie bez konieczności ponownej certyfikacji lub innych wpłaconych pieniędzy. To tylko drobna decyzja projektowa, która nie wydawała się Ci warta, tylko zaoszczędziłeś 5-cyfrową sumę pieniędzy! Czy to nie jest niesamowite? :)
Tylko nie zrozum mnie źle. Dotyczy to również innych rodzajów oprogramowania i platform. Pobieranie zaktualizowanych plików danych jest ogólnie o wiele łatwiejsze i wykonalne dla standardowego użytkownika, bez względu na to, czy jest to gra, czy jakaś aplikacja / narzędzie. Wyobraź sobie grę zainstalowaną w Program Files w systemie Windows. Aktualizator potrzebuje uprawnień administratora, aby modyfikować cokolwiek i nie można modyfikować uruchomionych programów. Dzięki DDD twój program po prostu pobiera dane i wykorzystuje je. Gracz może nawet nie zauważyć, że nastąpiła aktualizacja.
źródło
Ogólna zasada jest taka, że używasz różnych klas, gdy obiekty wymagają innego kodu i instancji tej samej klasy, gdy obiekty wymagają tylko różnych wartości.
Gdy zasoby mają różne mechanizmy gry, które są dla nich unikalne, sensowne może być reprezentowanie ich za pomocą klas. Na przykład, jeśli masz Pluton, który ma okres półtrwania i Króliki, które rozmnażają się zgodnie z sekwencją Fibonacciego , sensowne może być posiadanie klasy podstawowej
Resource
z metodąUpdate
zaimplementowaną jako brak operacji i dwie pochodne klasy,HalfLifeResource
iSelfGrowingResource
które przesłonić tę metodę, aby zmniejszyć / zwiększyć wartość (i być może uwzględniać również logikę, aby zarządzać tym, co dzieje się, gdy przechowujesz oba obok siebie).Ale kiedy twoje zasoby to tak naprawdę głupie liczby, nie ma sensu ich implementować za pomocą czegoś bardziej wymyślnego niż zwykła zmienna.
źródło
Chciałbym dodać, że istnieją dwie dodatkowe opcje:
Interfejs: możesz rozważyć to, jeśli klasa zasobów byłaby tylko „pamięcią” dla 5 liczb całkowitych, a każda inna jednostka miałaby odmienną logikę w odniesieniu do zasobów (np. Wydatki gracza / łup, miasto produkuje ratunek). W takim przypadku możesz w ogóle nie chcieć klasy - po prostu chciałeś ujawnić, że jakiś byt ma zmienną, z którą inni mogą wchodzić w interakcje:
ponadto ma to tę zaletę, że możesz ograbić miasto dokładnie tym samym kodem, co zrabowałbyś flotę wroga, promując ponowne użycie kodu.
Minusem jest to, że implementacja takiego interfejsu może być nudna, jeśli wiele klas go implementuje, więc możesz skończyć z interfejsami, ale nie z oryginalnym problemem, rozwiązując go z opcją 1 lub 2:
Instancje (z wyliczeniem): w niektórych przypadkach pożądane byłoby użycie wyliczenia zamiast łańcucha podczas definiowania typu zasobu:
to zapewni trochę „bezpieczeństwo”, ponieważ IDE daje wykaz wszystkich ważnych zasobów podczas przypisywania go po wpisaniu kropki
ResourceType = Resource.
, dodatkowo istnieje więcej „sztuczki” z teksty stałe ty może potrzebne, jak wyraźnej rodzaju lub składu / flagi które mogę sobie wyobrazić, aby było przydatne podczas tworzenia:Co do tego, co najlepsze - nie ma srebrnej kuli, ale osobiście skłaniałbym się ku jakiejkolwiek formie, mogą nie być tak wymyślne jak interfejs, ale są proste, nie zaśmiecają drzewa dziedziczenia i są szeroko rozumiane.
źródło
my-great-enum
) JSON na wyliczenie enum ( ) w języku C #MyGreatEnum
.Powinieneś użyć opcji 1, która ma 3 zalety:
new Gold(50)
resource instanceof Gold
źródło
Jeśli twoje zasoby nie mają własnej logiki biznesowej lub specjalnych scenariuszy dotyczących interakcji z otoczeniem, inne zasoby (oprócz handlu, które omówiłeś) lub odtwarzacz, to opcja druga bardzo ułatwia dodawanie nowych.
Jeśli musisz wziąć pod uwagę sytuacje, które mogłyby się zdarzyć, jeśli zmieszałeś dwa zasoby do wytworzenia, jeśli zasoby mogą mieć znacznie inne właściwości (wiadro wody bardzo różni się od bryły węgla) lub inne złożone interakcje ekonomiczne, wówczas podejście 1 to znacznie bardziej elastyczny dla silnika, ale nie dla danych.
źródło