6
\$\begingroup\$

Assuming the underlying low level audio logic is encapsulated in some outside class... what is the best way to add audio effects to a component-based entity framework?

I am taking the approach that entities are just IDs and a type of database is used to associate components with entity IDs. The I can create masks for the systems to query particular types from the database.

So for example for components I might have:

  • PositionComponent.
  • PhysicsComponent.
  • VisualComponent.

And the corresponding systems:

  • VisualSystem, which processes visual components, hands off to outside renderer.
  • PhysicsSystem, which processes physics, updates position vectors.

So the question is, how best to add audio to this system... or should I even bother? The problem I am having is mapping sounds to actions and when to play sounds.

Here are some approaches I've thought of::

  1. Force PhysicsComponents to have a some collision sound field and when a collision occurs, play the sound within the physics system.

  2. Same as 1 but instead when a collision is detected add a SoundComponent(sound) to the entity and lets the SoundSystem see that and consume it (so the sound plays once for example). This still means PhysicsComponent would have a sound field. So really this just shifts the playing of audio into a system.

I don't like either of those approaches. They are not very suitable to expanding the game either. Now I would need a field for every possible sound a unit could generate.

Really I think individual entities should dictate which sound to play based on some component attached to them I would assume. The question then becomes how to know when to play that sound.

Sorry if this is all very vague. Any tips appreciated.

\$\endgroup\$

3 Answers 3

9
\$\begingroup\$

You are teetering on the brink of the pit that is "putting everything in the entity system." Not everything needs to be a component, and indeed, many things should not be.

You should strive for separation of concerns where possible: a physics system should not know about the sound system at all. This includes avoiding a "sound to play on collision" field for objects.

Instead, build your physics system such that it exposes callbacks for collisions; a callback can be as simple as a function that has references to the two colliding rigid bodies to as complex as you need.

There is a system in your game that knows about both physics and sound: the "game" class itself, the thing that holds both the "sound system" and the "physics system." Have this game class wire up callbacks from the physics into the game logic, which can understand what sounds to play when two things collide, and play them. Since the game already knows about both systems out of necessity, you introduce no additional coupling. This works perfectly fine in an entity/component system because physics components typically correspond to rigid bodies.

If you need to put SoundComponents in your game, use them for things like controlling whether the entity plays a periodic sound or emits an audio effect (like local sound damping).

Most of the time, component-based entity systems work best when paired with some form of scripting language or other way to drive behavior without recompilation of code. If you have such a thing, I'd recommend giving your "script components" access to at least of subset of the audio API, so that they can request that sounds be played when they determine it's warranted (for example: a script component representing enemy AI that "detects" a player may want to issue a warning growl).

Generally, I find that sounds work best when they are just a service that appropriate components or other systems can access as needed. There's room for things like SoundComponent for limited, simple use cases (like periodic sounds, as mentioned). But if you try to shoehorn all sound activity into components I've found that you often end up with a complex mess of components that's hard to balance for good audio mixing.

Also remember: an entity system is a high level concept. It's higher level than sound or physics, so the components should usually be built on top of the underlaying system and not the other way around (by which I mean it should be theoretically possible to use your physics or audio system without components if you wanted to).

\$\endgroup\$
2
  • \$\begingroup\$ Excellent answer, thanks. I agree with everything you have mentioned. I'd like to mark this as an answer but it still leaves one question open. Going with the approach of a collision callback into game. How does this callback know which sound to play? The physics system doesn't care the type of game object it is processing, it is just concerned about collision shapes and adjusting vectors. So now in my callback I know two entities collided and their id but nothing more. Lets say each entity has a different collision sound to play, where do you introduce this field? \$\endgroup\$
    – Halsafar
    Commented Mar 8, 2014 at 20:43
  • \$\begingroup\$ The typical approach is to correlate the physics objects from the callback with their entities, and from there you can get the components or any other data you need to figure out, for those two bodies, what the interaction/collision sound should be. \$\endgroup\$
    – user1430
    Commented Mar 8, 2014 at 20:47
0
\$\begingroup\$

This is just an idea, as I didn't try it out in practice.

What if you use empty components to indicate state, such as IsColliding, IsFired, etc.

In this case, you could have a SoundSystem configured with a map, so that it triggers a sound for each state. Such as:

{(RedBullet, IsFired): "red_fire.wav",
 (BlueBullet, IsFired): "blue_fire.wav",
 (Player, IsCollidingWithPathBlocker): "thump.wav",
 (Player, IsCollidingWithEnemyBullet): "ouch.wav"}
\$\endgroup\$
1
  • \$\begingroup\$ This would be coupling to the max, right? I tend to create my systems so that once it's programmed and being used, I should not have to change it later (think open/closed principle). In your example, you would need to add n+n amounts of mappings each time you create a new collidable component type. I think we are better off not thinking of the sound system to own the rights to decide which sound to play, just like the renderable system does not know which image to render. It is up to a related component to decide that. \$\endgroup\$ Commented Feb 17, 2016 at 20:44
0
\$\begingroup\$

Actually that is easy to achieve.

If you have a collision, create a new entity with position component (for positional sound) and a collision component (maybe with the force as data). Now write a CollisionSoundSystem which deals with that new entity.

And I suggest a decay component and a decay system to get rid of the collision entity in under a second. Maybe you have an EffectSystem as well for some nice hit effects.

I would not mix it too much with traditional OO practices, at least not on the same abstraction level.

In ES, you have to solve things in a different way than in traditional way of programming. Think in therms of an ES.

\$\endgroup\$
1
  • \$\begingroup\$ You haven't fully answered the question. :) How does your solution deal with designers being able to define the sounds used in those collisions? \$\endgroup\$ Commented Mar 9, 2016 at 5:02

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .