Autor Wątek: Jak zorganizować kod engine'u - konkretne potrzeby.  (Przeczytany 5404 razy)

Offline azmodan

  • Użytkownik

# Luty 22, 2012, 03:49:02
Witam,
na początek wybaczcie mi, że ten post będzie taki obrzydliwie długi, ale nie sposób tego (w każdym razie dokładnie) opisać krócej.

Sprawy najbardziej podstawowe:
Zamierzam pisać w c#, w xna 4, engine dedykowany pod 3D.

Idąc dalej:
Sami pewnie najlepiej rozumiecie, że fakt, że potrafię programować, wcale nie czyni ze mnie programisty (chodzi mi o predyspozycje). I programistą wcale nie jestem. Dlatego też jeśli o mnie chodzi, to interesuje mnie wyłącznie paradygmat obiektowy. Nawet nie tyle "interesuje" (bo to brzmi jak bezpodstawny kaprys), a raczej: jest to dla mnie jedyna realna opcja po której potrafię się sprawnie poruszać. W dodatku ambicje są takie, że chcę teraz zacząć, obrawszy już teraz taki kierunek, by móc ten engine stale rozwijać/wzbogacać do kolejnych i kolejnych gierek. Nie chodzi o jakieś wielkie "nie wiadomo co", tylko (w wielkim przeogromnym skrócie) jak będę za ileś tam projektów chciał zrobić semi-rpg, to sobie dopiszę klasę odpowiedzialną za system questów, podłączę ją do engine'u, a w edytorze dodam odpowiednie narzędzia do zarządzania tym. Zdaję sobie sprawę, że obiektowość, wirtualne klasy i metody etc, to nie jest rozwiązanie maksymalnie optymalne pod kątem wydajności, ale spoko. Przeboleję to jako cenę wygody, czytelności (dla mnie, subiektywnie) i możliwości w miarę eleganckiego rozwijania. Jestem przekonany, że na moje potrzeby w zupełności wystarczy, jak uda mi się wycisnąć tyle wydajności na ile obiektowość, wirtualizacja, polimorfizm itp pozwalają.

Mogę śmiało powiedzieć, że umiejętności mam dość, żeby jakąś tam grę w 3D za-hard-code-ować w xna z marszu (nie bez trudu oczywiście, ale jednak). Niemniej właśnie za-hard-code-ować. Żeby to był jednak faktycznie engine, taki w miarę re-usable (a nawet jeśli już nie stricte re-usable, to chociaż re-rozwijalny), to już zdecydowanie gorzej. Głównie dlatego, że nie mam zielonego pojęcia o projektowaniu, o tym jak sobie to zaplanować tak, żeby za 6 miesięcy się nie okazało, że szybciej mi pójdzie napisać wszystko od nowa niż refactoring.

Kwestia elastyczności / uniwersalności:
Wydaję mi się, że nie potrzebuję jakiejś tam 100% elastyczności. To by wymagało ode mnie pisania zbyt makabrycznie uniwersalnego kodu, prawdopodobnie bym się w tym pogubił i poległ gdzieś po drodze. Poza tym, engine sam w sobie nigdy produktem raczej nie będzie, prędzej gry, może. Myślę, że wystarczy jak engine będzie "generalnie" w miarę uniwersalny, ale jako zestaw framework+edytor, zaś poszczególne gry będą ich adaptacją. Co mam na myśli? Mniej więcej to: Rozwijam sobie engine do wersji w której w podstawowym zakresie obsługuje grafikę 3D, wtedy robię backup'a enginu (zamrażając go w ten sposób) i adaptuję na potrzeby prostej gry w 3D. W ten sposób gra jest właśnie adaptacją engine'u. Jeśli w trakcie adaptacji zaimplementuję coś, co w enginie byłoby pożądane, to później do niego trafi. Gra skończona? Wracam do engine'u, wrzucam nowe ficzery które przybyły w projekcie gry, a są dość uniwersalne i rozwijam engine dalej. Np rozbudowuję go o moduł/klasę/etc odpowiedzialną za menu/gui, dodaję system questów i mam wersję 2.0, backup zamrożony i adaptuję engine i edytor tak by w efekcie powstała gierka w stylu mini-rpg. I tak dalej. Uważacie to swoją drogą za rozsądny pomysł? Wydaje mi się, że w ten sposób nie będę tracił czasu na implementowanie jakiś konkretnych funkcjonalności na wyrost, które - może się okazać - nawet nigdy mi się nie przydadzą. Jeśli chodzi o data-driven (czy jakkolwiek się to zwie) to w sumie nie mam z tym żadnego doświadczenia. Zupełnie nic. Tak jak czytałem, to wszystko wydaje się fajne. Bo można zbudować engine+edytor całkowicie uniwersalny (jak chociaż unity3d), w którym można właściwie zrobić bez zmian engine'u/edytora dowolną grę. Fajnie, ale nie sądzę bym podołał. Przeciwnie. Sądzę, że bym nie podołał.

Kwestia planowanych obszarów w jakich powinien się engine sprawdzać:
0. XBOX mnie nie interesuje. Tylko WIN. Z czasem (może, kto wie) jak mono game się rozwinie to zaportuję na linucha, ale to dość opcjonalne, bo trudno przewidywać co dalej będzie z portem samego xna na linuxy.
1. NIE MUSI sprawdzać się do światów 100% otwartych (vide morrowind, oblivion etc). W ogóle mnie to nie interesuje. Tylko światy od semi otwarty w dół. (Przez semi otwarty, mam na myśli: jest np 10 rejonów, po dojściu do jakiegokolwiek portalu pokazuje się mapa 2D na której można wybrać do jakiego rejonu chcę się iść. Ale i tak każdy taki rejon jest po prostu klasycznym levelem przed którym jest loading, więc dla enginu to chyba niczym się nie różni od zwykłego podziału na levele.
2. Na pewno: budowanie leveli w edytorze. Zatem musi w pełni obsługiwać tę dynamiczną naturę dodawania obiektów budujących świat. Projektuję level w edytorze, edytor zapisuje do txt czy tam xml, a engine umie tego xmla wczytać, zrobić loading i ot, tadam. Można grać.
3. Ogólnie, łatwość rozbudowy samego engineu. Tak, bym mógł dodać nową klasę/moduł wprowadzającą całkowicie nową funkcjonalność do engine'u, np z czasem kiedyś klasę odpowiedzialną za post produkcję, HLSL, pixel / vertex shadery itd. Np jak będę chciał zaimplementować SSAO (na ten temat również sporo jest materiałów). Domyślam się, że to będzie wymagało zmian w bebechach, sprawienia, żeby inne obiekty zaczęły z nowo zaimplementowanej klasy/funkcjonalności korzystać itd. Sęk w tym, żeby architektura engine'u była gotowa na takie rozwijanie i rozbudowywanie i żeby nie robił się od tego śmietnik.
4. Zamierzam zaimplementować frustum culling, być może Hoffman Atmosphere Light Scattering, tym bardziej, że są na to dostępne tutoriale. Tego typu "bajery" zależnie od potrzeb, tak to ujmijmy.
5. Wiadomo muzyka, dźwięki, jakiś darmowy engine do fizyki, kolizje etc.
6. Reszta to już zupełnie klasyczne wymogi, które są bardziej związane z samym programowaniem, a nie projektowaniem. Ot obsługa kamery fpp, tpp, rts. Itd itp.

Jak sądzę nie mam jakiś nadmiernie wygórowanych potrzeb (mam nadzieję), a problem jest jasny: Jak to zorganizować? Jakie klasy? Za co odpowiedzialne? Jak się komunikują? Jaki poziom abstrakcji? Jaki poziom hierarchizacji? Jak rozwiązać problem samych obiektów budujących świat i ogólnie cały system komponentów. Manager sceny trzyma komponenty (typu obiekt), zaś komponent czym jest? Klasą (od razu np.) trol, która dziedziczy od klasy obiekt-trójwymiarowy, który z kolei dziedziczy od klasy obiekt? To chyba zbyt duża hierarchizacja wyjdzie, tak? Zatem to co przechowuje Manager sceny powinno być kontenerem zawierającym "zarejestrowane" komponenty odpowiedzialne za różne możliwe funkcjonalności obiektów składających się na świat? Tak, że ów trol jest klasą obiekt3D z zarejestrowanym wewnątrz komponentem "atakuj" ustawionym na "każdego kogo widzisz" + inne komponenty które go definiują? To by niby było spoko, ale jak wtedy rozwiązać AI? Takie komponenty mają same umieć podejmować decyzje (AI umiejscowione w tych komponentach), czy tylko zgłaszać siebie do managera AI? ITD ITD ITD.

Ja po prostu nie jestem świadom długoterminowych konsekwencji takich poszczególnych decyzji. Kumam, że większość tego nauczę się po drodze, ale - najzwyczajniej w świecie - chcę chociaż "ruszyć w dobrym kierunku".

Sporo ostatnio szukałem na ten temat, ale przyznam, że trudno o takie materiały dotyczące teoretycznych aspektów, a nawet jeśli, to są już zbyt teoretyczne, albo nie omawiają żadnego konkretnego przypadku, tylko tak sobie gdybają, albo omawiają, ale tak ogólnie że i tak nic z tego nie wynika, nie podają "przykładowego" modelu organizacji klas i odpowiedzialności/zadań z jakich konkretne klasy się wywiązują.

Szukając znalazłem taki model, chyba najbardziej do mnie przemówił:
http://roecode.files.wordpress.com/2008/01/roe.jpg

Sam tutorial (http://roecode.wordpress.com/xna-gameengine-development-series/) również tyczy się xna, to spory plus, ale tak intuicyjnie nie wszystko mi się tam podoba. Trudno mi samemu ocenić czy to aby na pewno dobra droga, przyszłościowo patrząc.

Znalazłem jeszcze coś takiego:
http://www.innovativegames.net/blog/wp-content/uploads/2009/10/structure.jpg

Ale to zakłada mocną hierarchizację, bo ów troll musiałby dziedziczyć ( w najlepszym wypadku ) od potwór - humanoid - component3D - component, co dokładniej widać na roadmapie: http://www.innovativegames.net/blog/blog/2009/10/18/innovation-engine-roadmap-2009-2010/

No i mam problem, jak to dokładnie urządzić, z tą całą organizacją...? Jakieś porady? Pomysły? Stare, dobre, sprawdzone sposoby? Materiały, tutoriale, artykuły? Cokolwiek...? :x

Tak czy owak, z góry dzięki za pomoc :)
Pozdrawiam!

Offline Mr. Spam

  • Miłośnik przetworów mięsnych

Offline Oti

  • Użytkownik

# Luty 22, 2012, 05:24:19
Myślę, że kluczowe założenia, które sobie stawiasz są błędne-zakładasz, że kod, który napiszesz teraz, za dwa lata nadal będziesz uznawał za dobry i, że będziesz nieomylny w pisaniu go. Myślę, że każdy koder potwierdzi, że by dojść do perfekcji, potrzebne jest popełnianie błędów(a jak już do tej perfekcji dojdziesz, to przestanie ona być perfekcją, bo będą nowe ambicje). Wątpliwym jest, że ten kod który teraz napiszesz będzie spełniał wszystie wymagania, że, nawet z pomocą i dobrą radą mądrych głów z tego forum, uda Ci się zaprojektować go tak jak trzeba, by był 'nieskończenie rozwijalny' bez refaktoryzacji. Moja diagnoza jest prosta: nie zakładaj, że całe życie będziesz pisał ten jeden konkretny silnik. Zrób listę feature'ów, jakie chciałbyś docelowo mieć w silniku. Jeśli już kiedyś w życiu pisałeś silnik, to oceń czy będziesz w stanie je zaimplementować(tzn. czy masz jakiś pomysł jak je wrzucić do silnika, żeby działało-jeśli w tym momencie jest pustka w głowie,  to może być ciężko) i usuń te, z którymi wiesz, że nie dasz rady. Jeśli nie pisałeś, to śmiało wywal te bardziej fikuśne(jak np. właśnie SSAO i nawet post processing ogólnie, cienie, cube mapping etc). Następnie zaprojektuj wstępnie jak miałoby to współgrać i zacznij to pisać. Kod z pewnością nie będzie idealny, ale dużo się przy tym nauczysz i w sumie calkiem sporo tego kodu będzie można wykorzystać w następnej, poszerzonej o nowe ficzery(te usunięte z listy) i pozbawionej niektórych błędów wersji. Dobrze, że mierzysz od razu wysoko, ale po prostu trzeba cierpliwości. :)
« Ostatnia zmiana: Luty 22, 2012, 05:27:34 wysłana przez Oti »

Offline Kos

  • Użytkownik
    • kos.gd

# Luty 22, 2012, 10:28:57
na początek wybaczcie mi, że ten post będzie taki obrzydliwie długi, ale nie sposób tego (w każdym razie dokładnie) opisać krócej.

Znany syndrom. Tak samo mają niektórzy powieściopisarze :).

Na początek wybacz mi, że ten post tak obrzydliwie potnę na kawałki, ale nie sposób na to (w każdym razie dokładnie) odpowiedzieć inaczej.

Cytuj
Sprawy najbardziej podstawowe:
Zamierzam pisać w c#, w xna 4, engine dedykowany pod 3D.

Na dzień dobry- Jesteś przekonany, że chcesz pisać endżin? Dlaczego chcę go pisać? "Żeby nauczyć się pisania endżinów" to jedyna odpowiedź, przy której kiwnę głową i nie będę dyskutować :-).

Cytuj
Idąc dalej:
Sami pewnie najlepiej rozumiecie, że fakt, że potrafię programować, wcale nie czyni ze mnie programisty (chodzi mi o predyspozycje). I programistą wcale nie jestem.

Aaaa tam. "Getting things done" to jest u programisty umiejętność nr 1 - jak ją masz, to jesteś programistą.

Cytuj
interesuje mnie wyłącznie paradygmat obiektowy. Nawet nie tyle "interesuje" (bo to brzmi jak bezpodstawny kaprys), a raczej: jest to dla mnie jedyna realna opcja po której potrafię się sprawnie poruszać.

Czil! Tak samo ma 90% programistów w dużych firmach, z tym że oni nawet nie wiedzą, że inne paradygmaty istnieją. (Znają za to dużo wzorców!)
OOP nie jest jedyny słuszny, pewnie nie jest optymalny, powiedziałbym nawet że bywa dziurawy, ale nikt mu nie zarzuci, że się nie sprawdza. Czil. :)

Cytuj
W dodatku ambicje są takie, że chcę teraz zacząć, obrawszy już teraz taki kierunek, by móc ten engine stale rozwijać/wzbogacać do kolejnych i kolejnych gierek. Nie chodzi o jakieś wielkie "nie wiadomo co", tylko (w wielkim przeogromnym skrócie)...

skróty jeszcze faktycznie musisz poćwiczyć :-)

Cytuj
...jak będę za ileś tam projektów chciał zrobić semi-rpg, to sobie dopiszę klasę odpowiedzialną za system questów, podłączę ją do engine'u, a w edytorze dodam odpowiednie narzędzia do zarządzania tym.

I pozmieniasz stary kod tu i tam, bo zauważysz niedociągnięcia w architekturze tego, co już jest napisane i trzeba będzie je obejść/poprawić. Albo kiedyś się wkurzysz na założoną architekturę i przepiszesz wszystko od nowa bądź zrefaktoryzujesz, "tym razem ładniej" - that's the way, that's the way it goes!

Cytuj
Zdaję sobie sprawę, że obiektowość, wirtualne klasy i metody etc, to nie jest rozwiązanie maksymalnie optymalne pod kątem wydajności, ale spoko. Przeboleję to jako cenę wygody, czytelności (dla mnie, subiektywnie) i możliwości w miarę eleganckiego rozwijania.

Jak i cały świat. :-) Wydajności potrzebuje może, nie wiem, 5% kodu? Jak stwierdzisz, że coś jest za wolne, to przepiszesz szybciej. (A jak stwierdzisz, że cała architektura powoduje za duży narzut, to zakasasz rękawy i zmienisz architekturę - od napotykania, diagnozowania i rozwiązywania problemów exp rośnie)

Cytuj
Jestem przekonany, że na moje potrzeby w zupełności wystarczy, jak uda mi się wycisnąć tyle wydajności na ile obiektowość, wirtualizacja, polimorfizm itp pozwalają.
Wirtualizacja to troszkę co innego :-) A tyle wydajności, ile się da, i tak nie wyciśniesz (bo jak już będziesz umiał, to dawno będziesz zatrudniony w Cryteku, albo coś :-)).

Cytuj
Mogę śmiało powiedzieć, że umiejętności mam dość, żeby jakąś tam grę w 3D za-hard-code-ować w xna z marszu (nie bez trudu oczywiście, ale jednak). Niemniej właśnie za-hard-code-ować. Żeby to był jednak faktycznie engine, taki w miarę re-usable (a nawet jeśli już nie stricte re-usable, to chociaż re-rozwijalny), to już zdecydowanie gorzej. Głównie dlatego, że nie mam zielonego pojęcia o projektowaniu, o tym jak sobie to zaplanować tak, żeby za 6 miesięcy się nie okazało, że szybciej mi pójdzie napisać wszystko od nowa niż refactoring.

Meh, po prostu próbuj! To nie jest skomplikowane, przynajmniej na początku: patrzysz jaki kod by Ci się powtarzał z gry na grę, izolujesz go i wyrzucasz do biblioteki. I już jest reusable! Potem ew. trochę sprzątasz przeorganizowujesz, jak bardzo masz potrzebę.

W punkcie "izolujesz go" łatwo o skomplikowanie sobie życia jakimiś przesadnie przekombinowanymi pomysłami. Nie ma co kombinować. Jeśli twierdzisz, że rozumiesz OOP i tym samym nie będziesz np. wciskał dziedziczenia gdzie popadnie, to pewnie skończysz z całkiem ładnym kodem.

Cytuj
Kwestia elastyczności / uniwersalności:
(...)
Uważacie to swoją drogą za rozsądny pomysł? Wydaje mi się, że w ten sposób nie będę tracił czasu na implementowanie jakiś konkretnych funkcjonalności na wyrost, które - może się okazać - nawet nigdy mi się nie przydadzą.



Cytuj
Jeśli chodzi o data-driven (czy jakkolwiek się to zwie) to w sumie nie mam z tym żadnego doświadczenia. Zupełnie nic. Tak jak czytałem, to wszystko wydaje się fajne. Bo można zbudować engine+edytor całkowicie uniwersalny (jak chociaż unity3d), w którym można właściwie zrobić bez zmian engine'u/edytora dowolną grę. Fajnie, ale nie sądzę bym podołał. Przeciwnie. Sądzę, że bym nie podołał.

Meh, ten ficzer poniekąd sam wychodzi, jeżeli nie hardkodujesz rzeczy i projektujesz klasy tak, by można byłoby robić z nimi dużo fajnych rzeczy, co często sprowadza się do "projektujesz klasy tak, by wszystko wyglądało sensownie i nie było przekombinowane" :-)
Natomiast jeżeli ma to wszystko być podpięte pod edytor i w ogóle edytowalne w runtime, to czeka Cię jedno wyzwanie - podpięcie skryptów pod całość. W C# to nie powinien być duży problem, ale jest to dodatkowa komplikacja, owszem.

Cytuj
Kwestia planowanych obszarów w jakich powinien się engine sprawdzać:
0. XBOX mnie nie interesuje. Tylko WIN. Z czasem (może, kto wie) jak mono game się rozwinie to zaportuję na linucha, ale to dość opcjonalne, bo trudno przewidywać co dalej będzie z portem samego xna na linuxy.
Linux to dość egzotyczna platforma do grania :-).

Cytuj
3. Ogólnie, łatwość rozbudowy samego engineu. Tak, bym mógł dodać nową klasę/moduł wprowadzającą całkowicie nową funkcjonalność do engine'u, np z czasem kiedyś klasę odpowiedzialną za post produkcję, HLSL, pixel / vertex shadery itd. Np jak będę chciał zaimplementować SSAO (na ten temat również sporo jest materiałów). Domyślam się, że to będzie wymagało zmian w bebechach, sprawienia, żeby inne obiekty zaczęły z nowo zaimplementowanej klasy/funkcjonalności korzystać itd. Sęk w tym, żeby architektura engine'u była gotowa na takie rozwijanie i rozbudowywanie i żeby nie robił się od tego śmietnik.
Jak chcesz zobaczyć, jak coś takiego można nieźle zrobić, to obejrzyj sobie np. Horde 3D (silnik wyłącznie graficzny), który ma cały pipeline konfigurowalny przez XML-e.

Rendering to generalnie jest złożony proces i zakodowanie architektury tak ogólnej, by dało się do niej bez zmieniania kodu dorzucić nowy efekt (potrzebujący iluś tam nowych passów i dodatkowych atrybutów na wierzchołkach np) wymaga zachodu. Potrzebne Ci to?
Ja bym proponował inne podejście- silnik (czy raczej jego część, renderer) wystawia tyle i tyle efektów graficznych, z których gry mogą przez ładne API korzystać, i jest pod te konkretne efekty zoptymalizowany. Jak chcesz w silniku 2.0 dodać nowe efekty, to modyfikujesz renderer.

Ja wiem, że druga literka w SOLID mówi, że to złe podejście, ale w tym wypadku alternatywa to imho architektoniczny overkill, który nie przyniesie dużo korzyści, a może mieć narzut na wydajności. Chociaż z drugiej strony Horde to zrobił i niby jest szybki... "Niby" :) [citation needed].


O, i pytania się zaczynają...

Cytuj
Jak sądzę nie mam jakiś nadmiernie wygórowanych potrzeb (mam nadzieję), a problem jest jasny: Jak to zorganizować? Jakie klasy? Za co odpowiedzialne? Jak się komunikują? Jaki poziom abstrakcji? Jaki poziom hierarchizacji? Jak rozwiązać problem samych obiektów budujących świat i ogólnie cały system komponentów. Manager sceny trzyma komponenty (typu obiekt), zaś komponent czym jest? Klasą (od razu np.) trol, która dziedziczy od klasy obiekt-trójwymiarowy, który z kolei dziedziczy od klasy obiekt? To chyba zbyt duża hierarchizacja wyjdzie, tak? Zatem to co przechowuje Manager sceny powinno być kontenerem zawierającym "zarejestrowane" komponenty odpowiedzialne za różne możliwe funkcjonalności obiektów składających się na świat? Tak, że ów trol jest klasą obiekt3D z zarejestrowanym wewnątrz komponentem "atakuj" ustawionym na "każdego kogo widzisz" + inne komponenty które go definiują? To by niby było spoko, ale jak wtedy rozwiązać AI?

To jest spoko? Litości... Jak pisałeś gry, to pisałeś je w taki sposób? Z pięcioma poziomami dziedziczenia i dwudziestoma managerami? To jest ten fajny i wygodny sposób? Powaga? :-)

Jako projektant silnika masz wystawić ładne, wygodne i proste API dla usera. To miej cały czas w głowie.

Nie podyktuję Ci całej architektury, ale jeśli jest coś, czego bym unikał, to jest wielki monolit tysiąca klas dziedziczących od siebie nawzajem. Luźnie połączenia między klasami + mało klas = win. Keep it simple.

Proponuję: Wyjdź od tego, co powtarza się w grach - np. rendering i drzewo sceny. Jakie klasy są potrzebne do opisania drzewa sceny? Potem to samo z dźwiękiem, z fizyką, z czym chcesz.

Weź sobie kartkę i spróbuj narysować, jak będzie wyglądało użycie Twojego wymarzonego silnika w kodzie gry.

Cytuj
Sporo ostatnio szukałem na ten temat, ale przyznam, że trudno o takie materiały dotyczące teoretycznych aspektów, a nawet jeśli, to są już zbyt teoretyczne, albo nie omawiają żadnego konkretnego przypadku, tylko tak sobie gdybają, albo omawiają, ale tak ogólnie że i tak nic z tego nie wynika, nie podają "przykładowego" modelu organizacji klas i odpowiedzialności/zadań z jakich konkretne klasy się wywiązują.
"Przykładowy model" znajdziesz w istniejących silnikach. :-)

Cytuj
Szukając znalazłem taki model, chyba najbardziej do mnie przemówił:
http://roecode.files.wordpress.com/2008/01/roe.jpg
To nie model, to schemat. (Wygląda w miarę zdrowo)

Cytuj
Znalazłem jeszcze coś takiego:
http://www.innovativegames.net/blog/wp-content/uploads/2009/10/structure.jpg
No, też wygląda sensownie, głównie jako lista klas, czy raczej funkcjonalności, które w silniku się przydadzą.


Cytuj
Ale to zakłada mocną hierarchizację, bo ów troll musiałby dziedziczyć ( w najlepszym wypadku ) od potwór - humanoid - component3D - component, co dokładniej widać na roadmapie: http://www.innovativegames.net/blog/blog/2009/10/18/innovation-engine-roadmap-2009-2010/
Absolutnie nie widać. :-)

Pojęcia nie mam, co masz z tym dziedziczeniem; śni Ci się po nocach, czy coś? Zaraz Ci z tego wyjdzie wielodziedziczenie, bo humanoid musi dziedziczyć jednocześnie po CreatureWithArms i CreatureWithLegs! Litości...

Mam dla Ciebie uber-pomysł: zaprojektuj cały silnik nie stosując dziedziczenia ani razu. To jest przesada w drugą stronę, ale trochę Ci naprostuje sposób myślenia...

Tak bardziej na temat- jakbym robił grę, to miałbym jedną klasę Character, jedną klasę Item, i tak dalej; prawdopodobnie dałbym im jedno pole będące jakimś obiektem służącym do komunikacji z silnikiem, np. odpowiadający im węzeł na drzewie sceny, albo coś takiego.
Konkretny silnik mógłby tu narzucać jakąś konkretniejszą konwencję, inny sposób integracji logiki gry z nim samym. Zastanów się, jak chciałbyś pisać swoją grę, żeby było łatwo, może coś Ci przyjdzie do głowy?

Offline gotji

  • Użytkownik

# Luty 22, 2012, 14:12:07
Cytuj
Weź sobie kartkę i spróbuj narysować, jak będzie wyglądało użycie Twojego wymarzonego silnika w kodzie gry.

Bardzo mądre słowa. Każdą bibliotekę \ framework \ silnik najlepiej zacząć od napisania kodu który będzie ją \ go wykorzystywać. Unikniesz tym sposobem sporo niepotrzebnej pracy i będziesz miał lepsze pojęcie jak ma to finalnie wyglądać. Traktuję to jak swego rodzaju 'use case'-y.

Offline azmodan

  • Użytkownik

# Luty 22, 2012, 16:07:58
@Oti
No tak, rozumiem, że nie da się tak, żeby wystarczyło na następnych 60 lat. Powiedźmy, że nie chcę, żeby wyszło tak, że architektura nie będzie się nadawać zupełnie do niczego sensownego=większego niż zupełna mini-gierka. Bo ogólnie co do rad, to zapewne masz rację, sęk w tym, że chciałbym, żeby wystarczyło co najmniej do dwóch projektów. Projekt 1: Taka gierka ala oldschoolowy pong, taki gdzie gra się krążkiem, a gracze też mają krążki. Zatem level = stół + 2 postaci graczy, jakieś małe zamknięte otoczenie dla sceny. Do tego jakiś tryb kariery coś na tej zasadzie. Także gra będzie, na podstawie tego w jaki sposób gracz sobie radzi w karierze, dobierała różnych przeciwników, różne sceny / levele, zliczać to robić rankingi. Jeśli chodzi o sam engine to w sumie mało dla niego wymagające. Projekt 2: Jakaś nie nadmiernie obszerna objętościowo platformówka. Tutaj chyba nie ma nic do tłumaczenia.

Tak tedy, trochę mnie nie urządza ruszyć w "byle jakim" kierunku, byle tylko zacząć kodzić. Jeżeli przyjmę jakąś absurdalną taktykę rozplanowania engine'u, to nic z tego nie wyjdzie :)

@Kos
Twoje pytanie na dzień dobry. Po pierwsze chcę się nauczyć pisania engineów, po drugie for fun, bo to wszak przyjemność i satysfakcja zrobić engine, a po trzecie, dlatego, że chcę robić gierki. Wymyślanie i dłubanie nad realizacją kręci mnie bardziej niż samo granie w istniejące gry, także jestem chyba w dobrym miejscu, no nie? :)

To czego w odpowiedzi nie uwzględniłem, to te miejsca gdzie bezdyskusyjnie pokiwałem głową ;)

Cytuj
Wirtualizacja to troszkę co innego :-) A tyle wydajności, ile się da, i tak nie wyciśniesz (bo jak już będziesz umiał, to dawno będziesz zatrudniony w Cryteku, albo coś :-)).
Ok, źle się wyraziłem. Miałem chyba bardziej na myśli: Taka wydajność jaka wyjdzie, gdy po prostu napiszę nieprzekombinowany obiektowy engine, spokojnie mi wystarczy. Życie zweryfikuje, czy tak będzie zaiste.

Cytuj
Meh, po prostu próbuj! To nie jest skomplikowane, przynajmniej na początku: patrzysz jaki kod by Ci się powtarzał z gry na grę, izolujesz go i wyrzucasz do biblioteki. I już jest reusable! Potem ew. trochę sprzątasz przeorganizowujesz, jak bardzo masz potrzebę.

W punkcie "izolujesz go" łatwo o skomplikowanie sobie życia jakimiś przesadnie przekombinowanymi pomysłami. Nie ma co kombinować. Jeśli twierdzisz, że rozumiesz OOP i tym samym nie będziesz np. wciskał dziedziczenia gdzie popadnie, to pewnie skończysz z całkiem ładnym kodem.
Mnie chyba jest trochę trudno wyobrazić sobie jak to zaczynać od tej strony. Dla mnie to jest takie.. hm, chaotyczne? Bardziej naturalne wydaje mi się, że mniej więcej wiem czego będę potrzebował na bank. Np wyświetlanie postaci 3d wraz z texturami, jakieś sterowanie, jakieś AI, jakieś oświetlenie itd bla bla bla. Mniej więcej mam wizję, więc to programuję - system (w tym wypadku engine) który te zadania spełnia. Potem w samej grze tworzę sobie instancję engine, inicjuję i potem (przykładowo):

postać = new [tutaj konstruktor zależy od tego jak sobie wymyślę system komponentów];
engine.add(postać);

I tadam, pisząc grę wiem, że to mi zadziała (pomijam oczywiście błędy i całe debugowanie). Wtedy pisząc grę wiem co się dzieje, bo wiem, że zaimplementowałem w engine funkcje, których będę używał. Tak zawsze to robiłem, może to błąd, nie wiem, ale tak mi się wydawało "logicznie".

Cytuj
To jest spoko? Litości... Jak pisałeś gry, to pisałeś je w taki sposób? Z pięcioma poziomami dziedziczenia i dwudziestoma managerami? To jest ten fajny i wygodny sposób? Powaga? :-)
Tutaj chyba się nie zrozumieliśmy. Właściwie to nie mam większych doświadczeń z programowaniem gier. Programowania uczyłem się pisząc, albo proste programiki spełniające jakieś konkretne zadanie, albo jakieś programy bazodanowe + obrabiające te dane. Sam rozumiesz, w takich przypadkach program+biblioteka z funkcjami+hierarchia klas reprezentujących dane, to było wszystko (aż z nadwyżką) czego potrzebowałem. Nigdy nie pisałem czegoś co byłoby "złożonym systemem", dlatego mam problem z ogarnięciem jak to wszystko ma ze sobą współpracować. Jedyna gra jaką w życiu napisałem to ten pong2D który leży w projektach zaawansowanych.

A, co do zaś tego "To jest spoko? Litości..." to moje "spoko" tyczyło się wyłącznie tego jednego pomysłu, że mam klasę object3D, która jest kontenerem, do którego rejestruję sobie komponenty realizujące poszczególne funkcjonalności. Tak, że do każdej jednostki stosuję zawsze klasę object3D, a chcąc z niej stworzyć trola, rejestruję w niej po prostu odpowiednie komponenty z odpowiednimi ustawieniami, tak, że w efekcie klasa object3D wygląda i zachowuje się jak trol. A manager sceny ma po prostu kolekcję wszystkich object3D. Tylko to dokładnie skwitowałem że jest spoko. Jeśli się nie mylę w unity3D jest to rozwiązane dość podobnie i tam jest elastycznie. Mam rozumieć, że to jednak zły pomysł?

Cytuj
Ja wiem, że druga literka w SOLID mówi, że to złe podejście, ale w tym wypadku alternatywa to imho architektoniczny overkill, który nie przyniesie dużo korzyści, a może mieć narzut na wydajności. Chociaż z drugiej strony Horde to zrobił i niby jest szybki... "Niby" :) [citation needed].
No właśnie mi się nie uśmiecha przy pierwszym enginie w życiu od razu rzucać się na 100% elastyczność. Będę zresztą pisał to sam jeden, a przecież to jest ze dwa razy więcej roboty i na dodatek mogę czegoś nie przewidzieć, coś spaprać. Tylko sobie utrudnię i tak już nie łatwe zadanie.

Cytuj
To nie model, to schemat. (Wygląda w miarę zdrowo)
W miarę? A do czego byś się tam przyczepił, gdybyś chciał? (To dla mnie cenne info, gdy widzę co jest nie tak w danym konkretnym przypadku, a nie tylko teoretycznie kiwam głową i teoretycznie "rozumiem" :D)

Cytuj
Tak bardziej na temat- jakbym robił grę, to miałbym jedną klasę Character, jedną klasę Item, i tak dalej; prawdopodobnie dałbym im jedno pole będące jakimś obiektem służącym do komunikacji z silnikiem, np. odpowiadający im węzeł na drzewie sceny, albo coś takiego.
Konkretny silnik mógłby tu narzucać jakąś konkretniejszą konwencję, inny sposób integracji logiki gry z nim samym. Zastanów się, jak chciałbyś pisać swoją grę, żeby było łatwo, może coś Ci przyjdzie do głowy?
Spoko. Rozkminiam nad tym cały czas i nie zamierzam przestawać, chyba faktycznie wygrzebię jakiś ołówek i trochę pomaluję, może coś wymyślę i poddam Wam tutaj weryfikacji, jeśli nie macie nic przeciwko ;)


@gotji
Może i prawda, ale nigdy tak nie robiłem, dlatego trochę to dla mnie rewolucyjne. Niemniej popróbuję :)

Offline kubera

  • Użytkownik
    • Prywatna strona

# Luty 22, 2012, 16:33:25
1)
Ja piszę swój minimalistyczny silniczek od dwóch lat.
Ważna nie jest obiektowość dla obiektowości, tylko może partycjonowanie
poziome i pionowe architektury.
(moduł dźwięku, menadżer plików, a na górze GUI).
Wypracowałem jedną rzecz, należy stosować fasadę czasami.
U mnie fasada dźwięku, to API: podaj bufor, zakolejkuj bufor i in.
Jeśli chcę się przebić przez tę fasadę, to korzystam z CALLBACKów.
Stosowanie takiego API wydaje mi się czytelne.
Jest prawie nieobiektowe, choć w pliku body jest już obiektowość naturalna.
Nie występuje bezpośrednia zależność z innym kodem.

2) Rozwijam engine pisząc jedną aplikację, jak ją skończę,
to z nową powstanie nowa jego wersja. Standardowej i ogólnej biblioteki nie umiałbym
w rozsądnym czasie wykonać. (z doświadczenia :) )

Offline Kos

  • Użytkownik
    • kos.gd

# Luty 22, 2012, 21:46:20
Mnie chyba jest trochę trudno wyobrazić sobie jak to zaczynać od tej strony. Dla mnie to jest takie.. hm, chaotyczne? Bardziej naturalne wydaje mi się, że mniej więcej wiem czego będę potrzebował na bank. Np wyświetlanie postaci 3d wraz z texturami, jakieś sterowanie, jakieś AI, jakieś oświetlenie itd bla bla bla. Mniej więcej mam wizję, więc to programuję - system (w tym wypadku engine) który te zadania spełnia.

Wyświetlanie to jedna rzecz, sterowanie to druga rzecz (choć obie zależą od wspólnej, czyli "okno i jego pętla komunikatów"). AI jest od nich totalnie niezależne (jeszcze pytanie, co rozumiesz przez AI? co miałaby robić część silnika odpowiedzialna za AI? jakiś konfigurowalny utility do wyszukiwania ścieżek na grafie lub tilemapie, check - co dalej?) Oświetlenie to nie jest niezależne bla bla bla, tylko konfiguracja dla procesu wyświetlania, realizowana jako np. część drzewa sceny.
Wychodzi nam, że endżin składa się z paru podsystemów, z czego jedne są niezależne, a inne są zbudowane na bazie drugich. Cool. Trzeba widzieć zarówno zależności, jak i niezależności.

Cytuj
Potem w samej grze tworzę sobie instancję engine, inicjuję i potem (przykładowo):

postać = new [tutaj konstruktor zależy od tego jak sobie wymyślę system komponentów];
engine.add(postać);

Nic z tego nie rozumiem! :D
Pewnie masz jakąś ideę, ale ja nie wiem, co to jest "komponent", nie wiem, dlaczego postać miałaby być "komponentem", nie mam pojęcia, co to jest "engine", więc co to do jasnej anielki ma być "dodawanie postaci do engine"? Może coś w tym jest, ale tego nie rozumiem, wytłumacz mi :-)
Coś, co od razu zrozumiem, wyglądałoby np. tak, że piszę sobie obiekty logiki mojej gry i mam na przykład klasę Postać, która posiada obiekt typu Graphics (zapewniony przez komponent graficzny silnika). Tworząc postać tworzę jej obiekt Graphics, a potem znajduję odpowiednią gąłąź Drzewa Sceny lub tworzę nową (gdzie drzewo sceny pochodzi od silnika) i wykonuję następną, całkiem zrozumiałą operację - dodaję obiekt Graphics do drzewa sceny. Niby to samo, ale dla mnie brzmi jakby sensowniej.

Cytuj
Sam rozumiesz, w takich przypadkach program+biblioteka z funkcjami+hierarchia klas reprezentujących dane, to było wszystko (aż z nadwyżką) czego potrzebowałem.
"Hierarchia klas" to poprawne sformułowanie, ale brzmi dla mnie trochę jak "korek samochodów" lub "najazd karaluchów". Znacznie wolę "zestaw klas" połączonych ładnym grafem "korzysta z", a nie, fuj, hierarchią. Hierarchie są przereklamowane, hierarchie są passe! Chyba brzmię jak komunista.

Cytuj
A, co do zaś tego "To jest spoko? Litości..." to moje "spoko" tyczyło się wyłącznie tego jednego pomysłu, że mam klasę object3D, która jest kontenerem
Słyszysz jak to brzmi" Object3D jest kontenerem? Wiesz, nie piszę Twojego silnika, ale znacznie wolę, gdy Object3D jest trójwymiarową grafiką, niczym więcej, niczym mniej.

Cytuj
...do którego rejestruję sobie komponenty realizujące poszczególne funkcjonalności. Tak, że do każdej jednostki stosuję zawsze klasę object3D, a chcąc z niej stworzyć trola, rejestruję w niej po prostu odpowiednie komponenty z odpowiednimi ustawieniami
Czyli Twoja klasa Object3D powinna nazywać się WszystkowiedzącyWszystkorobiącyPełenKomponentówBlob3D :-)

Cytuj
tak, że w efekcie klasa object3D wygląda i zachowuje się jak trol. A manager sceny ma po prostu kolekcję wszystkich object3D.
Naprawdę nie wolałbyś projektować logiki gry operując na pojęciach typu Postać? Wiesz, możesz opierać wszystko na dziedziczeniu i w ogóle, ale przypominam że dziedziczenie jest najmocniejszą formą wiązania między klasami i generalnie łatwo się nim wkopać. Widziałem zdecydowanie więcej kodu cierpiącego przez nadmiar dziedziczenia, niż cierpiącego przez niedomiar.

Jak już będziesz pisał grę w taki sposób, że każdy jeden obiekt będzie dziedziczył po Object3D, to OK, tylko co dalej- będziesz miał jakiś inny komponent, np. nie wiem, sterowanie czy AI, spróbujesz zaprojektować go w ten sam sposób i nagle się okaże, że 1/3 Twoich obiektów w grze musi dziedziczyć po paru rzeczach. Wielokrotne dziedziczenie? Czemu nie, w C++ się da, w Pythonie się da, znaczy niektórzy używają! Tylko wiesz, 2/3 języków wielodziedziczenia nie ma, a to jest pewna ostrzegawcza lampka.

Ja nie mam doświadczenia, by Ci powiedzieć, co jest dobre, a co złe. Ale wystarczy mi go, by powiedzieć, że nadużytym dziedziczeniem łatwo się wkopać w zbyt skomplikowany design (najbardziej abusowany feature językowy ever wg mnie), a wielokrotnym dziedziczeniem to już w ogóle. Miałem kiedyś straszną fazę, dziedziczyłem wszystko, uwielbiałem piękne wysokie drzewa grafy dziedziczenia, każdy kod do re-use projektowałem pod dziedziczenie z niego... Ech, to były straszne czasy. :D Lubiłem sobie utrudniać.

Cytuj
Tylko to dokładnie skwitowałem że jest spoko. Jeśli się nie mylę w unity3D jest to rozwiązane dość podobnie i tam jest elastycznie. Mam rozumieć, że to jednak zły pomysł?
Nie wiem, czy zły, i nie wiem, jak jest w unity :-)

Cytuj
W miarę? A do czego byś się tam przyczepił, gdybyś chciał? (To dla mnie cenne info, gdy widzę co jest nie tak w danym konkretnym przypadku, a nie tylko teoretycznie kiwam głową i teoretycznie "rozumiem" :D)
Niczego, chodziło mi że nie jest na moje oko na tyle precyzyjny bym miał się do czego doczepić.

Offline siso

  • Użytkownik

# Luty 23, 2012, 20:50:10
Autorze Wątku,

Wybacz mi, że mój post będzie dla Ciebie średnio przyjemny, ale zaprawdę przeraźliwie oczywiste dla wielu jest to, co chcę Ci powiedzieć, dla reszty zaś - przeraźliwie niwidoczne.

Zanim zaczniesz wymyślać na kartce, jak dodawać nową postać do endżinu, pogooglaj, proszę, za czymś takim:
String calculator kata
To proste ćwiczenie pozwoli Ci spojrzeć na programowanie od strony, z jakiej go jeszcze nie oglądałeś. Wypowiedź Twoja jasno pokazuje, że nie tylko nie potrafisz projektować kodu, do czego otwarcie się przyznałeś, ale także nie potrafisz go pisać. Założenie o nie implementowaniu niepotrzebnych funkcjonalności jest szczytne, ale nieosiągalne bez znajomości programistycznego rzemiosła. Nie da się projektować, gdy nie umie się pisać.

Musisz nauczyć się patrzeć na kod i widzieć to, na co patrzysz.

Kiedy przejdzie Ci już pierwsza złość za to, co powyżej napisałem, i dojdziesz do wniosku że już nie masz powodów, by mnie nie lubić, oraz kiedy zapragniesz cofnąć czas, by Twój post, którym przeraźliwie nas tu uraczyłeś nigdy się nie pojawił, napisz do mnie na priv. Ta prosta technika, którą proponuję Ci się zainteresować, ma więcej mocy, niż na pierwszy rzut oka widać :)

Offline rm-f

  • Użytkownik
    • Tu trolluje

# Luty 23, 2012, 22:57:09
String calculator kata

Dzięki za to, zobaczymy co z tego wyjdzie, choć korci przeczytać następne punkty.

Offline azmodan

  • Użytkownik

# Luty 23, 2012, 23:15:24
@siso
Czy ja wiem, czy średnio przyjemny...? Hm. Masz prawo do swojego zdania, również na mój temat. Niemniej, możesz wierzyć lub nie (pełna dowolność) z wysuwanymi wnioskami średnio utrafiłeś. Dla programistów wszystko jest takie zero jedynkowe, albo się umie programować, albo nie. Jak to w rzeczywistości bywa, zdarzają się też szarości. Napisałem w życiu trochę programów. Jakiś kalkulator, jakiś program do konwertowania między formatami, jakieś aplikacje bazodanowe w asp.net + sql. A przy udziale książki (nie pamiętam już niestety tytułu) napisałem kiedyś nawet prosty endżin 3D tak zupełnie od samego zera bez używania nawet direct x 8) (Niestety, albo i stety, nic mi to nie daje w kontekście obecnego problemu, bo tamten był bez żadnych optymalizacji, kompletnie żadnych.) Z pomocą książki, więc jako osiągnięcie się nie liczy, ale jako wartość edukacyjna jak najbardziej. No i jak bym sobie mocno przypomniał, to pewnie coś jeszcze, ale nigdy żadnych "systemów", "silników". Dlatego nie wiem jak to zbudować tak, żeby w ogóle działało, a przy tym wyciągało jakąś tam niehaniebną ilość fpsów.

No i teraz Ty nie poczuj się urażony, ale chyba jesteś trochę zbyt pyszny. Zważ, że nikt nie zakłada tutaj wątku, nie zadaje pytań, po to by się pochwalić, że zna na nie odpowiedź. Nie, założyłem wątek, właśnie dlatego, że nie wiem i szukam w tej materii porady. W ogromnej mierze również weryfikacji. Nie wątpię też, że sam też kiedyś musiałeś się dowiedzieć i nauczyć jak działa engine, jak się komunikują ze sobą poszczególne jego elementy oraz jak w ogóle rozbić engine na elementy, by było optymalnie.

Ale nie, nie jestem zły bez obaw. Po prostu odpowiedziałem na Twój (tak to nazwijmy) zarzut, na tyle szczerze i otwarcie na ile dałem radę.

I nie twierdzę, że ćwiczenie jest złe. Nawet mogę obiecać, że w rozsądnej przyszłości je wykonam, hehe. Ale nie teraz, bo już nieco inaczej sobie wszystko zaplanowałem.

Pokój ;)



@Kos
Cytuj
Pewnie masz jakąś ideę
Hehehe, jak bym miał jasną i skrystalizowaną ideę niebudzącą mych wątpliwości, to bym takiego wątku nie zakładał, hehe.

No ale dobra, żeby nie było czczego gadania z mojej strony. Poszukałem, pogrzebałem, poczytałem. Myślę, że teraz już domyślam się skąd brało się Twe nierozumienie mej idei, ale szkoda czasu na tłumaczenie czegoś co było moim błędnym rozumowaniem. Trochę się naprostowałem, przysiadłem i spłodziłem mniej więcej taki schemat: http://tinypic.com/r/clr29/5

Oczywiście wzorując się na tych które mnie wcześniej urzekły. Zmieniłem w sumie kilka rzeczy, tak bardziej pod moje potrzeby oraz kilka dla wygody. Wydaje mi się, że większość powinna być oczywista. Tekstury, siatki i shadery chcę trzymać poza grafem, żeby trzymać w ramie tylko jedną kopię każdej ze sztuk. Tam gdzie jest "screen 2d" ma być samo "screen", bez żadnego dziedziczenia. A jeśli idzie o "component" na końcu grafa, to już tłumaczę:

Jak wspominałem, chcę zrobić możliwie (ale bez przesady) elastyczny edytor. Opiszę zatem od strony edytora właśnie. Dodaję sobie nowy węzeł grafu i mogę teraz sobie do niego załadować np model, texturę, normal mapę etc. I to tyle, będzie z tego statyczny graficzny obiekt, np domek. Ale jak chcę z tego węzła grafu zrobić coś ciekawszego, to wtedy w edytorze mam listę komponentów, które mogę do węzła dodać. Takimi komponentami byłyby przykładowo: komponent sprawiający że ten obiekt(węzeł) staje się emiterem particli, komponent dodający animację siatki (czy to animacji siatki na twardo, czy skinning na podstawie wybranego szkieletu), komp. sprawiający, że obiekt staje się portalem (takim jak drzwi prowadzące z outdoor do indoor), itd. Tak, żebym budował to z klocków, zależnie co mi będzie potrzebne. Oczywiście, w każdej grze występują powtarzalne konfiguracje takich klocków, można by je zapisywać jako szablony. I tyle.

Odpowiadając na Twoje pytanie związane z AI, to właśnie za takie AI z prawdziwego zdarzenia, to będę się brał pewnie dopiero za jakiś dłuższy czas. Jak bardziej podstawowe sprawy będą już działały. Niemniej myślałem (wstępnie, bo nie jestem pewien, czy to dobry pomysł) by AI również było jednym z komponentów jakie można dodać do węzła grafu. A samo AI programowane byłoby skryptowo. Same funkcje (jak podajWspolrzedneNajblizszegoWroga) były by wkompilowane w engine ma się rozumieć, a w edytorze jakieś skryptowe "api". Zalety? Mogę przygotować zestaw skryptowych metod, które będą wykorzystywane do programowania AI, a w enginie w ogóle AI właściwego nie implementować. Natomiast w samym edytorze dodaję do obiektu-węzła komponent AI do którego wczytuję plik skryptu AI dla jakiegoś tam rodzaju jednostek (lub taki tworzę). Dobry pomysł? Czy do dupy? :)

Cytuj
"Hierarchia klas" to poprawne sformułowanie, ale brzmi dla mnie trochę jak "korek samochodów" lub "najazd karaluchów".
Ok, oka. Jesteś bardzo wrażliwy w tym temacie. Ale w sumie to pewnie masz rację, nie trudno mi sobie wyobrazić sytuację, w której szukam, w jakim nadpisaniu metody, gdzieś tam po drodze, jest kod, który w danym przypadku powoduje mi błąd logiczny. Natomiast jak to poprawię, to błąd logiczny wyskakuje mi w innym przypadku. Bo to o to chyba, chodzi, right?

Wzorowałem się dlatego na tym co znalazłem, a co stwierdziłeś, że wygląda "w miarę rozsądnie". W sumie nie ma zbyt wiele klas, a dziedziczenie ogranicza się w większości do jednego poziomu. Czyli jest git, przynajmniej jeśli o to chodzi, i w miarę możliwości tak powinno być wszędzie. Right?


@kubera
Tak, tylko jak rozumiem / domyślam się, interesuje Cie w tej materii dość niskopoziomowe programowanie. Wówczas, w pojedynkę, faktycznie idzie bardzo wolno i mozolnie. Dlatego wybrałem xna, bo wtedy uczę się jak to wszystko powinno pracować i współpracować, a nie jak napisać wydajną procedurę wyświetlającą tris'a. W xna chyba raczej z takimi rzeczami nie będę musiał walczyć, więc mam "dodatkowy" czas, który mogę spożytkować na co innego :) Tak to przynajmniej widzę, ale za rady i przestrogi dziękuję :)

Offline siso

  • Użytkownik

# Luty 24, 2012, 00:05:41
@azmodan

Pokój :)
Nie miałem najmniejszego zamiaru Cię urazić i cieszę się, że we właściwy sposób odebrałeś mój przekaz w tym względzie.

Masz rację co do tego, że też musiałem się kiedyś pewnych rzeczy nauczyć. Uczę się rzeczy nadal, codziennie, jeśli mam szczęście :) I dalej wiem o wiele za mało, niż bym chciał wiedzieć.

Moją intencją nie było wskazać, że lama z Ciebie, tylko że projektowanie złożonego frameworka nie powiedzie się bez szerszego spojrzenia na programowanie w ogólności. Mimo tego, że kilka ładnych lat klepię kodzik, dopiero nauka TDD otworzyła mi oczy na design. Nie jest sztuką zaprojektować wszystko na papierze i próbować to zaimplementować. Sztuką jest projektować kod pisząc go. Można projektować kod refaktoryzując go. Można projektować kod usuwając zbyteczne rzeczy. Jeden warunek - trzeba to wyćwiczyć. Ćwiczyć, ćwiczyć i jeszcze raz ćwiczyć.

Siła prostych zadań w stylu string calc. kata polega na tym, że doskonalisz sposób rozwiązania znanego problemu. Nie musisz zastanawiać się  nad poprawnością wymagań, ani rozpraszać ich niekompletnością czy czymkolwiek innym.
Znasz problem i znasz rozwiązanie. Zadaniem jest dojście od zera do finalnej wersji w jak najczystszy sposób. I bez obaw o refaktoryzację - jest wskazana. Wszystko to z silnym wsparciem TDD.

Nie stawiałem Ci zarzutów, co to, to nie :) I nie jestem zbyt pyszny. Przeciwnie - znam swoje słabości. Nadal nie zawsze umiem znaleźć najlepsze rozwiązanie od razu. Nie raz i nie dwa musiałem krążyć, by wrócić na ścieżkę z głośnym facepalmem :) Na co dzień pracuję po prostu także z młodszymi stażem programistami (czasami zdarzy mi się coś ćwiczyć ze studentami), którym bardzo chcę pokazać proste sposoby nauki, pozwalające zaoszczędzić cenny czas, a które sam poznałem dopiero po wielu zmarnowanych na jakieś głupoty latach. Nie ma chyba nic bardziej bezproduktywnego, jak utknąć w jakimiś mrocznym kawałku kodu z wrednym błędem typu "bumerang" - takim, co to co chwilę wraca. A wystarczy raz napisać dla niego test (lub kilka) i po sprawie - od razu widać, kiedy chce wrócić i dlaczego.

A gdyby tak testy do kodu były od samego początku? Gdyby tak zaczynać kodowanie od pisania testów...? Nie byłoby łatwiej?
Na pewno byłoby szybciej. Byłoby więcej czasu na pozostałe rzeczy, a kodowanie byłoby przyjemnością.

Offline azmodan

  • Użytkownik

# Luty 24, 2012, 00:48:28
@siso
Tak natywnie, to zajmuję się grafiką. W pewnym sensie można by nawet rzec, że nie mam najmniejszej - realnej - potrzeby pisania własnego engine'u, bo spokojnie mógłbym skorzystać z darmowej wersji unity. Jest absolutnie pewnym, że do moich gierek w zupełności wystarczyły by jego możliwości. A jeśli nawet okazałoby się, że dynamiczne cienie, czy culling jest dla jakiegoś projektu kluczowy, to przecież wersja pro to 5 klocków. Śmieszne pieniądze w skali engine'ów 3D, no nie? Biorę się za pisanie własnego, jak wcześniej wspomniałem, by się tego nauczyć, oraz 4 fun. Chcesz, nazwij mnie masochistą, ale ja lubię wyzwania w rozsądnym stopniu wykraczające poza moje potencjalne możliwości. Dlaczego? Bo to wszystko co można nazwać "ćwiczeniem" zazwyczaj mało mnie pasjonuje, w efekcie osiągam znacznie mniejszy poziom motywacji i zaangażowania, co z kolei prowadzi do tego, że przedsiębiorąc jakieś (zgodnie z Twoim punktem widzenia) absurdalnie trudne zadanie uczę się znacznie szybciej i znacznie więcej, bo poziom koncentracji i zaciekłości w boju jest znacznie wyższy, nawet jeśli relatywny postęp "projektu" jest znacznie mniejszy.

Dodatkowo, myślę, że przedstawiasz sobie mnie, na niewłaściwym obrazku. Już tłumaczę. Ty, jako programista, masz zupełnie inny zestaw ambicji związanych z samym programowaniem. Chcesz być (zapewne, w idealnej docelowej sytuacji) bezbrzeżnie twórczy i odkrywczy, gdy programujesz. Chcesz, kiedyś, gdy już będziesz tak dobry jak zawsze chciałeś, siadać przed problemami, których rozwiązania nikt nigdy jeszcze się nie podejmował. I chcesz je rozwiązywać szybko, efektywnie i elegancko. Nieprawdaż? A ja nigdy nie będę wirtuozem kodu, nigdy nie będę w tej dziedzinie "artystą", wiem o tym i nie mam takich ambicji. Ja się cieszę, gdy się zwyczajnie czegoś nauczę i to już mnie w pełni zadowala. Satysfakcję daje mi, gdy znajdę poradnik / tutoriala / książkę, gdzie jest omówiona jakaś problematyka, przestudiuję sobie całe omówienie tegoż, przestudiuję sobie kod, uda mi się to zrozumieć i zaprogramować i najlepiej jak jeszcze działa, hehe. I nie chodzę wtedy smutny, ani zły, że to było takie proste, że sam mogłem to wymyślić. Nie, co najwyżej jestem pod wrażenie jak sprytnie to ktoś wymyślił. Podsumowując tę ideologiczną kwestię, jeśli chodzi o programowanie, to jestem hobbystą z przerostem ambicji :) I dlatego nigdy nie będę projektował kodu w trakcie pisania, nie ma się co oszukiwać.

Ale, żeby całe moje wypociny nie były off-topem, to co do całego tego TDD. Powiem tak, zacząłem się bardziej na poważnie wczytywać w różne wątki, tutaj na gamedevie, i uoah. Responsibilyty driven, test driven, functional reactive itp itd. Powoli to sobie ogarniam, czytam, kombinuję. I tu muszę Ci przyznać, że TDD jest pod jednym względem wyjątkowe. To chyba jedyna technika, która znajduje (tak po forach i internecie ogólnie) więcej zwolenników niż przeciwników, a nie po równo. No i jeżeli już sam na temat TDD wszedłeś, to przyznam, że nie bardzo rozumiem, w czym unit testy są lepsze od "stworzenia sztucznego środowiska", w którym sobie daną funkcjonalność przetestuję. Mam na myśli: np w kodzie gry dodam fukncję test21, w której zainicjuję testowany właśnie obiekt, sprawdzę jego metody, coś poprawię i tadam. Może ja po prostu mam złe wyobrażenie na temat tego "czym" jest TDD, a w szczególności sam test w tym kontekście...?

Edit:
Po zastanowieniu: Nie chciałem też zabrzmieć jak ignorant, na zasadzie, że mi TDD niepotrzebne, bo przecież i tak robię to tylko hobbystycznie. Bo nie jestem ignorantem, czego żywo dowodzę faktem, iż tym razem (w pewnym sensie dzięki Twemu naciskowi) zbadałem sprawę znacznie dokładniej.

Owo pytanie skreślam, bo jest beznadziejnie głupie. Wynika z tego, że za bardzo i zbyt dosłownie zasugerowałem się samą nazwą "test-driven". To mi zasugerowało bardziej sam sposób (algorytm) postępowania przy kodowaniu, a nie całą ideologię wraz z wykorzystaniem stosownego, dodatkowego softu. Dlatego właśnie wcześniej pobieżne info uznałem za pełną odpowiedź, że niczego mi w niej nie brakowało. A tu proszę :F jaka miła niespodzianka.

Wielkie dzięki Stary, naprawdę!
NUnit zainstalowany :)
« Ostatnia zmiana: Luty 24, 2012, 02:35:21 wysłana przez azmodan »

Offline siso

  • Użytkownik

# Luty 24, 2012, 02:52:48
@azmodan
Dzięki za wyjaśnienie tła :)
Genialne masz do tego podejście. O to właśnie chodzi, by programowanie było fajne, by wciągało i pozwalało mierzyć się z coraz to nowymi wyzwaniami.

Nawet jeśli programujesz hobbystycznie, to pewnie chciałbyś to robić właśnie w ciekawy sposób, nieprawdaż? TDD najczęściej kojarzy się z niebotycznym nakładem kosztów na pisanie "jakichś tam testów". I w nawiązaniu do Twojego "sztucznego środowiska", totalnie wydają się niepotrzebne.

Tak na prawdę unit test to jest właśnie sztuczne środowisko do uruchomienia jakiegoś przypadku testowego. Musi testować dokładnie jedną rzecz (metodę klasy lub jakąś funkcję swobodną) i musi być pozbawiony wszystkich zewnętrznych zależności. Czyli jeśli testowany kod wywołuje inne obiekty, to ich wywołania muszą zostać zastąpione jakimś fake'owym kodem (mockup), który ustala warunki środowiska testowego. Dodatkowo nie może zależeć od innych testów i musi być powtarzalny. Musi dawać te same rezultaty przy każdym uruchomieniu i w obojętnie jakiej kolejności względem innych testów. I powinien być szybki.

Dlaczego tak? Otóż, najłatwiej pracuje się nad jedną rzeczą naraz. Zastąpienie zależności w postaci rzeczywistego kodu przez podstawione wywołania naszego testowego środowiska pozwala łatwo stwierdzić czy testowany kod zachowuje się w tych konkretnych warunkach jak powinien. Odcięcie od zewnętrznego stanu i ustalenie przed wywołaniem stanu testowego eliminuje trudne do znalezienia błędy. Powtarzalność pozwala odpalać go przy każdej możliwej okazji, bo wiemy, że zawsze zadziała tak samo. Jeśli działa szybko, można odpalać go często bez straty czasu.

Do danej funkcji możemy mieć tyle testów, ile jest niezbędne, by opisać jej całe zachowanie.

Dysponując bazą testów do fragmentu kodu nie boimy się wprowadzać w tym kodzie zmian, o ile wiemy, że testy pokrywają całą jego funkcjonalność. Jeśli wprowadzimy jakiś błąd, część testów powinna się wyłożyć i wskazać na rodzaj błędu od razu.

Mając powyższe na uwadze ktoś przebiegły wpadł na pomysł, by zaczynać kodowanie ficzera od napisania dla niego testu. Test na początku jest niekompilowalny - wiadomo, nie ma dla niego kodu produkcyjnego. Generujemy więc kod i test się kompiluje. Ale jest czerwony, bo kod testowany nie robi tego, co test oczekuje. Implementujemy zatem metodę/funkcję, by test stał się zielony. Kiedy przejdzie, bierzemy na warsztat następny ficzer i tak do skutku, aż zrealizujemy wymagania. Na tym etapie wszystkie testy mamy zielone i cały zestaw wymagań oprogramowany. Kod produkcyjny zawiera TYLKO to, co niezbędne, tylko to, co kazały nam napisać testy. A testy kazały nam napisać tylko to, co było w wymaganiach. Teraz możemy refaktoryzować, by doprowadzić kod do perfekcji. Testy pokazują czy idzie to zgodnie z planem.

Nie znam lepszej od TDD metody projektowania oprogramowania, która daje odpowiednie pokrycie testami, jest szybka i do tego jeszcze wygodna.

Offline azmodan

  • Użytkownik

# Luty 24, 2012, 03:49:15
Można powiedzieć, że powoli zaczynam to sobie wyobrażać. W sumie najbardziej urzekające jest właśnie to, że wszystkie testy lecą automatycznie i niezależnie od siebie. Łatwo wykryć, jeśli zmiany w jednej metodzie czy klasie, sprawią, że siądzie inna / inne. No i eliminuje ciągłe komentowanie i odkomentowywanie jakiś tam sprawdzających coś funkcji i procedur. I to chyba mnie przekonało właśnie, hehe. Co do reszty dobrodziejstw, to pewnie przekonam się o nich dopiero jak się bardziej obcykam.

Testy testami, fajne są, przyznaję. Tylko, że póki co dziwnie nieco przestawić się na to, by testy pisać wpierw. No tak czy owak przekonałeś mnie, możesz być z siebie, kurde dumny, odłożę na razie bezpośrednie rozpoczynanie kodowania engine'u, ale żadnych string caculatorów, bez obaw. Przynajmniej na razie. Póki co znalazłem książkę, która traktuje (przy okazji) o unit testingu w xna (konkretnie tą, gdyby komuś mogło się to przydać: http://www.amazon.com/Professional-XNA-Game-Programming-Windows/dp/0470126779). Spróbuję się z tym oswoić, a potem wracam bezpośrednio do tematu engine'u :)

Gwoli ścisłości, kiepsko nam się pogawędka rozpoczęła, ale spoko z Ciebie gość,
dzięki ! Zabieram się za lekturę :)

Offline siso

  • Użytkownik

# Luty 24, 2012, 12:21:31
Kurde, w takim razie będę z siebie dumny :)
Przykład z kalkulatorem był tylko w celu zbudzenia zainteresowania. W sumie to nawet fajnie, jeśli zaczniesz ćwiczyć TDD na czymś ciekawszym. 

Racja, pogawędkę zacząłem dość kiepsko. Następnym razem postaram się bardziej ;)

Jakbyś miał jakieś spostrzeżenia czy chciałbyś o coś zapytać, wal śmiało na priv czy nawet tutaj.
Powodzenia w ćwiczeniu kunsztu!