|
Programiranje Programski jezici, tehnike, alatke... |
|
Alatke vezane za temu | Vrste prikaza |
10.7.2014, 21:15 | #1 |
V.I.P. Programiranje
Član od: 29.8.2007.
Lokacija: Valjevo
Poruke: 1.349
Zahvalnice: 983
Zahvaljeno 371 puta na 280 poruka
|
[C++] Unity sistem komponenti
Posle duze pauze rek'o da se vratim malo programiranju, a vidim da je i nesto slabo aktivan ovaj deo foruma.
Imao sam priliku da probam malo Unity endzin, posto je postao veoma popularan pa rek'o da vidim i ja o cemu se radi. Ukratko, mnogo mi se svideo njihov "component system" pa sam probao da implementiram nesto slicno sa C++-som. Postavicu kod ovde pa cu ispod malo da izkomentarisem. Kod:
#include <iostream> #include <string> #include <unordered_map> #include <type_traits> #include <memory> #include <cassert> #include <vector> #include <algorithm> template<typename T> struct type { static void id() { } }; template<typename T> std::size_t type_id() { return reinterpret_cast<std::size_t>(&type<T>::id); } class Component { public: Component() { } virtual ~Component() {} }; class Transform final : public Component { public: int x, y; Transform(int x, int y) : x(x), y(y) {} }; class Renderable final : public Component { public: std::string data; Renderable(std::string d) : data(d) {} }; class Other final : public Component { public: }; typedef std::shared_ptr<Transform> sp_Transform; typedef std::shared_ptr<Renderable> sp_Renderable; class RenderSystem { private: friend class SceneManager; typedef std::vector< std::weak_ptr<Renderable> > t_Renderable; t_Renderable vRend; void AddRenderable(sp_Renderable r) { vRend.push_back(r); } public: void Update() { for (std::size_t i = 0; i < vRend.size(); ++i) { if (auto sp = vRend[i].lock()) // not expired { std::cout << sp->data << std::endl; } else // component removed, swap - pop_back { std::swap(vRend[i--], vRend.back()); vRend.pop_back(); } } } }; class PhysicsSystem { private: friend class SceneManager; typedef std::vector< std::weak_ptr<Transform> > t_Transform; t_Transform vTransform; void AddTransform(sp_Transform t) { vTransform.push_back(t); } public: void Update() { for (std::size_t i = 0; i < vTransform.size(); ++i) { if (auto sp = vTransform[i].lock()) // not expired { sp->x += 3; sp->y += 7; std::cout << sp->x << ", " << sp->y << std::endl; } else // component removed, swap - pop_back { std::swap(vTransform[i--], vTransform.back()); vTransform.pop_back(); } } } }; class GameObject; typedef std::shared_ptr<GameObject> sp_GameObject; class SceneManager { private: std::unique_ptr<PhysicsSystem> psys; std::unique_ptr<RenderSystem> rsys; public: SceneManager() { psys = std::make_unique<PhysicsSystem>(); rsys = std::make_unique<RenderSystem>(); } sp_GameObject CreateObject() { return std::make_shared<GameObject>(this); } template < typename T > void EnqueueComponent(T t) { } // do nothing void Update() { psys->Update(); rsys->Update(); } }; template <> // specialization for Renderable void SceneManager::EnqueueComponent<sp_Renderable>(sp_Renderable t) { rsys->AddRenderable(t); } template <> // specialization for Transform void SceneManager::EnqueueComponent<sp_Transform>(sp_Transform t) { psys->AddTransform(t); } class GameObject { SceneManager* smgr; std::unordered_map<std::size_t, std::shared_ptr<Component>> components; public: GameObject(SceneManager* s) : smgr(s) {} template < typename C, typename... Args > void AddComponent(Args&&... args) { assert("Already have that component!" && components.count(type_id<C>()) == 0); auto c = std::make_shared<C>(std::forward<Args>(args)...); smgr->EnqueueComponent(c); components.insert({ type_id<C>(), c }); } template < typename C > bool HaveComponent() { return components.count(type_id<C>()) != 0; } template <typename C> std::shared_ptr<C> GetComponent() { assert("No such component!" && HaveComponent<C>()); return std::static_pointer_cast<C>(components[type_id<C>()]); } template <typename C> void RemoveComponent() { components.erase(type_id<C>()); } }; int main() { auto s = std::make_unique<SceneManager>(); auto o1 = s->CreateObject(); o1->AddComponent<Transform>(5, 9); o1->AddComponent<Renderable>("Hello obj1!"); auto o2 = s->CreateObject(); o2->AddComponent<Transform>(11, 22); o2->AddComponent<Renderable>("Hello obj2!"); //auto z = o1->GetComponent<Other>(); // error //auto r = o1->GetComponent<Renderable>(); //auto p = o1->GetComponent<Transform>(); s->Update(); o1->RemoveComponent<Transform>(); s->Update(); return 0; } Znaci imam GameObject koji sadrzi listu unikatnih komponenti koristeci unordered_map, gde je key size_t tip (koristeci type_id funkciju iz navedenog treda) a value je shared_ptr Component (bazna klasa svih komponenti), dodavanje/pristup komponenti bi trebalo biti veoma efikasno. Onda imam "sisteme" koji operisu na specificnim komponentama (RenderSystem, PhysicsSystem...) i SceneManager koji azurira ove sisteme i kreira objekte. final kljucnu rec na izvedene klase sam koristio jer sam cuo da moze biti hint kompajleru da uradi devirtualizaciju a i ne ocekujem neko daljnje nasledjivanje. Ne svidja mi se sto sam morao da embedujem pokazivac na SceneManager u sam GameObject jer moram nekako da ubacim komponente i u sisteme, a ne bih da menjam "sintaksu" koriscenja ovog sistema. Da li vidite neki fejl u kodu, da li ste radili nesto slicno, imate neke predloge? Posto sam radio u VS 2013 okruzenju, a zna se da VS nije uvek u saglasnosti sa c++ standardom, moze da se desiti da kod ne bude radio sa drugim kompajlerima. |
10.7.2014, 23:57 | #2 |
Kekule Mekule
|
Re: [C++] Unity sistem komponenti
Zezah se ja sa tim prosle godine, kod sam brisnuo sa harda ali je ostala neka pocetna verzija na SFML forumu, mozes proci kroz taj thread: http://en.sfml-dev.org/forums/index....75815#msg75815
|
Sledeći korisnik se zahvaljuje korisniku Andross na korisnoj poruci: | ||
Belphegor (11.7.2014) |
11.7.2014, 1:44 | #3 |
V.I.P. Programiranje
|
Re: [C++] Unity sistem komponenti
Ovde je dat interesantan (doduše kratak) pregled kako se sistem baziran na komponentama može integrisati u data-oriented dizajn endžina (za više o data-oriented dizajnu, evo ga jedan moj tekst koji bi (kao) trebalo da bude dobar uvod). Evo i nešto malo duže na sličnu temu. Nisam siguran koliko je relevantno, samo dodajem ono što sam ja čitao o tome
Što se kôda tiče, @Belphegor, sintaksa mi deluje baš fino, posebno sa AddComponent<T> i bratijom. I sviđa mi se korišćenje C++11, baš kôd postaje primetno lepši i lakše se rade neke stvari. Ne znam jesi li razmišljao da implementiraš neki event dispatch sistem između ovih različitih komponenti, ali bilo bi zanimljivo da mogu da šalju poruke međusobno. Npr. entitet koji predstavlja NPC može da ima AI komponentu koja može da odgovara na eventove tipa "collision" iz physics komponente. U svakom slučaju, ako hoćeš, možeš da pogledaš neko rešenje za delegate (kao u C#-u), mogu da budu brži od virtuelnih funkcija, boilerplate je mnogo manji i kôd na kraju ispadne lepši (bar meni). Ovde sam nešto piskarao o tome (ignorisati nesmešne opaske i klinačke komentare), evo ga i kôd za te Delegate klase. Nije gotov, i verujem da s nekom metaprogramming mađijom može da se sredi da sintaksa bude još lepša, ali kapiram da je fina referenca. delegate.h Kod:
template<typename R, typename... Args> class Delegate<R(Args...)> { public: Delegate() : mObject(nullptr), mStub(nullptr) { } Delegate(const Delegate& Other) = default; Delegate& operator=(const Delegate& Other) = default; R operator()(Args... Arguments) { return (*mStub)(mObject, Arguments...); } template<class T, R (T::*Method)(Args...)> static Delegate fromMethod(T* Object) { Delegate d; d.mObject = Object; d.mStub = &methodStub<T, Method>; return d; } protected: typedef R (*Stub)(void* Object, Args...); void* mObject; Stub mStub; template<class T, R (T::*Method)(Args...)> static R methodStub(void* Object, Args... Arguments) { return (static_cast<T*>(Object)->*Method)(Arguments...); } }; Kod:
template<typename T> std::vector<T> Vector; template<typename K, typename V, blablabla> std::map<K, V, blablabla> Map; template<typename... Args> class EventSource { public: typedef uint Handle; Handle addHandler(const Delegate<void(Args...)>& Handler) { Handle nh = mNextHandle++; mHandlers.push_back(Handler); mHandleMap[nh] = mHandlers.size() - 1; return nh; } void removeHandler(Handle H) { typename HandleMap::iterator it = mHandleMap.find(H); if (it == mHandleMap.end()) throw exceptions::NoSuchEventHandler(H); typename HandlerList::size_type i = it->second, last = mHandlers.size() - 1; std::swap(mHandlers.at(i), mHandlers.back()); mHandlers.pop_back(); for (auto& p : mHandleMap) if (p.second == last) { p.second = i; break; } } void clearHandlers() { mHandlers.clear(); } void fire(Args... Arguments) { for (auto& d : mHandlers) d(Arguments...); } protected: typedef Vector<Delegate<void (Args...)>> HandlerList; typedef Map<Handle, typename HandlerList::size_type> HandleMap; HandlerList mHandlers; HandleMap mHandleMap; Handle mNextHandle; }; |
Sledeći korisnik se zahvaljuje korisniku Geomaster na korisnoj poruci: | ||
Belphegor (11.7.2014) |
11.7.2014, 10:08 | #4 |
V.I.P. Programiranje
Član od: 29.8.2007.
Lokacija: Valjevo
Poruke: 1.349
Zahvalnice: 983
Zahvaljeno 371 puta na 280 poruka
|
Re: [C++] Unity sistem komponenti
Hvala za linkove.
Imam jedno pitanje u vezi "branch mispredictions" koje si pomenu u blogu, vezano za ovaj kod: Kod:
void MyEngine::queueRenderables() { for (auto it = mRenderables.begin(); it != mRenderables.end(); ++it) { if ((*it)->isVisible()) { queueRenderable(*it); } } Mogao bih uraditi nesto ovako kao preproces: Kod:
auto endVisible = std::partition(std::begin(vRenderable), std::end(vRenderable), [](R r){ return r.visible; }); for_each(beg, endVisible, [](R r){queueRenderable(r);}); EDIT: nvm. Mislim da ovo objasnjava. Poslednja ispravka: Belphegor (11.7.2014 u 16:34) |
11.7.2014, 16:44 | #5 |
V.I.P. Programiranje
|
Re: [C++] Unity sistem komponenti
std::рartition bi učinio više štete nego koristi, cenim. Moja poenta je bila da možeš da držiš listu trenutno vidljivih entiteta, i samo kroz nju prođeš. Kada treba da sakriješ entitet, izbaciš ga iz te liste, kad treba da se ponovo pojavi, opet ga ubaciš, dakle sem glavnog skupa entiteta na sceni držiš i još jednu strukturu koja ti govori koji su trenutno vidljivi. Tako imaš implicitni boolean (da li je u listi vidljivih ili ne?) umesto eksplicitan (Entity::mVisible). Ako skroz izbaciš taj visibility boolean, onda je isVisible sporiji od O(1), ali koliko često uhvatiš sebe da ti stvarno treba da li je neki entitet vidljiv ili ne? Sve ovo je poenta existence-based procesinga, objašnjeno, npr. ovde.
|
Sledeći korisnik se zahvaljuje korisniku Geomaster na korisnoj poruci: | ||
Belphegor (11.7.2014) |
11.7.2014, 18:24 | #6 | |
V.I.P. Programiranje
Član od: 29.8.2007.
Lokacija: Valjevo
Poruke: 1.349
Zahvalnice: 983
Zahvaljeno 371 puta na 280 poruka
|
Re: [C++] Unity sistem komponenti
Citat:
No otom-potom, nista bez profilisanja, probacu vise stvari pa cu proceniti sta mi odgovara. |
|
11.7.2014, 18:34 | #7 |
V.I.P. Programiranje
|
Re: [C++] Unity sistem komponenti
Da, ali sakrivanje entiteta se dešava mnogo, mnogo ređe nego prolaženje kroz sve vidljive entitete (što se verovatno dešava više puta u toku jednog frejma). Nije poenta da se izbaci svako grananje, poenta je da se izbace ona grananja koja se dešavaju dovoljno često i dovoljno su teška za predikciju da bi njihovo izbacivanje uopšte napravilo neku razliku. Zato nije efektivno izbaciti grananja samo u toj queueRenderables metodi (npr.) nego je potrebno naći sve takve delove i refaktorisati ih, i onda će generalno sve početi da radi brže. Pogledaj review fajla OgreNode.cpp koji je uradio Majk Ekton, tu se baš vide neki dobri kandidati za refaktorisanje, imaš link na kraju onog mog Tuts+ posta.
|
Sledeći korisnik se zahvaljuje korisniku Geomaster na korisnoj poruci: | ||
Belphegor (12.7.2014) |
16.7.2014, 20:45 | #8 | |
V.I.P. Programiranje
Član od: 29.8.2007.
Lokacija: Valjevo
Poruke: 1.349
Zahvalnice: 983
Zahvaljeno 371 puta na 280 poruka
|
Re: [C++] Unity sistem komponenti
Nisam stigao ranije da pogledam tvoj kod (implementacija bazirana na kodu Sergey Ryazanov-a ?).
Ovo mi nije jasno sta si radio? Citat:
Iskasapio sam malo ovu Delegate klasu, gde drzim direktno pokazivac na metodu umesto fukcije, a i ne vidim zasto bi koristio void* ako imam tip objekta. Kod:
template<typename T, typename R, typename... Args> class Delegate { private: typedef T* t_pointer; typedef R(T::*t_method)(Args...); t_pointer m_object; t_method m_pmethod; public: Delegate(t_pointer obj, t_method m) : m_object(obj), m_pmethod(m) {} Delegate(const Delegate& Other) = default; // delete Delegate& operator=(const Delegate& Other) = delete; R operator()(Args&&... args) { return (m_object->*m_pmethod)(std::forward<Args>(args)...); } }; ... SomeClass s; auto d = Delegate<SomeClass, void, int, double>(&s, &SomeClass::Fun); |
|
18.7.2014, 6:43 | #9 |
V.I.P. Programiranje
|
Re: [C++] Unity sistem komponenti
Ne razumem ovo što si uradio. Možda je do toga što sam dočekao 7 sati budan, doduše, ali bi svejedno valjalo da mi razjasniš
Dodao si template parametar za tip klase kojoj pripada metod u Delegate klasu da bi mogao da držiš pointer-to-method, ali sada su ti delegati, koliko sam ja shvatio, neupotrebljivi za ono za šta sam ih ja koristio. Da bi ih koristio za bilo kakav publish-subscribe pattern, moraju svi delegati s istim argumentima da imaju isti tip. Moju EventSource klasu sada ne možeš da implementiraš, jer ne možeš da napraviš vektor delegata, jer su svi različitog tipa. Poenta mojih delegata i event sistema je da možeš da uradiš ovo: Kod:
GameClass::nešto(): PhysicsObject *po = ...; po->getCollisionEventSource()->addHandler(Delegate<void(parametri za event, nije ni bitno)>::fromMethod <GameClass, &GameClass::handleCollision>(this)); Nadam se da si razumeo šta sam hteo da kažem. Dakle poenta je da delegati budu templatizirani samo po potpisu funkcije koju implementiraju, da bi mogli da se svi tretiraju isto i budu pozvani "na gomili" iz event sors klase, pomoću fire(). A da bi se to uradilo, mora postojati neki kôd koji zna tačan metod i klasu koju da pozove, a taj kôd mora da bude dostupan iz klase koja ne zna ništa o tome. Dakle zato koristimo ovaj method stub (originalno, kao što rekoh, Sergej Razanov predložio) i onda pozivamo njega. To je nažalost jedan dodatni poziv ka funkciji, ali možemo da se potrudimo da to rešimo tako što se nadamo da će kompajler biti dovoljno pametan da vidi da je stub samo poziv metode objekta i ništa drugo, i onda za methodStub<T, Method>() emituje direktan poziv metode te klase ili vtable lookup + poziv metode. A konstruktor kopije se poziva recimo u EventSource::addHandler (nem pojma da l' ima još negde, mrzi me da gledam sad). |
Sledeći korisnik se zahvaljuje korisniku Geomaster na korisnoj poruci: | ||
Belphegor (18.7.2014) |
18.7.2014, 9:24 | #10 | |
V.I.P. Programiranje
Član od: 29.8.2007.
Lokacija: Valjevo
Poruke: 1.349
Zahvalnice: 983
Zahvaljeno 371 puta na 280 poruka
|
Re: [C++] Unity sistem komponenti
Citat:
Kod:
template ... class Delegate <R(Args...)> Predpostavljam da je cilj da bi imao ovu sintaksu? Kod:
Foo<void(int, double)> Kod:
Foo<void, int, double> Da, da, nisam obratio paznju na EventSource klasu. EDIT: nvm. radi Kod:
template <typename T, typename R> class Delegate {}; template<typename T, typename R, typename... Args> class Delegate <T, R(Args...)> Poslednja ispravka: Belphegor (18.7.2014 u 10:03) |
|
20.7.2014, 11:51 | #11 |
Veteran
Član od: 3.5.2008.
Lokacija: Beograd
Poruke: 760
Zahvalnice: 81
Zahvaljeno 213 puta na 144 poruka
|
Re: [C++] Unity sistem komponenti
Ту синтаксу сам приметио пре неколико месеци и тражио по интернету. То је синтакса за функцијске типове. Када се напише
Kod:
typedef float Potpis1(const char*, int); Kod:
float uradiNesto(Potpis1 func) { return func("blabla", 42); } Kod:
struct A { Potpis1 metoda; }; float A::metoda(const char* str, int num) { //... } int main() { A a; a.metoda("bla bla", 42); } Kod:
template<typename T> struct B { T clan; }; int main() { B<void()> b; b.clan(); }; |
Sledeći korisnik se zahvaljuje korisniku ivan90BG na korisnoj poruci: | ||
Belphegor (20.7.2014) |
21.7.2014, 9:50 | #12 |
V.I.P. Programiranje
|
Re: [C++] Unity sistem komponenti
Da, možda valja napomenuti da sam ja sav ovaj kôd koji sam postavio ovde morao da kompajliram GCC-om, jer VS2013 (u to vreme, verovatno je sad bolja situacija) nije razumeo dobar deo C++11, čak i sa onim apdejtom za kompajler. Doduše ionako sam više sreće imao sa GCC-om i njegovim toolchainom tako da mi nije žao. ivan90BG je dobro objasnio funkcijske tipove kao template parametre, nemam šta da dodam. Znam da se pod GCC-om kompajlovalo bez problema za unit test kôd i prolazilo sve testove. Nemoj mnogo da veruješ VS-u, mene je znao opasno da opeče. I i dalje mi nije jasno zašto imaš tri mesto dva template parametra.
|
21.7.2014, 11:37 | #13 |
V.I.P. Programiranje
Član od: 29.8.2007.
Lokacija: Valjevo
Poruke: 1.349
Zahvalnice: 983
Zahvaljeno 371 puta na 280 poruka
|
Re: [C++] Unity sistem komponenti
Ostalo je tako kao test dok nisam provalio kako da prosledim kao "funkcijski" tip.
Ako se ne varam, ne svidja mi se to sto EventSource moze da drzi delgate samo sa istim tipovima argumenata, a i sa istim parametrima se i pozivaju. Videcu jos, polako dok ne prostudiram i napravim nesto sto odgovara mojim potrebama. |
22.7.2014, 0:15 | #14 |
Veteran
Član od: 3.5.2008.
Lokacija: Beograd
Poruke: 760
Zahvalnice: 81
Zahvaljeno 213 puta na 144 poruka
|
Re: [C++] Unity sistem komponenti
Istražujući davno mogućnost za implementaciju delegata u C++-u (onakvih kakvi su u .NET-u) naišao sam na ovo: http://www.codeproject.com/Articles/...stest-Possible, a evo maločas i na ovo: http://www.codeproject.com/Articles/...st-C-Delegates.
|
22.7.2014, 0:43 | #15 |
V.I.P. Programiranje
Član od: 29.8.2007.
Lokacija: Valjevo
Poruke: 1.349
Zahvalnice: 983
Zahvaljeno 371 puta na 280 poruka
|
Re: [C++] Unity sistem komponenti
^ Zapravo Geomasterov kod se bazira na kodu sa drugog linka.
|
30.7.2014, 21:46 | #16 | |
V.I.P. Programiranje
|
Re: [C++] Unity sistem komponenti
Citat:
Ako uspeš da napraviš neke delegate koji mogu da se drže u homogenom kontejneru a da budu sličnih performansi kao ovi Sergejevi, postavi ovde, interesuje me, pošto meni ne pada nikakva ideja na pamet kako u tom slučaju izbeći dinamičku alokaciju i biti brži od recimo std::bind. |
|
4.8.2014, 14:41 | #17 |
V.I.P. GNU/Linux
Član od: 1.11.2005.
Poruke: 11.166
Zahvalnice: 2.086
Zahvaljeno 4.923 puta na 2.859 poruka
|
Re: [C++] Unity sistem komponenti
Thread hijack
Ako ste zainteresovani za eventualnu saradnju sa jednom domaćom firmom, a saradnja bi se svodila na "portovanje" njihovog postojećeg endžina na Unity, kontaktirajte me na PM. |
Bookmarks sajtovi |
|
|
Slične teme | ||||
tema | temu započeo | forum | Odgovora | Poslednja poruka |
Avioni | Dr Zoidberg | Brbljaonica | 3931 | 17.4.2022 21:11 |
Laptop HP dv3600 nece da podigne sistem | igor7 | Prenosni računari | 10 | 2.4.2014 19:37 |
Ne mogu da biram koji sistem | BMWM5 | Kvarovi | 2 | 16.9.2010 19:40 |
Upomoc, ljudi, sistem mi je nacisto odlepio! | Fibonacci | Kvarovi | 7 | 16.9.2010 12:04 |
Opera mi ubi sistem | Monk Psycho | Operativni sistemi | 12 | 12.3.2006 5:09 |