1. Introduction

Note: This tutorial builds on the first YAP tutorial, and it assumes you have already worked through it.

Now that we have our graphical world ready, it's time to add some sound and even better .. 3D sounds. In our pong game, we will need only 2 sounds. One played by the ball hitting a « wall » and the other for when the ball collides a paddle.

We need also one listener which will follow the camera and to move the sound around, we will use one source. In a more complex application you probably wants more sources attached to each object but here it will be enough.

2. Setup

We need to modify the configuration of our application to tell yake that we are now using the OpenAL plugin.

//Configuration struct, storing the module used for our application.
struct TheConfiguration : public raf::ApplicationConfiguration
{
	virtual StringVector getLibraries()
	{ return MakeStringVector() << "graphicsOgre" << "inputOgre" << "audioOpenAL" ;}

	virtual StringVector getInputSystems()
	{ return MakeStringVector() << "ogre";}
	
	virtual StringVector getGraphicsSystems()
	{ return MakeStringVector() << "ogre3d";}

	virtual StringVector getAudioSystems()
	{ return MakeStringVector() << "openalpp";}

};

We need also to add all what we need in the YapMainState class:


/.../

//Adding sound... 
	
//Sound world
	yake::audio::IWorld*				mAWorld;

//One source and one listener
	audio::ISource*						mASource;
	audio::IListener*					mAListener;

//Sounds
	audio::ISoundData*					mPingSound1;
	audio::ISoundData*					mPingSound2;



/.../

And finally we need to load everything in our onCreateScene method:


/.../
                YAKE_LOG_INFORMATION("**********************************************************");
		YAKE_LOG_INFORMATION("*                 audio creation                         *");
		YAKE_LOG_INFORMATION("**********************************************************");

		YAKE_LOG_INFORMATION("Creating audio world");
		
		//Create the audio world.
		mAWorld = getAudioWorld();

		YAKE_ASSERT( mAWorld );

		YAKE_LOG_INFORMATION("Creating sounds");
		SetupSounds();

		YAKE_LOG_INFORMATION("Creating source");
		SetupSource();

		YAKE_LOG_INFORMATION("Creating listener");
		SetupListener();


		YAKE_LOG_INFORMATION("Audio scene creation done.");


/.../

3.Loading sounds.

Let's load some sounds:


void YapMainState::SetupSounds(void)
{
	//Load the sounds and set them to no loop. (for 3D localisation to work, sounds must be mono).
	mPingSound1 = mAWorld->createSoundDataFromFile ("../../media/audio/plink.wav");
	std::cout << "\"plink.wav\" loaded" << "\n";
	mPingSound1->setLoopMode (mPingSound1->SLM_LOOP_OFF);
	mPingSound2 = mAWorld->createSoundDataFromFile ("../../media/audio/pong.wav");
	std::cout << "\"pong.wav\" loaded" << "\n";
	mPingSound2->setLoopMode (mPingSound2->SLM_LOOP_OFF);
}

Don't make the same mistake as me and be careful to not use stereo sounds or the 3D localization will not work because the sound type will be set to ambiant. We also set our sounds to not loop as we just want them to play once.

4. Setup the source


void YapMainState::SetupSource(void)
{
	//Create one audio source which will follow the ball.
	mASource = mAWorld->createSource ();
}

Nothing more is needed for the source. We will set is position just before a sound is played.

5. The listener.


void YapMainState::SetupListener(void)
{
	//Create a listener.
	mAListener = mAWorld->createListener ();
	//Set the active listener (only one can be active).
	mAWorld->setActiveListener (mAListener);
	//Set position and orientation.
	mAListener->setVelocity (Vector3(0,0,0));
	mAListener->setPosition (getDefaultCamera()->getPosition());
	mAListener->setOrientation (getDefaultCamera()->getOrientation());
}

As you can see here we can directly use the camera position and orientation to set our listener. It is then easy to update the position of the listener when the camera position or orientation change in the onMMove and onFrame method:


void YapMainState::onMMove(Vector3 mVector)
	{
		
		std::cout << "MouseMove.x: " << static_cast<int>( mVector.x ) << std::endl;
		std::cout << "MouseMove.y: " << static_cast<int>( mVector.y ) << std::endl;	
		getDefaultCamera()->yaw(-( mVector.x ) * 0.13);
		getDefaultCamera()->pitch(-( mVector.y ) * 0.13);
		//Update the listener orientation.
		mAListener->setOrientation ( getDefaultCamera()->getOrientation());
	}


void YapMainState::onFrame (const real timeElapsed)
{
	real distance;
	
	if ( getApp().getKeyboard() )
		{
		//Camera stuff.
		distance = -10. * timeElapsed;
		if ( getApp().getKeyboard()->isKeyDown(input::KC_LEFT))
		  getDefaultCamera()->moveRelative( distance*Vector3::kUnitX ); //Strafe Left (TS_LOCAL)
		if ( getApp().getKeyboard()->isKeyDown(input::KC_RIGHT))
		  getDefaultCamera()->moveRelative( -distance*Vector3::kUnitX );  //Strafe Right (TS_LOCAL)
		if ( getApp().getKeyboard()->isKeyDown(input::KC_UP))
		  getDefaultCamera()->moveRelative( distance*Vector3::kUnitZ );//Move Forward in TS_LOCAL
		if ( getApp().getKeyboard()->isKeyDown(input::KC_DOWN))
		  getDefaultCamera()->moveRelative( -distance*Vector3::kUnitZ );//Move Backwards in TS_LOCAL
		
		//Update listener position.
		mAListener->setPosition (getDefaultCamera()->getPosition());
		

	/.../

6. Play the sounds

Every time a collision is detected will a wall or a paddle, we will setup the source location on the current ball location and then play the sound:


void YapMainState::onFrame (const real timeElapsed)
{


		/..../


//Check ball collision with arena.

if  (((mBall.x + mBall.mSize / 2) >= mArena.rightBound) || ((mBall.x - mBall.mSize / 2) <= mArena.leftBound))
{
	mBall.horizontalDirection = -mBall.horizontalDirection + mRandgen()*0.01; //invert horizontal direction and add some random stuff for fun.
	//Collision with wall so play the correct sound.
	mASource->stop();
	mASource->setSoundData (mPingSound1);
	mASource->setPosition(Vector3(mBall.x,mBall.mHeight,mBall.y));
	mASource->setVelocity (Vector3(mBall.horizontalDirection * distance, 0 , mBall.verticalDirection * distance));
	mASource->play();
}
if  (((mBall.y + mBall.mSize / 2) >= mArena.upperBound) || ((mBall.y - mBall.mSize / 2) <= mArena.lowerBound))
{
	mBall.verticalDirection = -mBall.verticalDirection + mRandgen()*0.01; //invert vertical direction and add some random stuff for fun.
	//Collision with wall so play the correct sound.
	mASource->stop();
	mASource->setSoundData (mPingSound1);
	mASource->setPosition(Vector3(mBall.x,mBall.mHeight,mBall.y));
	mASource->setVelocity (Vector3(mBall.horizontalDirection * distance, 0 , mBall.verticalDirection * distance));
	mASource->play();
}

//Check ball collision with paddle1. (works ok but not so realistic.)

real dx, dy;

dx = mMath.Abs (mBall.x-mPaddle1.x);
dy = mMath.Abs (mBall.y-mPaddle1.y);

//First, check if there is a collision
if ((dx <= (mBall.mSize / 2 +15)) && (dy <= (mBall.mSize / 2 + mPaddle1.mSize / 2)))
{
	//calculate collision angle and compare with paddle diagonal.
	real angle = mMath.ATan2 ((dy),(dx));
	real diag = mMath.ATan2 (mPaddle1.mSize/2,15.);
	std::cout << "ball collision with paddle 1, angle =  " << angle << std::endl;
	if ( (angle >= -diag) && 	(angle <=  diag))
	{
		mBall.horizontalDirection = -mBall.horizontalDirection + (mRandgen()-0.5)*0.05; //invert horizontal direction and add some random stuff for fun.
		//Move the ball outside the paddle.
		if ((mBall.x-mPaddle1.x) > 0) 
			mBall.setPosition (mPaddle1.x + mBall.mSize / 2. + 15.,mBall.y); 
		else
			mBall.setPosition (mPaddle1.x - mBall.mSize / 2. - 15.,mBall.y); 
	}
	else 
	{	
		mBall.verticalDirection = -mBall.verticalDirection + (mRandgen()-0.5)*0.05; //invert vertical direction and add some random stuff for fun.
		//Move the ball outside the paddle.
		if ((mBall.y-mPaddle1.y) > 0) 
			mBall.setPosition (mBall.x,mPaddle1.y + mBall.mSize /2. + mPaddle1.mSize/2.);
		else
			mBall.setPosition (mBall.x,mPaddle1.y - mBall.mSize /2. - mPaddle1.mSize/2.);

	}
	//Collision with paddle 1 so play the correct sound.
	mASource->stop();
	mASource->setSoundData (mPingSound2);
	mASource->setPosition(Vector3(mBall.x,mBall.mHeight,mBall.y));
	mASource->setVelocity (Vector3(mBall.horizontalDirection * distance, 0 , mBall.verticalDirection * distance));
	mASource->play();
	
}

//Check ball collision with paddle2. (works ok but not so realistic.)

dx = mMath.Abs (mBall.x-mPaddle2.x);
dy = mMath.Abs (mBall.y-mPaddle2.y);

//First, check if there is a collision
if ((dx <= (mBall.mSize / 2 +15)) && (dy <= (mBall.mSize / 2 + mPaddle2.mSize / 2)))
{
	//calculate collision angle and compare with paddle diagonal.
	real angle = mMath.ATan2 ((dy),(dx));
	real diag = mMath.ATan2 (mPaddle2.mSize/2,15.);
	std::cout << "ball collision with paddle 2, angle =  " << angle << std::endl;
	if ( (angle >= -diag) && 	(angle <=  diag))
	{
		mBall.horizontalDirection = -mBall.horizontalDirection + (mRandgen()-0.5)*0.05; //invert horizontal direction and add some random stuff for fun.
		//Move the ball outside the paddle.
		if ((mBall.x-mPaddle2.x) > 0) 
			mBall.setPosition (mPaddle2.x + mBall.mSize / 2. + 15.,mBall.y); 
		else
			mBall.setPosition (mPaddle2.x - mBall.mSize / 2. - 15.,mBall.y); 
	}
	else 
	{	
		mBall.verticalDirection = -mBall.verticalDirection + (mRandgen()-0.5)*0.05; //invert vertical direction and add some random stuff for fun.
		//Move the ball outside the paddle.
		if ((mBall.y-mPaddle1.y) > 0) 
			mBall.setPosition (mBall.x,mPaddle2.y + mBall.mSize /2. + mPaddle2.mSize/2.);
		else
			mBall.setPosition (mBall.x,mPaddle2.y - mBall.mSize /2. - mPaddle2.mSize/2.);
	}
	//Collision with paddle 2 so play the correct sound.
	mASource->stop();
	mASource->setSoundData (mPingSound2);
	mASource->setPosition(Vector3(mBall.y,mBall.mHeight,mBall.x));
	mASource->setVelocity (Vector3(mBall.horizontalDirection * distance, 0 , mBall.verticalDirection * distance));
	mASource->play();
}

It is important to stop playing the sound before making any change to it or it will generate an assert.

7. Conclusion

This exemple, I think, illustrates well the main advantage of yake: the homogeneity. It is possible to mix graphical object with sound objects very easly because the interface are already the same. You don't have to do it yourself.

Based on the original yap tutorial by Regress.

Lythaniel

 
tutorials/beginners/yap_2._3d_sound.txt · Last modified: 2008/02/21 21:59 (external edit)
 
Recent changes RSS feed Creative Commons License Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki