Forum Sveta kompjutera

Nazad   Forum Sveta kompjutera > Test Run > Programiranje
Uputstvo Članstvo Kalendar Današnje poruke Pretraži

Programiranje Programski jezici, tehnike, alatke...

Odgovor
 
Alatke vezane za temu Vrste prikaza
Stara 10.7.2014, 21:15   #1
Belphegor
V.I.P. Programiranje
 
Član od: 29.8.2007.
Lokacija: Valjevo
Poruke: 1.349
Zahvalnice: 983
Zahvaljeno 371 puta na 280 poruka
Određen forumom [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;
}
Skoro naletim na ovaj intersantan tred (zadnji kod tag), gde se unikatan id po tipu (kompnenti) moze dobiti na osnovu adrese funkcije oslanjajuci se na "one definition rule".
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.
Belphegor je offline   Odgovor sa citatom ove poruke
Stara 10.7.2014, 23:57   #2
Andross
Kekule Mekule
 
Avatar korisnika Andross
 
Član od: 8.12.2005.
Lokacija: Beograd
Poruke: 4.134
Zahvalnice: 649
Zahvaljeno 1.354 puta na 693 poruka
Slanje poruke preko Skypea korisniku Andross
Određen forumom 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
Andross je offline   Odgovor sa citatom ove poruke
Sledeći korisnik se zahvaljuje korisniku Andross na korisnoj poruci:
Belphegor (11.7.2014)
Stara 11.7.2014, 1:44   #3
Geomaster
V.I.P. Programiranje
 
Član od: 28.6.2007.
Lokacija: Beograd
Poruke: 2.342
Zahvalnice: 2.836
Zahvaljeno 1.047 puta na 507 poruka
Slanje poruke preko MSN-a korisniku Geomaster Slanje poruke preko Skypea korisniku Geomaster
Određen forumom 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...);
  }
};
eventsource.h
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;
};
Dakle fora je da jednostavno emiteri ponude EventSource klasu i ti njoj samo zakačiš svoj delegat kad hoćeš da se subscribe-uješ na evente (i posle ga otkačiš kad ti ne treba više, razume se). Onda emiteri pozivaju fire(...) i svi registrovani delegati bivaju pozvani. Zato što nema nikakav setup overhead u kôdu, možeš da nudiš EventSource za svaki tip eventa, ili da napraviš neku klasu koja sadrži grupu takvih emitera, pa da nju ponudiš iz komponente... Ne znam kako bi se to uklopilo u tvoj komponentski sistem, ali eto, neka ga tu, nek bleji. Izvinjavam se za off, ovo su samo neke ideje koje sam ja nekad imao i koje bih voleo da zažive u nekom obliku, pa ih poturam ljudima
Geomaster je offline   Odgovor sa citatom ove poruke
Sledeći korisnik se zahvaljuje korisniku Geomaster na korisnoj poruci:
Belphegor (11.7.2014)
Stara 11.7.2014, 10:08   #4
Belphegor
V.I.P. Programiranje
 
Član od: 29.8.2007.
Lokacija: Valjevo
Poruke: 1.349
Zahvalnice: 983
Zahvaljeno 371 puta na 280 poruka
Određen forumom 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);
  }
}
Citao sam o tome i ranije ali ne vidim kako zaobici ovaj problem?
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);});
Ali da li se uopste isplati? Jer bih morao onda da sortiram ili da pravim novu listu.

EDIT: nvm. Mislim da ovo objasnjava.

Poslednja ispravka: Belphegor (11.7.2014 u 16:34)
Belphegor je offline   Odgovor sa citatom ove poruke
Stara 11.7.2014, 16:44   #5
Geomaster
V.I.P. Programiranje
 
Član od: 28.6.2007.
Lokacija: Beograd
Poruke: 2.342
Zahvalnice: 2.836
Zahvaljeno 1.047 puta na 507 poruka
Slanje poruke preko MSN-a korisniku Geomaster Slanje poruke preko Skypea korisniku Geomaster
Određen forumom 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.
Geomaster je offline   Odgovor sa citatom ove poruke
Sledeći korisnik se zahvaljuje korisniku Geomaster na korisnoj poruci:
Belphegor (11.7.2014)
Stara 11.7.2014, 18:24   #6
Belphegor
V.I.P. Programiranje
 
Član od: 29.8.2007.
Lokacija: Valjevo
Poruke: 1.349
Zahvalnice: 983
Zahvaljeno 371 puta na 280 poruka
Određen forumom Re: [C++] Unity sistem komponenti

Citat:
Kada treba da sakriješ entitet, izbaciš ga iz te liste...
Gde opet imam brancovanje, jer moram testirati da bi znao da li treba da se izbaci iz liste.
No otom-potom, nista bez profilisanja, probacu vise stvari pa cu proceniti sta mi odgovara.
Belphegor je offline   Odgovor sa citatom ove poruke
Odgovor

Bookmarks sajtovi


Vaš status
Ne možete postavljati teme
Ne možete odgovarati na poruke
Ne možete slati priloge uz poruke
Ne možete prepravljati svoje poruke

BB kod: uključeno
Smajliji: uključeno
[IMG] kod: uključeno
HTML kod: isključeno


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


Sva vremena su po Griniču +2 h. Sada je 6:21.


Powered by vBulletin® verzija 3.8.7
Copyright ©2000–2024, vBulletin Solutions, Inc.
Hosted by Beograd.com