As this is making game code into framework code and frameworks need to be in a very generalized form to be used by many applications, its automatically getting overengineered for simple things like changing color of a sprite, but it still seems too specialized for general use (only usable for some changes on sprites).
I see what you mean, and it's indeed not always easy to have a good trade-off between genericity and simplicity. Thor.Animation may be a bit unconventional at first (especially if one is used to other animation systems), and it won't fit in every design, but I think overall it does a good job at serving a diversity of use cases with a simple API.
Concerning the animations themselves, they are just functors defining some modification of
anything for a given progress in [0,1] -- you're not limited to SFML or Thor objects being animated. Some of the most often used animations (like fading in/out) have been predefined, and they have been defined in a generic way to be also applicable to particles, for example. Apart from those animations, I developed
thor::Animator as a "player" for animations. Animations have some constraints and requirements (only one can be active at a time, animations are referenced by an ID, animations automatically stop or loop when ending) that require considerable amount of code, mostly including boring boilerplate like handling time/frames correctly and accounting for special cases. These are the parts I tried to factor out in Thor.
When you browse the forum and the wiki, you'll see a lot of
AnimatedSprite classes that are similar to
sf::Sprite. That's a design I didn't want, because it is way too limited: The user is forced to use this class (instead of his own objects), he can only use predefined animations such as changing frames, and higher-level functionality (like managing different animations, as provided by
thor::Animator) must still be handcrafted.
How I would code a game is, I provide some generalized code to calculate gameticks in the game loop (not have N objects repeatedly calculate times). Then on each tick iterate through all needed gameobjects in a vector or shortened vector with only pointers to relevant objects (or different data structure when its more appropriate for the current game), then call update on each of those objects, where inside it it does whats needed, by explicitely calling appropriate helpers depending on current data.
You can't handle everything inside the entities, for many tasks there is still need for a centralized object that has an overview of everything. The collision thread
here is a good example.
When the game grows more complex, there will be several data structures that maintain some kind of state, e.g. graphs for pathfinding, list of targets, a queue of orders, a selection of units, whatever. All these data structures require unit registering and unregistering, for example a unit that dies must notice all the lists where it's referenced. This is a typical use case for the Observer pattern. You have to implement this functionality somehow, and if you want to do it generically (simply register a function that is called upon destruction), you will always end up with something like callbacks or a list of polymorphic listener objects. Hardcoding everything in the entity's destructor does not scale: It creates a dependency bottleneck (suddenly the entity must know about all kinds of lists) and you need to create a different entity class everytime the unregister actions differ.
I did not look that much at the particle systems, but some of those things, where you added extra classes for callbacks, could maybe get folded into the particlesystem, which could get some data at initialization like particle-weight, lifetime as they are probably needed most of the time anyway. Maybe it could also get simplified by not having variable amounts of emitters per system and just creating a new system when needed by the user.
That would be a way, but I prefer to keep the responsibilities of storing and creating particles separate, thus the emitter concept. A list of emitters provides the advantage that you can easily attach them to different positions, for example 4 engines of an airplane -- especially if one writes an emitter that integrates to a scene graph, which is very easy to achieve with the current API.