Friday, March 16, 2018

architecture - How are physics or graphics components typically built in a component-oriented system?


I have spent the last 48 hours reading up on Object Component systems, and feel I am ready enough to start implementing it. I got the base Object and Component classes created, but now that I need to start creating the actual components I am a bit confused. When I think of them in terms of HealthComponent or something that would basically just be a property, it makes perfect sense. When it is something more general as a Physics/Graphics component, I get a bit confused.


My Object class looks like this so far (If you notice any changes I should make please let me know, still new to this)...


typedef unsigned int ID;


class GameObject
{
public:

GameObject(ID id, Ogre::String name = "");
~GameObject();

ID &getID();
Ogre::String &getName();


virtual void update() = 0;

// Component Functions
void addComponent(Component *component);
void removeComponent(Ogre::String familyName);

template
T* getComponent(Ogre::String familyName)
{

return dynamic_cast(m_components[familyName]);
}

protected:

// Properties
ID m_ID;
Ogre::String m_Name;
float m_flVelocity;
Ogre::Vector3 m_vecPosition;


// Components
std::map m_components;
std::map::iterator m_componentItr;
};

Now the problem I am running into is what would the general population put into Components such as Physics/Graphics? For Ogre (my rendering engine) the visible Objects will consist of multiple Ogre::SceneNode (possibly multiple) to attach it to the scene, Ogre::Entity (possibly multiple) to show the visible meshes, and so on. Would it be best to just add multiple GraphicComponent's to the Object and let each GraphicComponent handle one SceneNode/Entity or is the idea to have one of each Component needed?


For Physics I am even more confused. I suppose maybe creating a RigidBody and keeping track of mass/interia/etc. would make sense. But I am having trouble thinking of how to actually putting specifics into a Component.


Once I get a couple of these "Required" components done, I think it will make a lot more sense. As of right now though I am still a bit stumped.



Answer




First of all, you when build component-based systems, you don't have to take the approach of turning everything into a component. In fact, you generally shouldn't -- it's something of a neophyte mistake. In my experience the best way to tie together rendering and physics systems in a component based architecture is to make those components little more than dumb containers for the real rendering or physics primitive object.


This has the added advantage of keeping the rendering and physics systems decoupled from the component system.


For physics systems, for example, the physics simulator generally keeps a big list of rigid body objects that it manipulates every time it ticks. Your component system does not mess with that -- your physics component simply stores a reference to the rigid body that represents the physical aspects of the object the component belongs to, and translates any messages from the component system into appropriate manipulations of the rigid body.


Likewise with rendering -- your renderer will have something representing the instance data to be rendered, and your visual component simply holds on to a reference to that.


It's relatively straightforward -- for whatever reason it seems to confuse people, but that's often because they are thinking about it too hard. There's not a lot of magic there. And since it's most important to get things done rather than agonize over the perfect design, you should strongly consider an approach where at least some of the components have defined interfaces and are direct members of the game object, as momboco has suggested. It's just as valid an approach and it tends to be easier to grok.


Other Comments


This is unrelated to your direct questions, but these are things I noticed when looking at your code:


Why are you mixing Ogre::String and std::string in your game object, which is ostensibly a non-graphics object and thus should not have a dependency on Ogre? I would suggest removing all direct references to Ogre except in the rendering component itself, where you should do your transformation.


update() is a pure virtual, which implies that one must subclass GameObject, which is actually kind of exactly the opposite of the direction you want to go in with a component architecture. The main point of using components is to prefer aggregation of functionality rather than inheritance.


You could benefit from some use of const (particular where you are returning by reference). Also I'm not sure that you really want velocity to be a scalar (or if it, and position, should be present in the game object in the kind of overly generic component methodology you seem to be going for).



Your approach of a using a big map of components and an update() call in the game object is quite sub-optimal (and a common pitfall for those first building these sorts of systems). It makes for very poor cache coherency during the update and doesn't allow you to take advantage of concurrency and the trend towards SIMD-style process of large batches of data or behavior at once. It's often better to use a design where the game object doesn't update its components, but rather the subsystem responsible for the component itself updates them all at once. This might be worth a read.


No comments:

Post a Comment

Simple past, Present perfect Past perfect

Can you tell me which form of the following sentences is the correct one please? Imagine two friends discussing the gym... I was in a good s...