Projektuję dla gry, która składa się z postaci, które mają unikalne umiejętności ofensywne i inne zdolności, takie jak budowanie, naprawa itp. Gracze mogą kontrolować wiele takich postaci.
Zastanawiam się nad umieszczeniem wszystkich takich umiejętności i zdolności w poszczególnych poleceniach. Kontroler statyczny zarejestruje wszystkie te polecenia na statycznej liście poleceń. Statyczna lista składałaby się ze wszystkich dostępnych umiejętności i zdolności wszystkich postaci w grze. Kiedy więc gracz wybierze jedną z postaci i kliknie przycisk w interfejsie użytkownika, aby rzucić zaklęcie lub wykonać zdolność, Widok wywoła kontroler statyczny, aby pobrać żądane polecenie z listy i wykonać je.
Nie jestem jednak pewien, czy jest to dobry projekt, biorąc pod uwagę, że buduję swoją grę w Unity. Myślę, że mógłbym wykorzystać wszystkie umiejętności i zdolności jako pojedyncze elementy, które następnie zostałyby dołączone do GameObjects reprezentujących postacie w grze. Następnie interfejs użytkownika musiałby zatrzymać obiekt GameObject postaci, a następnie wykonać polecenie.
Jaki byłby lepszy projekt i ćwiczenie dla gry, którą projektuję?
źródło
Odpowiedzi:
TL; DR
Ta odpowiedź jest trochę szalona. Ale to dlatego, że widzę, że mówisz o implementowaniu swoich umiejętności jako „Poleceń”, co implikuje wzorce projektowe C ++ / Java / .NET, co implikuje duże obciążenie kodu. To podejście jest poprawne, ale istnieje lepszy sposób. Może już robisz w drugą stronę. Jeśli tak, to dobrze. Mam nadzieję, że inni uznają to za przydatne, jeśli tak jest.
Spójrz na podejście oparte na danych poniżej, aby przejść do sedna. Uzyskaj CustomAssetUility Jacoba Pennock jest tutaj i przeczytać jego wpis o niej .
Praca z jednością
Jak inni wspominali, przeglądanie listy 100-300 przedmiotów nie jest tak wielką sprawą, jak mogłoby się wydawać. Więc jeśli jest to intuicyjne podejście, po prostu zrób to. Zoptymalizuj wydajność mózgu. Ale Słownik, jak pokazał @Norguard w swojej odpowiedzi , jest łatwym , niewymagającym siły mózgowej sposobem na wyeliminowanie tego problemu, ponieważ dostajesz ciągłe wstawianie i pobieranie. Prawdopodobnie powinieneś go użyć.
Jeśli chodzi o sprawienie, by działało dobrze w Unity, moje wnętrzności mówią mi, że jeden MonoBehaviour na umiejętność jest niebezpieczną ścieżką zejścia. Jeśli którakolwiek z twoich umiejętności utrzymuje stan w czasie, w którym się wykonuje, musisz zarządzać, aby zapewnić sposób na zresetowanie tego stanu. Korytarze łagodzą ten problem, ale nadal zarządzasz referencją IEnumerator w każdej ramce aktualizacji tego skryptu i musisz absolutnie upewnić się, że masz pewny sposób na zresetowanie umiejętności, aby nie była niekompletna i utknęła w pętli stanu umiejętności cicho zaczynają popsuć stabilność gry, gdy pozostają niezauważone. „Oczywiście, że to zrobię!” mówicie: „Jestem„ dobrym programistą ”!”. Ale tak naprawdę, wszyscy jesteśmy obiektywnie okropnymi programistami, a nawet najwięksi badacze sztucznej inteligencji i autorzy kompilatorów cały czas coś psują.
Spośród wszystkich sposobów, w jakie możesz wdrożyć tworzenie instancji i wyszukiwanie poleceń w Unity, mogę myśleć o dwóch: jednym jest w porządku i nie da ci tętniaka, a drugim pozwala na NIEOGRANICZONĄ MAGICZNĄ TWÓRCZOŚĆ . Raczej.
Podejście zorientowane na kod
Pierwszy to podejście oparte głównie na kodzie. Zalecam, aby uczynić każdą komendę prostą klasą, która albo dziedziczy z absencji klasy BaseCommand, albo implementuje interfejs ICommand (dla zwięzłości zakładam, że te komendy będą tylko zdolnościami postaci, nie jest trudno włączyć Inne zastosowania). Ten system zakłada, że każde polecenie jest komendą IC, ma konstruktor publiczny, który nie przyjmuje parametrów i wymaga aktualizacji każdej ramki, gdy jest ona aktywna.
Sprawa jest prostsza, jeśli używasz abstrakcyjnej klasy bazowej, ale moja wersja używa interfejsów.
Ważne jest, aby Twoje MonoBehaviours obejmowały jedno konkretne zachowanie lub system zachowań ściśle powiązanych. W porządku jest mieć wiele MonoBehaviours, które skutecznie po prostu pośredniczą w zwykłych klasach C #, ale jeśli sam też to zrobisz, może aktualizować wywołania do różnego rodzaju obiektów do tego stopnia, że zaczyna wyglądać jak gra XNA, wtedy „ masz poważne kłopoty i musisz zmienić architekturę.
Działa to całkowicie dobrze, ale możesz to zrobić lepiej (również
List<T>
nie jest to optymalna struktura danych do przechowywania zdolności czasowych, możesz chcieć aLinkedList<T>
lub aSortedDictionary<float, T>
).Podejście oparte na danych
Prawdopodobnie możliwe jest zredukowanie efektów umiejętności do logicznych zachowań, które można sparametryzować. Po to naprawdę zbudowano Unity. Jako programista projektujesz system, w którym Ty lub projektant możecie manipulować w edytorze, aby uzyskać różnorodne efekty. To znacznie uprości „fałszowanie” kodu i skupi się wyłącznie na wykonaniu umiejętności. Nie ma potrzeby żonglowania klasami podstawowymi ani interfejsami i rodzajami tutaj. Wszystko będzie oparte wyłącznie na danych (co również uprości inicjowanie instancji poleceń).
Pierwszą rzeczą, której potrzebujesz, jest obiekt skryptowy, który może opisywać twoje umiejętności. Obiekty ScriptableObjects są niesamowite. Są zaprojektowane tak, aby działały jak MonoBehaviours, ponieważ możesz ustawić ich publiczne pola w inspektorze Unity, a zmiany te zostaną serializowane na dysk. Nie są one jednak przyłączone do żadnego obiektu i nie muszą być dołączone do obiektu gry w scenie ani utworzone w instancji. Są to zbiorcze pakiety danych Unity. Mogą serializować podstawowe typy, wyliczenia i proste klasy (bez dziedziczenia) oznaczone
[Serializable]
. Struktury nie mogą być serializowane w Unity, a serializacja umożliwia edycję pól obiektów w inspektorze, więc pamiętaj o tym.Oto ScriptableObject, który próbuje wiele zrobić. Możesz podzielić to na bardziej szeregowe klasy i obiekty ScriptableObjects, ale ma to po prostu dać ci wyobrażenie, jak to zrobić. Zwykle wygląda to brzydko w ładnym nowoczesnym języku obiektowym, takim jak C #, ponieważ naprawdę wydaje się, że to trochę gówna C89 z tymi wszystkimi wyliczeniami, ale prawdziwa moc polega na tym, że teraz możesz tworzyć różnego rodzaju umiejętności bez pisania nowego kodu do obsługi im. A jeśli twój pierwszy format nie robi tego, czego potrzebujesz, po prostu dodawaj go, aż to zrobi. Dopóki nie zmienisz nazw pól, wszystkie stare serializowane pliki zasobów będą nadal działać.
Możesz dodatkowo wyodrębnić sekcję Obrażenia do klasy możliwej do serializacji, abyś mógł zdefiniować umiejętności zadające obrażenia lub leczące się i posiadające wiele rodzajów obrażeń w jednej umiejętności. Jedyną regułą jest brak dziedziczenia, chyba że użyjesz wielu obiektów skryptowalnych i odniesiesz się do różnych plików konfiguracji szkód złożonych na dysku.
Nadal potrzebujesz MonoBehaviour Aktywatora Umiejętności, ale teraz on wykonuje trochę więcej pracy.
NAJBARDZIEJ CZĘŚĆ
Interfejs i ogólne oszustwo w pierwszym podejściu będą działać dobrze. Ale aby naprawdę wyciągnąć jak najwięcej z Unity, ScriptableObjects zabierze Cię tam, gdzie chcesz być. Jedność jest świetna pod tym względem, że zapewnia bardzo spójne i logiczne środowisko dla programistów, ale ma także wszystkie funkcje wprowadzania danych dla projektantów i artystów, które otrzymujesz od GameMaker, UDK, i in. glin.
W zeszłym miesiącu nasz artysta wziął ulepszony typ ScriptableObject, który miał określać zachowanie dla różnych rodzajów pocisków naprowadzających, połączył go z AnimationCurve i zachowaniem, które spowodowało, że pociski zawisły nad ziemią i sprawiły, że ten nowy szalony krążek hokejowy broń śmierci.
Nadal muszę wrócić i dodać konkretne wsparcie dla tego zachowania, aby upewnić się, że działa ono skutecznie. Ale ponieważ stworzyliśmy ten ogólny interfejs opisu danych, był on w stanie wyciągnąć ten pomysł z powietrza i wprowadzić go do gry bez nas, programistów, nawet wiedzących, że próbował to zrobić, dopóki nie przyszedł i powiedział: „Cześć, patrzcie na tej fajnej rzeczy! ” A ponieważ było wyraźnie niesamowite, cieszę się, że mogę dodać bardziej solidne wsparcie dla tego.
źródło
TL: DR - jeśli zastanawiasz się nad wpakowaniem setek lub tysięcy umiejętności do listy / tablicy, którą następnie iterowałbyś, za każdym razem, gdy wywoływana jest akcja, aby sprawdzić, czy akcja istnieje i czy istnieje postać, która może wykonaj to, a następnie przeczytaj poniżej.
Jeśli nie, nie przejmuj się tym.
Jeśli mówisz o 6 znakach / typach znaków i może 30 zdolnościach, to tak naprawdę nie będzie to miało znaczenia, co robisz, ponieważ narzut związany z zarządzaniem złożoności może wymagać więcej kodu i więcej przetwarzania niż tylko zrzucenie wszystkiego na stos i sortowanie...
Właśnie dlatego @eBusiness sugeruje, że mało prawdopodobne jest wystąpienie problemów z wydajnością podczas wysyłania zdarzeń, ponieważ chyba że naprawdę się starasz, nie ma tu zbyt wiele przytłaczającej pracy, w porównaniu do przekształcania pozycji 3- milion wierzchołków na ekranie itp.
Nie jest to również rozwiązanie , ale rozwiązanie do zarządzania większymi zestawami podobnych problemów ...
Ale...
Wszystko sprowadza się do tego, jak duża grasz, ile postaci ma te same umiejętności, ile jest różnych postaci / różnych umiejętności, prawda?
Umiejętności są elementami postaci, ale rejestracja / wyrejestrowanie się z interfejsu poleceń, gdy postacie dołączają lub pozostawiają kontrolę (lub zostają znokautowane / itp.), Nadal ma sens, w bardzo podobny sposób do StarCrafta, za pomocą skrótów klawiszowych i karta dowodzenia.
Mam bardzo, bardzo małe doświadczenie ze skryptami Unity, ale bardzo dobrze czuję się w JavaScript jako języku.
Jeśli na to pozwalają, to niech lista będzie prostym obiektem:
I może być używany jako:
Gdzie może wyglądać funkcja init Dave ().:
Jeśli ma więcej osób niż tylko Dave
.Repair()
, ale możesz zagwarantować, że będzie tylko jeden Dave, zmień go nasystem.notify("register-ability", "dave:repair", this.Repair);
I sprawdź umiejętność za pomocą
system.notify("use-action", "dave:repair");
Nie jestem pewien, jak wyglądają używane listy. (Pod względem systemu typów UnityScript ORAZ pod względem tego, co dzieje się po kompilacji).
Prawdopodobnie mogę powiedzieć, że jeśli masz setki umiejętności, które planujesz po prostu wpisywać na listę (zamiast rejestrować i wyrejestrowywać, na podstawie dostępnych postaci), to iterowanie po całej tablicy JS (ponownie, jeśli to właśnie robią), aby sprawdzić właściwość klasy / obiektu, która odpowiada nazwie akcji, którą chcesz wykonać, będzie mniej wydajna niż ta.
Jeśli istnieją bardziej zoptymalizowane struktury, będą one bardziej wydajne niż to.
Ale w obu przypadkach masz teraz Postacie, które kontrolują własne działania (przejdź o krok dalej i uczyń je komponentami / bytami, jeśli chcesz) ORAZ masz system kontroli, który wymaga minimum iteracji (ponieważ jesteś po prostu wyszukiwanie tabel według nazwy).
źródło