[Openal] suggestions on c++ game design with openAL most welcome
Space Ship Traveller
space.ship.traveller at gmail.com
Tue Jun 2 02:11:38 PDT 2009
Hello,
I have a number of comments for you.
Firstly, looking at your code I would suggest you review your naming
conventions.
"class classCar" is not a conventional name. If it suits you, that is
fine, but it seems like you've got an incredibly verbose naming scheme
which many people will find hard to look at.
A good scheme is to have UpperCase class names which are typically
common or proper nouns. Secondly, variables don't need to include
their type in the name. This is generally considered tedious and
unmaintainable. For example, fltFuel might change to dblFuel. But now
you have to change a lot of code. There are lots of information
regarding different kinds of coding conventions available on the
internet.
Also, you have made all your member variables public. This is also
considered harmeful, for example if you change the implementation or
storage of fltFuel, this now becomes a project wide change. The better
option is to rely on encapsulation which generally means you have
getters/setters for key values related to the object you are trying to
represent. For example, rather than having setFuel(...) you might have
a function addFuel(...) which carries out logic such as checking the
maximum fuel that can be added, etc, which in my experience reduces
the complexity of code elsewhere.
Looking at the solutions you are proposing - consider what best models
what you are trying to represent.
For example, if you have multiple cars, each car has an engine,
therefore each engine produces a sound. We can consider this the ideal
world. In computer programming we need to use tricks to achieve
adequate performance. So, it might be the case that you have up to 5
sources dedicated to engine sounds, and then choose the 5 engines
closest to the "camera" for actually playing audio. If you have more
than "x" sources close to the "camera" it may become impossible for
the listener to identify individual sounds anyway, so we can limit to
5 without any loss of apparent quality.
I would question your use of a multimap for storing cars. It seems
incredibly complex and performance heavy.
Consider the use of a simple std::vector. This is very fast and has
guaranteed O(1) performance for all operations if you are not
concerned about order. To remove an element from a vector simply use
std::swap and pop:
/// Provides an efficient way to erase elements from a std::vector
template <typename t>
bool eraseElementAtIndex (IndexT index, std::vector<t> & array)
{
if (array.size() == (index+1)) {
array.pop_back();
return false;
} else {
array[index] = array.back();
array.pop_back();
return true;
}
}
So we can achieve very quick storage with std::vector.
If you need to do space querying, for a small number of cars (< 20)
brute force algorithm will be fine, such as simply iterating through
the list and choosing the closest cars, etc.
Finally in terms of structuring the OpenAL code the best option is to
look at keeping audio completely separate from your "model".
Try to avoid things like:
> int MAX_SOUNDS = 150;
This generally considered bad practice (there are specific cases where
it is okay, but generally use std::vector for this kind of allocation).
We should consider the importance of two kinds of sounds:
- Sound that caused by specific event that occurred (crash sound).
- Sound that has been started and stopped by a particular object
(engine sound).
These two kinds of sounds can be managed differently. For example,
often with sounds based of event, my experience is that we can't tell
the difference between single source and multiple source in typical
cases as long as the sound is short - for example, explosion sound. So
in this case you can simply have a single source which is allocated
when the game starts. This is very convenient. Alternatively you can
allocate a list of sources and use them in a round robin fashion if
you need more realism or the sound is longer.
For a sound that is continuous we either have a single source (i.e.
music) or multiple sources (i.e. multiple engines). This case is
slightly more complex especially if we separate audio function from
object model (class Car). In this case, the easiest solution is to
have the audio source as part of the class Car. This is the simplest
solution and means that each car has its own audio source. But this
isn't efficient for a large number of cars, and increases coupling
between audio and your simulation which if it is running as a server /
client, obviously you don't want the server making all sorts of sounds
or sound simply may not be available.
Therefore, I recommend you look at the following structure:
using boost::shared_ptr;
class Car {
public:
Coord getPosition ();
};
class CarsController {
typedef shared_ptr<Car> CarPtr;
std::vector<CarPtr> m_cars;
std::vector<CarPtr> findCarsClosestToPoint (Coord c, unsigned maxCars);
void update (float dt);
};
> class AudioSource
> {
> ALuint intSource
>
> //funcs
> bool AssignBufferToThis(ALbyte *filename);
> void UpdatePosition(float x, float y, float z);
> void Play();
> void Stop();
> void PitchMod(float fltMod);
> void GainMod(float fltMod);
> void SetLooping(bool boolLooping);
>
> etc.
>
> }
class CarAudioController {
// OpenAL mixer, etc
struct CarSource {
CarPtr car;
AudioSource source;
}
std::vector<CarSource> m_engineSources;
void playEngineSound (CarsController * carsController) {
// Step 1: Update source positions / stop sources that are no longer
relevant, build a std::set of cars currently active
// Step 2: Query for cars that are close to the listener
// Step 3: For all these cars, check if they are currently in the
set of active cars. If not, add it and start the appropriate engine
sound.
// Step 4: Profit.
}
};
In terms of implementation, playEngineSound could implement a round
robin scheme that is updated at set time intervals - depending on RPM,
distance, etc you can update or stop the sound. There are many many
options and this pseudo code is not well developed, but it should give
you an idea of how to separate out the code. There are many ways to do
this kind of separation - another way is using a delegate.
I also wrote an article you might find interesting/useful:
http://wiki.oriontransfer.org/blog:2009:05:06:what_is_abstraction
I hope something here helps. The question you asked has a lot of
possible solutions. Out of the methods you listed below, Method 2 is
probably more in the right direction.
Kind regards,
Samuel
On 2/06/2009, at 1:59 PM, Pingwah Leronz wrote:
> First of all, hello everyone :)
>
>
> I'm working on a c++ project that's moving into the early stages of
> audio design and facing what Im guessing would be a fairly common
> problem, and wondering if anyone has any advice or input about the
> best way to approach it.
>
> For those interested -
> Let's say we have a car
> class classCar()
> {
> public:
> classCoords coordsPosition;
> classCoords coordsVelocity;
> float fltFuel;
> ...etc.
>
> };
>
> where classCoords is simply
> coords
> {
> public:
> float x, y, z;
>
> };
>
> and Cars are to be able to emit several sounds at once , an ambient
> engine roar, horn beeps, drivers yelling etc.
> Also there's to be literally millions of cars (OK, in the project
> itself , they're not cars :) ), so a method of culling sound
> emitters from the set needs to be there.
> Criteria for adding and removing Cars from the audio set is simply
> proximity to a viewing position, and the cars themselves are stored
> in something like
>
>
> multimap <classCoord*, classCar*> mapCars;
>
> (again, they're not cars- let's say the track consists of many
> discrete positions, each of which can contain many cars).
>
> I've been experimenting with two methods of handling this - the
> first with a global controller:
>
> Method 1
> ---------------------------------
> class classAudioController()
> {
> int MAX_SOUNDS = 150;
> multimap <classCar*, ALuint> mapSounds; //where ALuint here
> is a ref to the standard openal source[MAX_SOURCES]
>
> //functions
> void AddCar(classCar* newCar, ALuint* newAudioSource);
> bool AssignBufferToSource(ALuint *intSource, ALbyte* filename);
> void UpdateAllPositionsAndVelocities();
> void UpdateCarsPositionAndVelocity(classCar* findCar);
> etc.
>
>
> };
>
>
> and also by a more object oriented
>
> Method 2
> ----------------------------------
> classCar
> {
> //position, fuel, health etc.
> //....
> classAudioSource *soundSource[MAX_SOURCES]; //remember, each
> car can emit many simultaneous sounds
>
>
> }
>
> where classAudioSource is
> {
> ALuint intSource
>
> //funcs
> bool AssignBufferToThis(ALbyte *filename);
> void UpdatePosition(float x, float y, float z);
> void Play();
> void Stop();
> void PitchMod(float fltMod);
> void GainMod(float fltMod);
> void SetLooping(bool boolLooping);
>
> etc.
>
> }
>
>
>
> With Method 1, Cars are added and removed from the emitters map on
> movement events, In method 2 classAudioSources are triggered to Play/
> Stop based on listener proximity , again on movement events.
> I'm running into problems with both, mainly from unfamiliarity.
>
> If anyone here has input into handling large sets of sound sources
> in a good OO-heavy way, or can point me to any reading that does I'd
> be massively grateful.
>
> Thanks,
> Pingwah
>
>
>
>
>
>
> Find car news, reviews and more Looking to change your car this
> year? _______________________________________________
> Openal mailing list
> Openal at opensource.creative.com
> http://opensource.creative.com/mailman/listinfo/openal
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://opensource.creative.com/pipermail/openal/attachments/20090602/66a5eff0/attachment-0001.html
More information about the Openal
mailing list