Kodowanie oparte na danych
Każda rzecz, o której wspominasz, jest czymś, co można określić w danych. Dlaczego ładujesz aspecificmap
? Ponieważ konfiguracja gry mówi, że jest to pierwszy poziom, gdy gracz rozpoczyna nową grę, lub ponieważ jest to nazwa bieżącego punktu zapisu w właśnie zapisanym pliku zapisu gracza itp.
Jak znaleźć aspecificmap
? Ponieważ znajduje się w pliku danych, który zawiera identyfikatory map i ich zasoby na dysku.
Potrzebny jest tylko szczególnie mały zestaw „podstawowych” zasobów, które są prawnie trudne lub niemożliwe do uniknięcia na stałe. Przy odrobinie pracy można to ograniczyć do pojedynczej zakodowanej domyślnej nazwy zasobu, takiej jak main.wad
lub podobnej. Ten plik można potencjalnie zmienić w czasie wykonywania, przekazując do gry argument wiersza polecenia, alias game.exe -wad mymain.wad
.
Pisanie kodu opartego na danych opiera się na kilku innych zasadach. Na przykład można uniknąć sytuacji, w której systemy lub moduły proszą o określony zasób, i zamiast tego odwrócić te zależności. Oznacza to, że nie należy DebugDrawer
ładować debug.font
kodu inicjującego; zamiast tego DebugDrawer
weź uchwyt zasobu w jego kodzie inicjującym. Ten uchwyt może zostać załadowany z głównego pliku konfiguracyjnego gry.
Jako konkretne przykłady z naszej bazy kodu mamy obiekt „danych globalnych”, który jest ładowany z bazy danych zasobów (która sama jest domyślnie ./resources
folderem, ale może być przeciążona argumentem wiersza poleceń). Identyfikator bazy danych zasobów tych globalnych danych jest jedyną koniecznie zakodowaną nazwą zasobu w bazie kodu (mamy inne, ponieważ czasami programiści stają się leniwi, ale ostatecznie ostatecznie je naprawiamy / usuwamy). Ten globalny obiekt danych jest pełen komponentów, których jedynym celem jest dostarczenie danych konfiguracyjnych. Jednym ze składników jest składnik danych globalnych interfejsu użytkownika, który zawiera uchwyty zasobów dla wszystkich głównych zasobów interfejsu użytkownika (czcionki, pliki Flash, ikony, dane lokalizacji itp.) Wśród wielu innych elementów konfiguracji. Gdy programista interfejsu użytkownika decyduje się zmienić nazwę głównego zasobu interfejsu użytkownika z /ui/mainmenu.swf
na/ui/lobby.swf
po prostu aktualizują to globalne odniesienie danych; kod silnika nie musi się w ogóle zmieniać.
Używamy tych globalnych danych do wszystkiego. Wszystkie grywalne postacie, wszystkie poziomy, interfejs użytkownika, audio, podstawowe zasoby, konfiguracja sieci, wszystko. (cóż, nie wszystko , ale te inne rzeczy wymagają naprawy.)
To podejście ma wiele innych zalet. Po pierwsze, sprawia, że pakowanie i wiązanie zasobów jest integralną częścią całego procesu. Ścieżki kodowania w silniku również zwykle oznaczają, że te same ścieżki muszą być zakodowane na sztywno w dowolnych skryptach lub narzędziach pakujących zasoby gry, a ścieżki te mogą się potem zsynchronizować. Bazując na pojedynczym kluczowym zasobie i łańcuchach referencyjnych stamtąd, możemy zbudować pakiet zasobów za pomocą jednego polecenia podobnego bundle.exe -root config.data -out main.wad
i wiedzącego, że obejmie on wszystkie potrzebne zasoby. Co więcej, ponieważ producent pakietów po prostu śledzi odniesienia do zasobów, wiemy, że obejmie on tylko potrzebne zasoby i pominie cały pozostały puch, który nieuchronnie gromadzi się przez cały czas trwania projektu (a ponadto możemy automatycznie generować listy tego puch do przycinania).
Trudny przypadek tego wszystkiego jest w skryptach. Stworzenie silnika opartego na danych jest łatwe koncepcyjnie, ale widziałem tak wiele projektów (od hobby do AAA), w których skrypty są uważane za dane i dlatego „wolno” po prostu używać ścieżek zasobów bez rozróżnienia. Nie rób tego Jeśli plik Lua potrzebuje zasobu i po prostu wywołuje funkcję taką jak textures.lua("/path/to/texture.png")
wtedy, potok zasobów będzie miał wiele problemów, wiedząc, że skrypt wymaga /path/to/texture.png
poprawnego działania i może uznać tę teksturę za nieużywaną i niepotrzebną. Skrypty należy traktować jak każdy inny kod: wszelkie potrzebne dane, w tym zasoby lub tabele, powinny być określone we wpisie konfiguracji, który silnik i potok zasobów mogą sprawdzać pod kątem zależności. Dane, które mówią „ładuj skrypt foo.lua
” powinny zamiast tego powiedzieć „foo.lua
i podaj mu te parametry ", gdy parametry obejmują wszelkie niezbędne zasoby. Jeśli skrypt na przykład losowo odradza wrogów, przekaż listę możliwych wrogów do skryptu z tego pliku konfiguracyjnego. Silnik może następnie wstępnie załadować wrogów poziomem ( ponieważ zna pełną listę możliwych odrodzeń), a potok zasobów wie, że łączy wszystkich wrogów z grą (ponieważ są one definitywnie określone w danych konfiguracyjnych). Jeśli skrypty generują ciągi nazw ścieżek i po prostu wywołują load
funkcję, wówczas żadne silnik ani potok zasobów nie mają żadnego sposobu na określenie, które zasoby skrypt może załadować.