@pinecone said:
Initially I had animation code tied up with my player control code, I then created a animationState component (I'm using Zay ES) and seperated my animation code from the player movement that way because my initial code was messy and needed to be seperated. THEN... I refactored again to use the observer pattern instead.
My reasoning was that I need a clean way to separate not only animations, but things like playing sound effects and unlocking achievements from the code that triggers them. Using components as flags for all of these things seems like a clumsy solution, and actually calling other AppStates looks messy and has the potential to break when I change things.
As an example of how it works, my character control code sends messages to all of it’s observers with the relevant EntityId and an enum that could be RUNNING_STARTED, RUNNING_STOPPED, and a few other things. The animation system uses these to keep track of which animations it should be playing for which entities. In the future I imagine my sound code would do a similar thing and play footstep audio, or sound effects on ATTACK.
I mostly followed this for the implementation: Observer · Design Patterns Revisited · Game Programming Patterns, I chose not to use the Observer system in the java core library because it seemed too complex for what I needed.
If I have massively overcomplicated things I’d love to be set back on the right track.
Yeah, kind of.
The observer pattern is one way to decouple tightly coupled ‘things’ but as you’ve discovered, it isn’t really decoupling them that much. There are still some inherent dependencies that happen.
…versus the ES, where everything is 100% independent and any coupling is only notional.
The sound system has an EntitySet for anything with a Position and a Sound component. It uses that to play sound and it’s really simple. From that point on, anything with a position and a sound will automatically have sound played with no additional code written. The player walks, they get a “footsteps” sound attached to them. The drop a book on the floor, an entity is created at that position with the Position and Sound. Persistent sounds are covered, moving sounds are covered, temporary sounds are covered… all from one dirt-simple system.
It’s also possible to couple the sound to the animation but I always think of good reasons not to do that. (I may want animation without sound and sound without animation, for example. Character animation is where it gets a little trickier.)
Character animation need not be a component at all but purely reactive based on other state. After all, it’s a purely visual thing based on something else. When the player is walking, the player has some state that indicates he is walking. It kind of has to otherwise the systems that move him wouldn’t know to move him. In that case, a system could be watching for this state and play an animation as appropriate. That being said, things can be even simpler having an animation component and you could cover non-character animation. Character animation is a weird one, though, because generally you’d want to have the animation linked directly with movement. It’s not like a windmill spinning in the corner. The walk animation may need to speed up or slow down to keep the feet from sliding, etc… But if you already have animation components for other things then you could go a long way using that until you can implement better character animation.
Especially in an ES based application, you should not need game-level events like this. Moreover, they will likely cause you issues because now you will end up having chicken/egg problems with entity set updates and so on. One system may receive an event for something before it’s had a chance to update and process its EntitySet. An ES is supposed to prevent the need for these event objects flying all over the place anyway.