Tak w bardzo mocnym skrócie i uproszczeniu, a do tego dość ogólnie to tak to się przedstawia jak napisałem poniżej. Zaznaczam, że bardzo mocno staram się skracać i pisać ogólnie. Przy czym poza kodem tworze jednocześnie dokumentacje, konfigi, grafikę, itp. itd. Dlatego dość długo się schodzi. Tym bardziej, że .. eh no zacząłem refaktoryzacje renderera, potem trzeba poprawić kwestie konfigów, potem trzeba jednak stworzyć profesjonalny config loader, potem ... - no i się schodzi. Ale dzięki temu, że mam dość ambitne podejście do tematu myśle, że nawet nieźle to wychodzi. Niestety na chwile obecną nie chce jeszcze udostępniać kodu źródłowego ze względu na to, że wprowadzam sporo poprawek i zmian w architekturze.
Jeżeli chodzi o silnik to wybrałem podejście ECS Lite.
Silnik posiada konfigi:
Kopiuj
ls -1 assets/data/configs/
engine.toml
game.json
input.json
input.md
physics.toml
rendering.toml
session.toml
Gra posiada konfigi:
-
gry
-
wielu map
- mapa posiada rozmieszczenie obiektów (prefabs)
- Każdy obiekt na mapie to de facto nazwa prefarb-u wraz z właściwością "role" która może być np. ( PLAYER | ENEMY | NEUTRAL | AMMUNITION | OBJECT` | .... )
I teraz każda taka encja to konfig typu component który jest zapisany w .json . Dla jednej encji może to jeden konfig lub kilka ze sobą powiązanych. Ze względu na logiczny podział elementów (cechy obiektu, parametry dla renderera, itp.).
Konfigi wczytywane są za pomocą specjalnego serwisu. Który to wczytuje konfig, parsuje, mapuje, ładuje wskazane przez konfig zasoby, tworzy obiekt Java.
Wracając do map. Mapa to defacto odpowiedni katalog z podkatalogami (zasoby) oraz konfigami. Konfigi mapy to:
Kopiuj
config.json - parametry mapy (name, description, ...) oraz właściwości (difficult level, ...)
map.tmx - właściwa mapa składająca się wielu warstw (background, obiekty typu collidable, obiekty z gry, triggery).
Obiekt w map.tmx to tylko:
- name of prefarb (np.: "enemy/zombie/runner", "character/soldier")
- "role= { PLAYER | ENEMY | NEUTRAL | AMMUNITION | OBJECT | .... }"
- współrzędne startowe X:Y
- i być może jakieś dodatkowe parametry których na pamięć nie znam.
A więc silnik wczytuje mape gry, z jej konfiga "map.tmx" wyciąga listę obiektów których konfigi wczytuje, przetwarza.... i tworzy z nich obiekty.
Sam plik mapy "map.tmx" tworzę z użyciem gotowego, zewnętrznego edyora Tiled Map Editor. De facto za jakiś czas stworzę taki edytor do tworzenia obiektów w tej grze.
Ten konfig oprócz obiektów zawiera oczywiście rozmieszczenie ścian itp.
Oczywiście do całego projektu podpięty jest logger (SLF4J + logback).
Config Loader.
Konfigi w grze mają różne formaty zależnie od zastosowań. Bo jak poszechnie wiadomo zarówno ".toml" jak i ".json" mają swoje mocne i słabe strony.
Zwykłe konfigi (engine.toml, renderer, ...., session.toml) - są jako TOML. Tutaj ma być jasna, czytelna dla człowieka struktura, możliwość występowania komentarzy.
Konfigi od encji (poszczególnych obiektów) to już JSON. Przy JSON za pomocą pewnych zabiegów da się wstawiać komentarze ale... ale i tak później z takimi komentarzami mogą być problemy. Nie chce zaczynać dyskusji na temat dlaczego ale chodzi o serializacje/deserializacje/mapowanie.
Do konfigów tworzę jednocześnie JSON Schema - aby jasno określić co każdy rodzaj konfiga musi i co może zawierać.
Poza tym tworze też skrypty (python, bash) które są walidatorami konfigów. A te walidatory (python) można podłączyć na GitHub pod CD/CI - więc.... jest dobrze.
Oczywiście tworzę też "docs/tasks.md" - czyli listę zadań do wykonania.
Jest plan aby tą liste zamienić na listę zadań z wykorzystaniem Issues na GitHub Issues. Ale z racji, że na chwile obecną jestem jedynym developerem tego projektu to nie miał bym korzyści z przejścia na Issues list.
Hm... co tam jeszcze. Aaaaa przedmówca wspomniał już dwa razy o kwesti:
- kompilacji textur, animacji
Faktycznie, da się to zrobić. Być może nawet w przyszłości będzie to wprowadzone. Jednak na chwile obecną odkładam temat na dalszy plan.
-
atlas tekstur
W poprzedniej wersji (sprzed obecnie wykonywanej refaktoryzacji) jeden obiekt ("role=ENEMY, prefab=spider_robot/yellow") miał nawet atlas zrobiony, zaimplementowany i to nawet działało.
W czym jednak problem, że nie każdy obiekt używał atlasu? A no w tym, że dla każdego obiektu trzeba poświęcić:
- znalezienie tekstur
- przegranie tych tekstur i zmiana nazwy poszczególnych plików na nazwy zgodne z konwencją. Wiem, w konfigu można by podawać nazwy oryginalne no ale.. jednak w konwencji jest potem znacznie czytelniej. A do tego skrypty tworzą gotowe konfigi z poprawnymi śćieżkami do zasobów. No! Z czasem powstanie edytor obiektów to problem przestanie istnieć.
- stworzenie struktury podkatalogów na zasoby (he he, bez paniki do tego też jest już gotowy skrypt)
- znalezienie odpowiednich dźwięków w formacie .wav - a do tego z konkretnymi parametrami, bo jak się okazuje jednak zdarzają się problemy.
- umieszczenie dźwięków w podkatalogach i zmiana nazw plików na nazwy wg konwencji.
- Wygenberowanie konfiga oraz późniejsza jego edycja
-
Kwestia tworzenia paczek, gdzie:
jedna paczka = pojedyncza mapa z zasobami
jedna paczka = jeden obiekt z zasobami
Fajna sprawa taka paczka tylko.. po co? Jeżeli gra taka jak na przykład Quake 1-3, Diablo, Starcraft skłąda się z tysięcy plików to wtedy paczka jako jeden plik zawierający w sobie wszystkie zasoby - ma sens. Ale tutaj? Jeden obiekt na chwile obecną to około 10 - 40 plików.
Ręczne tworzenie paczki faktycznie oraz implementowanie jej użycia przez silnik jest faktycznie przerostem formy nad treścią. Jednak jeżeli stworzył bym edyor obiektów no to wtedy miało by to może jakiś sens. Chociaż.. też niewielki. Bo po co ktoś miał by z kimkolwiek kopiować i wymieniać się takimi paczkami obiektami/mapami? Bez sensu.
Troche się z tym schodzi.
Konfig od obiektu może być w różny sposób skonfigurowany. I zależnie czy obiekt ma być "rysowany" przez silnik czy renderowany za pomocą tekstur (pliki lub atlas) - wszystko można skonfigurować.
W skrócie mówiąć wszystko da się skonfigurować, sparametryzować.
Oczywiście jest kilka kwestii które jescze muszę zaprojektować, a które nawet nie są zaczęte.
Na przykład:
Gra składa się z wielu map.
- W jaki sposób nastepuje przejście między różnymi mapami w grze?
- Czy silnik ma umożliwiać łatwe tworzenie w grze fabuły?
- Tworzenie dialogów z postaciami w grze
- Tworzenie interakcji w grze. Obecnie zaimplemenotwałem to na zasadzie triggerów. Czyli gracz wszedł na obszar mapy, albo gracz znajduje się na obszarze mapy i nacisnął przycisk "AKCJI".
Dobra, idę zapalić... przypomnę sobie o czym jeszcze miałem wspomnieć, a jak wrócę to ponownie przeczytam wasze komentarze bo na pewno zapomniałem do czegoś nawiązać w mojej odpowiedzi.
@Spine
Obiekty można ze sobą łączyć. Prefab (gotowy obiekt z komponentami) albo ScriptableObject (same dane/parametry) można podpiąć do innego obiektu i w skryptach tego obiektu się do takiego Prefaba odnieść. To daje dużą elastyczność w pracy z silnikiem i stwarza możliwości, które w dotychczasowych poczynaniach nie były takie oczywiste.
Ja np. mam w grze pocisk, do którego podpinam skrypt poruszający pociskiem. I może to być linia prosta, albo też samonaprowadzanie
Przewiduje możliwość dołączania zewnętrznego skryptu (.lua) z logiką dla obiektu. Ale nie zastanawiałem się szerzej nad zastosowaniem.
Kopiuj
tree -C assets/entities/characters/template/player/
assets/entities/characters/template/player/
├── animations
├── audio
│ ├── death.wav
│ ├── hit.wav
│ └── idle.wav
│ └── shooting.wav
├── data
│ ├── config.json
│ ├── PROCEDURAL.json
│ └── SPRITE.json
├── particles
│ └── blood.json
├── scripts
│ └── ai_logic.lua
└── sprites
├── death.png
├── idle.png
└── walk.png
└── shooting.png
@Spine
Unity / Edytor
Jak znajdę chwile czasu spróbuje rzucić okiem.
@flowCRANE, @LukeJL
Ten pomysł z edytorem obiektów aby miał własny format przechowywania danych, a do tego różny od formatu przeznaczonego dla silnika - jest faktycznie istotny. W ten sposób edytor eksportował by gotową paczkę w formacie zrozumiałym dla silnika. To słuszna droga.