Gra 2D, część 5 Odtwarzamy dźwięk

background image

Następny artykuł - Wykrywanie kolizji i obsługa jednostek

[2]

Opublikowane na Wrocławski Portal Informatyczny (http://informatyka.wroc.pl)

Strona główna > Gra 2D, część 5: Odtwarzamy dźwięk

Gra 2D, część 5: Odtwarzamy dźwięk

03.02.2010 - Łukasz Milewski

Trudność
Możemy łatwo zwiększyć atrakcyjność naszej gry. Wystarczy wpłynąć na wyobraźnię gracza, tak
aby sam zaczął widzieć to, czego oczekuje. Jednym z najprostszych sposobów oddziaływania na
wyobraźnię jest dźwięk i muzyka. Stosując odpowiedni podkład muzyczny łatwo jest wpływać na
nastrój gracza - sprawiać aby był szczęśliwy i odprężony, albo żeby się wystraszył (jeżeli gra w
horror). Oprawa dźwiękowa jest jednym z najważniejszych elementów gier. Dlatego w tym artykule
zobaczymy jak można dodać ją do naszej produkcji.

Poprzedni artykuł - Hall of fame

[1]

Plan działania

Zaczniemy od przywrócenia gry do stanu, w którym możemy biegać graczem po mapie. Następnie
zaprogramujemy klasę Sound, która będzie odpowiedzialna za odtwarzanie muzyki i efektów dźwiękowych.
Na koniec dodamy muzykę do rozgrywki oraz dźwięk w momencie, gdy gracz będzie skakał.

Skopiuj pliki 05_game.mp3

[3]

oraz jump.wav

[4]

do katalogu data. Skopuj tam również również plik sounds.txt

[5]

.

kod początkowy

[6]

Linkowanie

Aby uruchomić kod z tego artykułu, potrzebujemy biblioteki SDL_mixer. Musimy zainstalować tę bibliotekę
oraz lekko zmodyfikować plik SConstruct, aby uwzględniał ją przy linkowaniu. Linijkę:

1

env.

MergeFlags

(

"-lSDL -lGL -lGLU"

)

;

zamieniamy na:

1

env.

MergeFlags

(

"-lSDL -lGL -lGLU -lSDL_mixer"

)

;

Interfejs klasy Sound

Naszym celem jest zaprogramowanie klasy do dźwięku i muzyki. Ostatecznie chcemy mieć możliwość
odtworzenia efektu dźwiękowego znając tylko jego nazwę.

Potrzebujemy nowy nagłówek "SDL/SDL_mixer.h". Chcemy móc załadować dźwięki i ich konfigurację z dysku
oraz mieć możliwość odtworzenia muzyki czy odgłosu. Te funkcje spełniają metody, odpowiednio:
LoadSounds, PlayMusic, PlaySfx, LoadMusic i LoadSfx posłużą do wczytania obiektu danych muzyki i danych
odgłosu (tak! to będą różne typy danych). W mapach m_sfx i m_music zapamiętamy odwzorowanie nazwy w
obiekt dźwięku.

Pokaż/ukryj kod

1

2

3

4

5

6

7

#ifndef __SOUND_H__

#define __SOUND_H__

#include <string>

#include <map>

#include <boost/shared_ptr.hpp>

Gra 2D, część 5: Odtwarzamy dźwięk

http://informatyka.wroc.pl/print/476

1 z 7

2012-12-21 17:03

background image

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

#include <SDL/SDL_mixer.h>

class

Sound

{

public

:

explicit

Sound

()

;

void

LoadSounds

()

;

void

PlayMusic

(

const

std

::

string

&

name

)

;

void

PlaySfx

(

const

std

::

string

&

name

)

;

private

:

void

LoadMusic

(

const

std

::

string

&

name,

const

std

::

string

&

filename

)

;

void

LoadSfx

(

const

std

::

string

&

name,

const

std

::

string

&

filename

)

;

private

:

std

::

map

<

std

::

string

, Mix_Chunk

*

>

m_sfx

;

std

::

map

<

std

::

string

, Mix_Music

*

>

m_music

;

}

;

typedef

boost

::

shared_ptr

<

Sound

>

SoundPtr

;

#endif /* __SOUND_H__ */

Start!

Inicjalizacja

Na początek potrzebna jest nam lekka modyfikacja pliku App.cpp. Linijkę:

1

SDL_Init

(

SDL_INIT_VIDEO

)

;

zamieniamy na:

1

SDL_Init

(

SDL_INIT_VIDEO

|

SDL_INIT_AUDIO

)

;

W ten sposób inicjalizujemy dźwięk w bibliotece SDL. Musimy jeszcze zaimplementować inicjalizację w
konstruktorze Sound::Sound (plik Sound.cpp)

1

2

3

4

5

6

7

8

9

10

11

12

13

Sound

::

Sound

()

{

int

audio_rate

=

44100

;

Uint16 audio_format

=

AUDIO_S16SYS

;

int

audio_channels

=

2

;

int

audio_buffers

=

4096

;

if

(

Mix_OpenAudio

(

audio_rate, audio_format,

audio_channels, audio_buffers

)

!

=

0

)

{

std

::

cout

<<

"Unable to initialize audio: "

<<

Mix_GetError

()

<<

"

\n

"

;

exit

(

1

)

;

}

}

Przekazujemy parametry do funkcji Mix_OpenAudio. Ważny jest pierwszy (audio_rate). Określa on
częstotliwość próbkowania. Im większy, tym lepsza jakość i większe zużycie CPU (tak - SDL_mixer to dźwięk
programowy, a nie sprzętowy). Jeżeli zamiast 44100 wpiszemy 22050 to prawdopodobnie będzie słychać
szum.

Wczytanie danych z dysku

Zajmijmy się wczytaniem opisu dźwięków z pliku. Nasz plik (data/sound.txt) wygląda tak:

Gra 2D, część 5: Odtwarzamy dźwięk

http://informatyka.wroc.pl/print/476

2 z 7

2012-12-21 17:03

background image

1

2

music game data

/

game.

mp3

sfx jump data

/

jump.

wav

Pierwsza kolumna to typ dźwięku (muzyka lub sfx.). Druga to nazwa dźwięku (po tej nazwie będziemy
odwoływali się do dźwięku w programie). Ostatnia kolumna to ścieżka do pliku z dźwiękiem, jaki należy
załadować.

Kod jest bardzo prosty. Wczytujemy kolejne linijki. W każdej z nich wczytujemy najpierw typ i nazwę, a
następnie nazwę pliku. Z nazwy pliku usuwamy spacje. Ostatnim krokiem jest sprawdzenie, jakiego typu jest
nasz dźwięk i wczytanie go przy pomocy metody LoadMusic lub LoadSfx.

Pokaż/ukryj kod

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

void

Sound

::

LoadSounds

()

{

std

::

ifstream

settings

(

"data/sounds.txt"

)

;

std

::

string

type

;

std

::

string

name

;

std

::

string

filename

;

while

(

settings

)

{

settings

>>

type

>>

name

;

std

::

getline

(

settings, filename

)

;

filename.

erase

(

std

::

remove

(

filename.

begin

()

, filename.

end

()

,

' '

)

, filename.

end

())

;

if

(

type

==

"music"

)

{

LoadMusic

(

name, filename

)

;

}

else

if

(

type

==

"sfx"

)

{

LoadSfx

(

name, filename

)

;

}

else

{

std

::

cout

<<

"Unknown sound type: '"

<<

type

<<

"'

\n

"

;

}

}

}

Metody LoadMusic i LoadSfx są bardzo podobne. Aby załadować plik z muzyką, wywołujemy metodę
Mix_LoadMUS. Aby załadować plik z SFXem, wykorzystujemy Mix_LoadWAV. Te metody zwracają obiekty
odpowiednich dźwięków, które następnie kojarzymy ze sobą w mapach m_sfx i m_music.

Pokaż/ukryj kod

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

void

Sound

::

LoadMusic

(

const

std

::

string

&

name,

const

std

::

string

&

filename

)

{

Mix_Music

*

m

=

Mix_LoadMUS

(

filename.

c_str

())

;

if

(

m

)

{

m_music.

insert

(

std

::

make_pair

(

name, m

))

;

}

else

{

std

::

cout

<<

"Can't load music file "

<<

filename

<<

"

\n

"

;

}

}

void

Sound

::

LoadSfx

(

const

std

::

string

&

name,

const

std

::

string

&

filename

)

{

Mix_Chunk

*

c

=

Mix_LoadWAV

(

filename.

c_str

())

;

if

(

c

)

{

m_sfx.

insert

(

std

::

make_pair

(

name, c

))

;

}

else

{

std

::

cout

<<

"Can't load sfx file "

<<

filename

<<

"

\n

"

;

}

}

Odtworzenie wczytanych dźwięków

Pozostaje tylko odtwarzanie dźwięku. Tutaj ponownie metody dla odgłosów i muzyki się nie różnią.

Gra 2D, część 5: Odtwarzamy dźwięk

http://informatyka.wroc.pl/print/476

3 z 7

2012-12-21 17:03

background image

Sprawdzamy, czy dźwięk o podanej nazwie był wczytany. Jeżeli nie, wypisujemy odpowiedni komunikat.
Jeżeli tak, odtwarzamy go, wykorzystując odpowiednią funkcję SDL_mixera (Mix_PlayMusic lub
Mix_PlayChannel).

Pokaż/ukryj kod

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

void

Sound

::

PlayMusic

(

const

std

::

string

&

name

)

{

if

(

m_music.

find

(

name

)

==

m_music.

end

())

{

std

::

cout

<<

"Unknown music '"

<<

name

<<

"'

\n

"

;

}

else

{

Mix_PlayMusic

(

m_music

[

name

]

,

-

1

)

;

// -1 oznacza zapętlenie

}

}

void

Sound

::

PlaySfx

(

const

std

::

string

&

name

)

{

if

(

m_sfx.

find

(

name

)

==

m_sfx.

end

())

{

std

::

cout

<<

"Unknown sfx '"

<<

name

<<

"'

\n

"

;

}

else

{

if

(

Mix_PlayChannel

(

-

1

, m_sfx

[

name

]

,

0

)

==

-

1

)

{

std

::

cout

<<

"Unable to play sfx: '"

<<

name

<<

"'

\n

"

;

}

}

}

Dodajemy dźwięki do gry

OK. Mamy już wszystko, co potrzebne. Teraz zmieniamy klasę App.cpp (plik App.cpp). Na początek
przywróćmy chodzącego po mapie gracza (podczas poprzedniej lekcji usunęliśmy go). W tym celu zmieniamy
metodę Run. Linijki:

Pokaż/ukryj kod

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

// pętla główna

ScoreSubmit ss

(

222

)

;

size_t

last_ticks

=

SDL_GetTicks

()

;

while

(

!

ss.

IsDone

())

{

ss.

ProcessEvents

()

;

// time update

size_t

ticks

=

SDL_GetTicks

()

;

double

delta_time

=

(

ticks

-

last_ticks

)

/

1000.0

;

last_ticks

=

ticks

;

// update & render

if

(

delta_time

>

0

)

{

ss.

Update

(

delta_time

)

;

}

ss.

Draw

()

;

}

zamieniamy na:

Pokaż/ukryj kod

1

2

3

4

5

6

7

8

9

// pętla główna

size_t

last_ticks

=

SDL_GetTicks

()

;

while

(

!

is_done

)

{

ProcessEvents

()

;

// time update

size_t

ticks

=

SDL_GetTicks

()

;

double

delta_time

=

(

ticks

-

last_ticks

)

/

1000.0

;

last_ticks

=

ticks

;

Gra 2D, część 5: Odtwarzamy dźwięk

http://informatyka.wroc.pl/print/476

4 z 7

2012-12-21 17:03

background image

10

11

12

13

14

15

16

17

// update & render

if

(

delta_time

>

0

)

{

Update

(

delta_time

)

;

}

Draw

()

;

}

Teraz zmieniamy klasę Engine (plik Engine.h).

Pokaż/ukryj kod

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

#ifndef ENGINE_H_

#define ENGINE_H_

#include <string>

#include <GL/gl.h>

#include "SpriteConfig.h"

#include "Renderer.h"

class

Engine

{

public

:

static

Engine

&

Get

()

{

static

Engine engine

;

return

engine

;

}

void

Load

()

{

m_sprite_config.

reset

(

new

SpriteConfig

::

SpriteConfig

())

;

m_renderer.

reset

(

new

Renderer

::

Renderer

())

;

}

SpriteConfigPtr SpriteConfig

()

{

return

m_sprite_config

;

}

RendererPtr Renderer

()

{

return

m_renderer

;

}

private

:

SpriteConfigPtr m_sprite_config

;

RendererPtr m_renderer

;

}

;

#endif /* ENGINE_H_ */

Dodajemy wskaźnik na klasę dźwięku tak samo, jak np. na klasę Renderer. Odpowiedni kod po zmianach
wygląda następująco:

Pokaż/ukryj kod

1

2

3

4

5

6

7

8

9

10

11

12

13

#ifndef ENGINE_H_

#define ENGINE_H_

#include <string>

#include <GL/gl.h>

#include "SpriteConfig.h"

#include "Renderer.h"

#include "Sound.h"

class

Engine

{

public

:

Gra 2D, część 5: Odtwarzamy dźwięk

http://informatyka.wroc.pl/print/476

5 z 7

2012-12-21 17:03

background image

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

static

Engine

&

Get

()

{

static

Engine engine

;

return

engine

;

}

void

Load

()

{

m_sprite_config.

reset

(

new

SpriteConfig

::

SpriteConfig

())

;

m_renderer.

reset

(

new

Renderer

::

Renderer

())

;

m_sound.

reset

(

new

Sound

::

Sound

())

;

}

SpriteConfigPtr SpriteConfig

()

{

return

m_sprite_config

;

}

RendererPtr Renderer

()

{

return

m_renderer

;

}

SoundPtr Sound

()

{

return

m_sound

;

}

private

:

SpriteConfigPtr m_sprite_config

;

RendererPtr m_renderer

;

SoundPtr m_sound

;

}

;

#endif /* ENGINE_H_ */

Wracamy do App.cpp i przed komentarzem "pętla główna" dodajemy:

1

2

Engine

::

Get

()

.

Sound

()

-

>

LoadSounds

()

;

Engine

::

Get

()

.

Sound

()

-

>

PlayMusic

(

"game"

)

;

Zmieńmy jeszcze metodę skoku gracza (Player::Jump w pliku Player.cpp) z:

1

2

3

4

5

6

7

8

9

void

Player

::

Jump

(

double

y_velocity

)

{

// wykonaj skok o ile jest taka możliwość

if

(

m_jump_allowed

)

{

m_jump_allowed

=

false

;

m_is_on_ground

=

false

;

m_vy

=

y_velocity

;

// początkowa prędkość

m_ay

=

DefaultYAcceleration

;

// przyspieszenie

}

}

na:

1

2

3

4

5

6

7

8

9

10

void

Player

::

Jump

(

double

y_velocity

)

{

// wykonaj skok o ile jest taka możliwość

if

(

m_jump_allowed

)

{

m_jump_allowed

=

false

;

m_is_on_ground

=

false

;

m_vy

=

y_velocity

;

// początkowa prędkość

m_ay

=

DefaultYAcceleration

;

// przyspieszenie

Engine

::

Get

()

.

Sound

()

-

>

PlaySfx

(

"jump"

)

;

}

}

Jak widać, odgrywanie dźwięków jest bardzo proste! Nauczyliśmy się wczytywać własne pliki z danymi oraz
odtwarzać dźwięk i muzykę. W wyniku naszej pracy powstał nowy kod źródłowy, który jest dostępny tutaj

[7]

.

Gra 2D, część 5: Odtwarzamy dźwięk

http://informatyka.wroc.pl/print/476

6 z 7

2012-12-21 17:03

background image

Masz pytanie, uwagę? Zauważyłeś błąd? Powiedz o tym na forum

[8]

.

Zadania

Przywróć widok klawiatury ekranowej z poprzedniego artykułu (ScoreSubmit). Dodaj dźwięk klawiszy
naciskanych wtedy, gdy są wpisywane litery.
Spróbujmy zrobić odgłos kroków. W tym celu potrzebujemy odtworzyć zapętlony dźwięk, gdy gracz
zaczyna się poruszać i wyciszyć go, gdy się zatrzymuje. Za zapętlenie odpowiada ostatni argument
funkcji Mix_PlayChannel. Znajdź w internecie odpowiedni dźwięk i napisz metodę
Sound::PlaySfxLooped.
Zmień metody PlaySfx, aby zwracały id kanału (to, co zwraca funkcja Mix_PlayChannel). Gdy gracz
zaczyna iść, wywołaj metodę Sound::PlaySfxLooped("move"). Zapamiętaj zwrócone id. Gdy gracz się
zatrzyma, wywołaj metodę Sound::StopSfx(zapamiętane_id); metodę Sound::StopSfx zaimplementuj,
wykorzystując Mix_HaltChannel i podając jako argument id kanału.

Adres źródła: http://informatyka.wroc.pl/node/476

Odnośniki:
[1] http://informatyka.wroc.pl/node/475
[2] http://informatyka.wroc.pl/node/477
[3] http://informatyka.wroc.pl/upload/mmi/platf/05_game.mp3
[4] http://informatyka.wroc.pl/upload/mmi/platf/05_jump.wav
[5] http://informatyka.wroc.pl/upload/mmi/platf/05_sounds.txt
[6] http://informatyka.wroc.pl/upload/mmi/platf/05_kod_poczatkowy.zip
[7] http://informatyka.wroc.pl/upload/mmi/platf/05_kod_wynikowy.zip
[8] http://informatyka.wroc.pl/forum/viewtopic.php?f=55&t=358

Gra 2D, część 5: Odtwarzamy dźwięk

http://informatyka.wroc.pl/print/476

7 z 7

2012-12-21 17:03


Wyszukiwarka

Podobne podstrony:
Gra 2D, część 1 Platformówka jak to się robi
8 Technologie multimedialne podstawy czesc II Dzwiek Zastosowanie multimediow
Zapis i odtwarzanie dźwięku
Wyznaczenie prędkości dźwięku metodą składania drgań elektromagnetycznych, Pwr MBM, Fizyka, sprawozd
Rodzaje plików dźwiękowych Odtwarzacze multimedialne
gra dźwięki
88 Leki przeciwreumatyczne część 2
guzy część szczegółowa rzadsze

więcej podobnych podstron