Components Aggregation Scene Management

Started by
15 comments, last by hymerman 18 years, 1 month ago
http://web.archive.org/web/20031010130208/http://www.incagold.com/pipermail/sweng-gamedev/2001-February/002883.html
has some neat stuff on this topic, still reading but no real info on how to hierarchy and how the object gets its components.

[Edited by - Valor Knight on March 11, 2006 1:20:47 AM]
There is no life without honor
Advertisement
In my system I will be creating prebuilt tree structures, and adding the root of these trees wherever they need to go in my graph..

So say, I have a torch hierarchy, already prebuilt out of data from my editor..

It has a transformation node, a geometry node, a particle node, a light node, and an ambient sound node...

I can now simply attach the root node (the transformation node) to wherever I need it to go in the tree, and the rest will follow..

Now, a torch is just a piece of data, though a fairly complex piece of data, I can give "blob" objects this piece of data to work with..

Simply a class, with a renderable object (torch).. this defines what the torch is physically speaking, (well at least in terms of rendering.. And then I can leave the class that contains this information, the torch class, have its own scripted logic.. For logic issues..

The complex objects (scene graph mini trees) I create with my editor will be pieces of data for logical objects to manipulate.

----------------------------------------------------------Rating me down will only make me stronger.----------------------------------------------------------
i work on scene graph based on something like what is discussed here.
The nodes are a plain implementation of the composite pattern with additional methods to add/remove data objects/properties/components (i call them "aspects") and do queries on them very similar to what _swz_ proposed with queryInterface.

template <typename Component>
Component * getAspect();

and
template <typename Component>
Component * getAspect( const std::string & name )

the type is identified by plain C++ RTTI (simple typeid compares, should be reasonable fast, just a pointer comparison )

Components get a pointer to they holder/owner nodes and can instantiate Components to that node as they please. Communication between Components is message or interface based (depends on the kind of communication). My first plan was to "wire" the components from outside using signals/slots but i found no good solution how to provide scripting support for sig/slots due to their C++ template based nature. This wiring was inspired by message based actor systems used for concurrent tasks (e.g. in telecom systems)

Additionally, some Components depend on other Components (e.g. GeoChunkComponent depends on the MeshsComponent, if the MeshComponent is gone, the gchunks should be removed as well). So each Component maintains a list with dependencies.

Now, Components are mostly data (mostly, since they have an update method for things like an AI Component). The scenegraph just establishes a logical relationship. The real work is done in other graphs/managers/lists. They walk through the graph using the visitor pattern every frame (or less often) and query the nodes for components they are interested in. E.g. the renderer asks for geometric chunks and queues it into the shader/render qeueue system.

My intention is reach a good level of decoupling. I can replace subsystems like the renderer or introduce new ones. The "user" just adds nodes with aspects to the logic tree. the rest happens behind the scenes and is interchangeable.

Components are created with a template factory.

some problems:
- hard to write bindings to scripting languages, template-heavy code doesnt make it easy
- alot of sg traversals might be neccessary
(there are still alot more issues to be resolved...)

regards,
stephan

[Edited by - stephanh on March 12, 2006 8:15:12 AM]
I finally found my March issue to read this artical. It brought back bad memories and sparked imagination into improving functionality implementation.
First the bad: I once worked with about 40 developers on a project that was an aggregated properties based system built on a set out about 235 specialized objects types. While the project succeded in programmer expectation it failed to meet it's operational objective. The code was a mass of blobs with multiple dynamic aggregate property collections. Functionality was implemented right into the Inheritance. Execution would take over an hour to achieve it's task where the program(s) it replaced took about 5 mins. To be fair, about half of the time wasted was spent in poorly written code for what was the "physics" engine of the app. A major amount of time was spent in the data set querying the properties.
Could the same thing happen to a lot of projects should functionality implementation now be aggregated? The author does the right thing of metioning a migration path which allows you to move over with encapsulated sections to be able to work out problems avoid the doom of a failed project. IMO, One of the aspects realized by the project is that code got refactored by necessity during transition in order accept the new methodology. Cross "pollenating" pointers to brige to functionality sounds like an area for improvement. Not to bash the author but I've spent decades trying to convince progammers not to use GOTOs (another four letter word) in new code. The "physics" engine I mentioned earlier was infested with a massive amount of pointers. The point is, map out what objects are doing to avoid having to pass addresses all over the place. If one point gets mangled and passed along then trying to debug may be a nightmare when the knight passes the sword to the alien BSODs your workstation.

Now the good: Building componants bases on lightweight objects with some aggregation of functionaliy can be the winning approach. What I'm proposing is breaking up the objects into smaller functional pieces. The tree would grow a little but the lightweight objects are specialized to a particular function(s).

Just for thought and discussion:
Take the basic bowmans arrow, I'll trace it's cycle. The componant manager produces the arrow in the fletchers hut(Class Factory). The arrow is the some of a few parts (Feather, shaft and blade). These could be just part of the CArrow object or aggregated parts depending on what you want from it. The arrow gets basic fuctionality, render, position etc. Launch and flight functionality I would assume are added then the archer uses it. When it lands flight is taken away and damage is added. The archer could also add fire to the tip (another function) before letting the arrow fly. Using this same arrow object let's say we send a couple of surfs out to dig man traps (pits). They collect spent arrows from the battlefield and shove them into the bottom of the pit business side up. Or componet manager now has the duty of adding the impale function to these arrows. Owwch! While this is wild bunny chase, it covers a lifecycle of a simple object. How we implement this into the component model is the question. I will be looking into this and more for how not to create overhead and extra pointers.
I started a thread after I read that article asking pretty much the same thing. I wish he would have touched on that a little deeper. I got hooked on the idea but then didn't fully understand what all went into it.

http://www.gamedev.net/community/forums/topic.asp?topic_id=378753
Helix,

Thanks for the link. Great Read!. Although, it seems to end on the same note here, nothing too specific on how its done exactly. I have some ideas. Maybe we can spark some more answers here.

Everyone, please let me know if this is a good approach from what I'm thinking:

class Container{public:  component* GetComponent(std::string& name);  void AddComponent(component* _component);private:  std::map<std::string,component*> m_ComponentMap;}void Container::AddComponent(component* _component){   _component->AddContainer(this);   m_ComponentMap.Add(_component->m_Name,_component);}class Player : public Container{public:private:  }Player player;player.AddComponent(DXRenderer);player.AddComponent(Transform);player.AddComponent(Collision);player.AddComponent(Geometry);Geometry* geom = player.GetComponent("Geometry");geom->LoadModel("C:\\test.x");update(){     DXRenderer* renderer = player.GetComponent("DXRenderer");     renderer->Render();}//in DXRendererDXRenderer::Render(){   SetTransform(localEntity->GetComponent("Transform"));   DrawGeometry(localEntity->GetComponent("Geometry"));}

(Yes, some of this code is more psuedo code)

Is this a simple example of how components should work in a game engine? This way, "class Player : public Container" is really nothing more than a container of components and doesn't do anything but provide the components for what your doing?

Is this correct? Am I on the track to understanding this or am I completely wrong? Any examples would be great if im wrong here.

Thanks!!
Jeff.
The question shouldn't be "Is this correct?", but "Does this make sense?".

My answer would have to be... "sort of".

A thing for you to think about: If the DXRenderer component relies on its containing entity having Transform and Geometry components too, would it not make sense to integrate these things into one component? And really, if we're talking about components, shouldn't the entities have 'Renderable' components rather than 'Renderer' components? Think carefully about what exactly it is the component system is trying to achieve.

This topic is closed to new replies.

Advertisement