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.