Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I'm taking my first dive into creating a very simple entity framework. The key concepts (classes) are:

  • Entity (has 0+ components, can return components by type)
  • SpriteEntity (everything you need to draw on screen, including lighting info)
  • PhysicsEntity (velocity, acceleration, collision detection)

I started out with physics notions in my sprite component, and then later removed them to a sub-component. The separation of concerns makes sense; a sprite is enough information to draw anything (X, Y, width, height, lighting, etc.) and physics piggybacks (uses the parent sprite to get X/Y/W/H) while adding physics notions of velocity and collisions.

The problem is that I would like collisions to be on an entity level -- meaning "no matter what your representation is (be it sprites, text, or something else), collide against this entity." So I refactored and redirected collision handling from entities to sprite.physics, while mapping and returning the right entity on physics collisions.

The problem is that writing code like this.GetComponent<SpriteComponent>().physics is a violation of abstraction. Which made me think (this is the TLDR): should I keep physics as a separate component from sprites, or a sub-component, or something else? How should I share data and separate concerns?

share|improve this question
    
The Artemis framework is a good one to learn from. TheLinuxLich (a user here) as done work on porting it to C#. Artemis is the type of system I had in mind when I created this answer. Good luck! –  Byte56 Dec 14 '12 at 17:36
    
@Byte56 I prefer to write stuff myself for the sake of learning. Once I understand how things look "my way," it's easy to pick up mature frameworks and see how to do things in a better way. –  ashes999 Dec 14 '12 at 18:34
    
I'm the same, I was only suggesting them so you'd have a resource to learn from, not copy/paste. –  Byte56 Dec 14 '12 at 18:48
add comment

4 Answers

You should keep them separate, yes, a sprite has nothing to do with physics, even if your physics get information like size from the sprite. You could have a CollisionComponent with that information, or maybe a SizeComponent that both sprite and collision can access. I would recommend the CollisionComponent having the size on itself, so you don't actually need a Sprite at all. You will still need to share the position, which brings to the next point: Sharing the data.

Sharing the data is a really difficult question, to which I say it depends. In my code I have systems, they are the ones with logic and they can access more than one component, so a physics system could access the SpriteComponent and get the size (probably with no need for a PhysicsComponent if every object is going to act the same.) In your case, since you didn't mention systems, I'm assuming your components have a pointer to their owner entity, you can simply do something like:

class PhysicsComponent
{
...
    void updatePhysics()
    {
        owner->getComponent<SpriteComponent>().position = vec2(...);
        ...
    }
...
}

Which is fine for a simple game, you can also make a message system, so your components don't actually depend on other components, they can simply receive and react to generic messages.

I think using systems is a much better approach, but there's nothing wrong with a direct pointer or messages, it's a matter of what makes more sense to you.

share|improve this answer
    
FYI, I'm using XNA. A messaging system seems overkill for this scale of a game. Thanks for your input. –  ashes999 Dec 14 '12 at 15:16
    
@ashes999 if it's a simple game, then don't worry about the code so much, unless you want to support/change/add stuff/etc in the future, there's no reason to overthink it. –  Luke B. Dec 14 '12 at 16:24
    
+1 agreed. The physics shape does not necessarily need to be the same shape as the sprite shape. It's a good idea to keep them separate. –  Byte56 Dec 14 '12 at 17:32
    
I disagree. In my case, it makes sense for them to be tied together -- but in general, it's true that they could be totally separate. Thanks guys. –  ashes999 Dec 14 '12 at 18:34
add comment

I would say that separating out the physics from the sprite is the best way to go. It would otherwise violate the single responsibility principle of the class.

Is there any reason that your solid implementations of these concepts cannot inherit from both of them ? Say for example, you had a Ball entity. It would be both a ISpriteEntity and a IPhysicsEntity. The Ball could then be acted upon by both a physics engine, and a rendering engine, for example PhysicsEngine.Update(IPhysicsEntity) and RenderingEngine.Draw(ISpriteEntity). This would separate the concerns nicely.

It would also allow your game to 'work' without having a rendering device, say for example if you were running a dedicated server (where there is no need to render anything), the Ball would still have its position and velocity updated.

The Ball itself would contain the information about its size (height, width etc) rather than relying on the sprite to determine its size. The sprite is just a visual representation of the data behind it, it should not be the data itself. You wouldn't base an objects behavior on the sound it creates, so why do so for the vision it creates ?

You want text to collide as well ? Well your text objects would also implement IPhysicsEntity and contain information about its size and position. The physics engine would update all IPhysicsEntitys irrespective of what concrete class they actually are.

share|improve this answer
add comment

Definition (Wikipedia): "Sprite: a two-dimensional image or animation that is integrated into a larger scene."

Your own words: SpriteEntity (everything you need to draw on screen, including lighting info)

This is a clear separation of concerns. The physics don't care how the data is displayed on the screen, and the display doesn't care how the object being displayed interacts.

Neither of these objects should inform each other at all, from a purist perspective. What should happen is probably something like this:

abstract class GameEntity
{
    Physics _physics;
    Sprite _sprite;

    public Physics Physics 
    {
        get {return _physics;}
        private set{_physics = value;}
    }

    public Sprite Sprite
    {
        get { return _sprite; }
        private set { _sprite = value; }
    }

    public GameEntity(Physics physics, Sprite sprite)
    {
        this.Physics = physics;
        this.Sprite = sprite;
    }    
}

class Physics { }

class Sprite { }

class SomeObject : GameEntity
{
    public SomeObject()
    {
        Physics p = new Physics(args);
        Sprite s = new Sprite(args);
    }

    protected SomeObject(Physics p, Sprite s)
        : base(p, s) { }
}
share|improve this answer
1  
Hi Michael, welcome to the site. Your answer is a bit naive, because it doesn't go into the deeper issue of shared data, which is really what my question is about. That's something hard to model in an entity/component system. –  ashes999 Dec 14 '12 at 22:37
add comment

How two components (what you call SpriteEntity/PhysicsEntity) is often something that drastically varies between implementations. Some developers prefer the message/event approach to keep everything absolutely decoupled and not aware of the world outside it's purview of purpose. Others often decide to expose each other through carefully well defined interface classes to minimize the exposure of one component to another. But component systems always face this same problem of shared data exchange.

My preference has always been to separate data and logic when it comes to systems like this. A very trivial example based on your two components would be:

// An abstract class to allow for storing components in containers
class IComponent {};

class SpriteComponent : public IComponent {
  /* stores sprite properties like mesh, etc */
};

class PhysicsComponent : public IComponent {
  /* stores rigid body, physics-ish proeprties */
};

In this example, I am deriving the two components from a parent class but nothing in dictates that this is necessary. It makes it convenient if you wanted to store these two components in a hetrogenious container but also allows a means to store common API methods between two different component types too.

Now if you wanted to get the position from a physics component, you could do it like the following by using your entity class as the mediator to lookup the needed component reference:

class SpriteSystem {
  void Update(float delta) {
    BOOST_FOREACH(SpriteVector::value_type& sprite, mSpriteList) {
      sprite.SetPosition(sprite.GetEntity()->GetComponent<PhysicsComponent>()->GetPosition());
    }
  }
};

What I dislike here is that the SpriteSystem which manages my SpriteComponent objects now has complete knowledge of the PhysicsComponents. It also implies that there is most likely some dynamic_cast<> going on under the hood because you probably have a simple hetergenious list storing your components which isn't ideal for performance. It's obviously a pre-optimization no no but dynamic_cast<> are often things I avoid particularly if it's in code that will be executed often in a loop.

Some may be fine with such a solution, but now if that PhysicsComponent's interface changes, I impact the SpriteSystem. One could argue that you cuold abstract that away and subclass PhysicsComponent from a IPhysicsComponent to avoid that probability, but it still doesn't hide the fact that my sprite system is directly dependent on the existing and exposed API of the physics component itself.

So how about we deal with this by providing another layer of indirection?

If we were to consider some slight changes, where entity doesn't actually exist as a class but rather an identifier that categorizes a subset of components across systems and we make a rule in the sand that systems talk to systems and components talk to systems, we've now added that layer of indirection. Combine that with well defined interfaces and you could get something like:

class IPhysicsSystem {
  // provides means of getting an entity's position
  Vector3& GetEntityPosition(TEntityId entityId) = 0;
};

class PhysicsSystem : public IPhysicsSystem {
};

class SpriteSystem : public ISpriteSystem {
public:
  SpriteSystem(IPhysicsSystem& physics) : mPhysicsSystem(physics) {}

  void Update(float delta) {
    BOOST_FOREACH(SpriteVector::value_type& sprite, mSpriteList) {
      Vector3& position = mPhysicsSystem.GetEntityPosition(sprite.entityId());
      sprite.SetPosition(position);
    }
};

The great part of this design is that you can clearly see the system dependencies because we use constructor injection to represent them. This helps knowing how to order these systems in the update loop so that all your state changes remain consistent in each iterative loop.

The above example demonstrates reactive updates. If you would have rather done proactive updates instead, possibility minimizing the number of sprites to update each pass and the number of physics system calls, you could easily have your physics system check whether a physics component's position changed since the last frame. If it did, send an event/message indicating the entity id and new position. The sprite system could cache those events for when it updates and it would have looked like this:

class SpriteSystem : public ISpriteSystem {
public:
  void Update(float delta) {
    BOOST_FOREACH(PositionEventList::value_type& positionEvent, mPositionEventList) {
      GetSpriteByEntityId(positionEvent.entityId).SetPosition(positionEvent.position);
    }
};

How you get the events into the sprite system is an entirely different discussion.

The point here is that there are many methods of handling the exchange of data between different components, but believe me when I say that if you can keep your components as decoupled as possible, the ability to maintain and mold your game from iteration to iteration into something bigger and better will be far easier for you.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.