W pewnym momencie silnik MUSI specjalizować się i wiedzieć coś o grze. Pójdę tutaj na styczną.
Weź zasoby w RTS. Jedna gra może mieć, Credits
a Crystal
druga Metal
iPotatoes
Powinieneś właściwie używać koncepcji OO i sięgać po maks. ponowne użycie kodu. Oczywiste jest, że Resource
istnieje tutaj koncepcja istnienia.
Dlatego decydujemy, że zasoby mają następujące elementy:
- Zaczep w głównej pętli do samodzielnego zwiększania / zmniejszania
- Sposób na uzyskanie bieżącej kwoty (zwraca an
int
)
- Sposób na dowolne odejmowanie / dodawanie (gracze przenoszący zasoby, zakupy ...)
Zauważ, że to pojęcie Resource
może oznaczać zabójstwa lub punkty w grze! To nie jest bardzo potężne.
Teraz pomyślmy o grze. Możemy w pewnym sensie mieć walutę, handlując groszami i dodając przecinek dziesiętny do wyniku. Nie możemy zrobić „natychmiastowych” zasobów. Jak powiedz „generowanie sieci energetycznej”
Powiedzmy, że dodajesz InstantResource
klasę za pomocą podobnych metod. Teraz (zaczynasz) zanieczyszczać silnik zasobami.
Problem
Weźmy jeszcze raz przykład RTS. Załóżmy, że gracz cokolwiek przekazuje część Crystal
innemu graczowi. Chcesz zrobić coś takiego:
if(transfer.target == engine.getPlayerId()) {
engine.hud.addIncoming("You got "+transfer.quantity+" of "+
engine.resourceDictionary.getNameOf(transfer.resourceId)+
" from "+engine.getPlayer(transfer.source).name);
}
engine.getPlayer(transfer.target).getResourceById(transfer.resourceId).add(transfer.quantity)
engine.getPlayer(transfer.source).getResourceById(transfer.resourceId).add(-transfer.quantity)
Jest to jednak naprawdę dość niechlujny. To ogólny cel, ale bałagan. Już narzuca to, resourceDictionary
co oznacza, że teraz twoje zasoby muszą mieć nazwy! I to jest na gracza, więc nie możesz już mieć zasobów zespołu.
To jest „za dużo” abstrakcji (nie jest to świetny przykład, przyznaję), zamiast tego powinieneś osiągnąć punkt, w którym akceptujesz, że twoja gra ma graczy i kryształ, wtedy możesz po prostu mieć (na przykład)
engine.getPlayer(transfer.target).crystal().receiveDonation(transfer)
engine.getPlayer(transfer.source).crystal().sendDonation(transfer)
Z klasą Player
i klasą CurrentPlayer
, gdzie CurrentPlayer
„s crystal
obiektu automatycznie pokaże rzeczy na HUD dla przeniesienia / wysyłania datków.
To zanieczyszcza silnik kryształem, przekazuje kryształ, komunikaty na interfejsie dla obecnych graczy i tak dalej. Jest zarówno szybszy, jak i łatwiejszy do odczytu / zapisu / konserwacji (co jest ważniejsze, ponieważ nie jest znacznie szybszy)
Uwagi końcowe
Przypadek zasobów nie jest genialny. Mam jednak nadzieję, że nadal rozumiesz o co chodzi. Jeśli cokolwiek wykazałem, że „zasoby nie należą do silnika” jako to, czego potrzebuje konkretna gra i co dotyczy wszystkich pojęć zasobów, to BARDZO różne rzeczy. Zazwyczaj znajdziesz 3 (lub 4) „warstwy”
- „Rdzeń” - jest to podręcznikowa definicja silnika, jest to scena z hakami zdarzeń, dotyczy shaderów i pakietów sieciowych oraz abstrakcyjnego pojęcia graczy
- „GameCore” - jest to dość ogólny rodzaj gry, ale nie wszystkie gry - na przykład zasoby w RTS lub amunicja w FPS. Tutaj zaczyna się logika gry. To tutaj byłoby nasze wcześniejsze pojęcie zasobów. Dodaliśmy te rzeczy, które mają sens w przypadku większości zasobów RTS.
- „GameLogic” BARDZO specyficzny dla aktualnie tworzonej gry. Znajdziesz zmienne o nazwach takich jak
creature
lub ship
lub squad
. Korzystając z dziedziczenia otrzymasz klasy, które obejmują wszystkie 3 warstwy (na przykład Crystal
jest to, Resource
co jest GameLoopEventListener
powiedzmy)
- „Zasoby” są bezużyteczne w żadnej innej grze. Weźmy na przykład skrypty AI łączące okres półtrwania 2, nie będą one używane w RTS z tym samym silnikiem.
Tworzenie nowej gry ze starego silnika
Jest to BARDZO powszechne. Faza 1 polega na rozerwaniu warstw 3 i 4 (i 2, jeśli gra jest CAŁKOWICIE innego typu) Załóżmy, że tworzymy RTS ze starego RTS. Wciąż mamy zasoby, po prostu nie kryształy i inne rzeczy - więc klasy podstawowe w warstwach 2 i 1 nadal mają sens, wszystkie kryształy wymienione w 3 i 4 można odrzucić. Tak robimy. Możemy jednak to sprawdzić jako odniesienie do tego, co chcemy zrobić.
Zanieczyszczenia w warstwie 1
To może się zdarzyć. Abstrakcja i wydajność są wrogami. Na przykład UE4 zapewnia wiele zoptymalizowanych przypadków komponowania (więc jeśli chcesz X i Y, ktoś napisał kod, który X i Y naprawdę razem bardzo szybko - wie, że robi oba) i w rezultacie NAPRAWDĘ jest dość duży. To nie jest złe, ale jest czasochłonne. Warstwa 1 decyduje o tym, jak „przekazać dane do shaderów” i jak animować. Robienie tego w najlepszy sposób dla twojego projektu jest ZAWSZE dobre. Po prostu spróbuj zaplanować przyszłość, ponowne użycie kodu jest twoim przyjacielem, dziedzicz tam, gdzie ma to sens.
Klasy klasyfikacyjne
OSTATNIE (obiecuję) nie bój się zbyt wielu warstw. Silnik jest archaicznym terminem z dawnych czasów rurociągów o stałej funkcji, w których silniki działały prawie tak samo graficznie (i w rezultacie miały wiele wspólnego), programowalny rurociąg odwrócił to do góry nogami i jako taka „warstwa 1” została zanieczyszczona niezależnie od efektów, jakie chcieli osiągnąć programiści. AI była cechą wyróżniającą (ze względu na niezliczoną liczbę podejść) silników, teraz jest to AI i grafika.
Twój kod nie powinien być zapisywany w tych warstwach. Nawet słynny silnik Unreal ma WIELE różnych wersji, z których każda jest specyficzna dla innej gry. Istnieje kilka plików (może innych niż struktury danych), które pozostałyby niezmienione. Jest okej! Jeśli chcesz stworzyć nową grę z innej, zajmie to więcej niż 30 minut. Kluczem jest zaplanowanie, wiedzieć, jakie bity skopiować i wkleić, a co zostawić.