Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

1

24.02.2007, 15:53

Simple DirectMedia Layer (SDL) Tutorial

SDL - kurze Übersicht(Grafik, Sound)

Ich habe mich entschlossen ein kleines Tutorial zu schreiben, weil viele Fragen im Forum über SDL in letzter Zeit kamen. Ich würde die Mods mal bitten dieses Turorial in die Sektion "Tutorial" zu verschieben, wenn ihr es als angemessen betrachtet.

Was ist SDL?
SDL ist eine Medienbibliothek, mitdem man Medien (wie Musik, Bilder,...) wiedergeben kann. Ein großer Vorteil von SDL ist das es auf vielen Plattformen läuft und damit flexibel ist, dazu ist SDL sehr einsteigerfreundlich.
In diesem Tutorial werde ich näher auf die Video- und Soundschnittstelle einegehen.

Hinweis: ich benutze den Compiler Visual Studio .NET 2003 und SDL Version 1.2.0.11 (Stand: Juni 2006).

Vorbereitung
Falls ihr SDL noch nicht habt, am Ende dieses Tutorials habe ich ein paar Links, auch zum Download von SDL.
Zudem solltet ihr nicht vergessen die LIB-Datei ihn euren Projekt zu linken und die SDL.dll muss in eurem Projektordner(also da wo die ausführbare Datei ist) sein.

Jetzt komme ich auf den Videobereich von SDL zu sprechen. Bevor man überhaupt eine Bitmap laden oder irgendeine Szene zeichnen kann, muss man SDL initialisieren. Dies geschieht mit der Funktion:

C-/C++-Quelltext

1
int SDL_Init(Uint32 flags)

Flags:
[list]
SDL_INIT_VIDEO Initialiseren von der Videoschnittstelle
SDL_INIT_EVERYTHING Initialiseren aller Schnittstellen von SDL
[/list]Grafik
Um die Videoschnittstelle aufzubauen, brauchen wir nicht mehr als die Funktion

C-/C++-Quelltext

1
SDL_Surface* SDL_SetVideoMode(int Width, int Heigth, int bpp, Uint32 flags);

Verschieden Flags:
[list]
SDL_HWSURFACE Erstellt ein Hardwaresurface
SDL_SWSURFACE Erstellt ein Softwaresurface
SDL_DOUBLEBUF Surface mit Front- und Backbuffer
SDL_FULLSCREEN Vollbildmodus
[/list]Es gibt den Frontbuffer, dies ist die Fläche, welche man sieht. Da wir das Flag SDL_DOUBLEBUF benutzen haben wir nicht nur den sichtbaren Buffer, sondern noch einen zweiten, genannt Backbuffer. In den Backbuffer zeichnen wir die Bilder mit SDL_BlitSurface.
Danach werden die beiden Buffer geflippt, d.h. das was im Inhalt des Backbuffer ist, kommt in den Frontbuffer und wird somit sichtbar.
Den Backbuffer setzt man dazu ein, um alle Bitmaps gleichzeitig zu zeichnen.
Damit ist auch das eigentliche Prinzip um Bitmaps zu laden und zeichnen schon erklärt, man lädt eine Bitmap in den Backbuffer und flippt diesen dann, ein Beispiel folgt jetzt.

Bitmaps laden und zeichnen

C-/C++-Quelltext

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include <SDL.h>
#include <windows.h>

int __stdcall WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
    SDL_Surface*    screen;     // unser Bildschirm/Zeichenfläche/Frontbuffer

    SDL_Surface*    image;      // unsere Bitmap

    SDL_Rect    src_bmp;    // Maße der Bitmap

    SDL_Rect    dest_scr;   // Maße der Bitmap auf dem Bildschirm


    // SDL initialisieren

    if(SDL_Init(SDL_INIT_VIDEO) < 0)
    {
        // Fehlerbehandlung

        return 1;
    }

    // Videomodus setzten(640*480*32) und abspeichern

    screen = SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE | SDL_DOUBLEBUF);
    if(!screen)
    {
        // Fehlerbehandlung

        return 1;
    }

    // Bitmap laden und abspeichern

    image = SDL_LoadBMP("C:/Test.bmp");
    if(!image)
    {
        // Fehlerbehandlung

        return 1;
    }

    // Werte für unsere Bitmap setzten

    src_bmp.w = image->w;       // Ganze Breite der Bitmap, die wir von unseren Surface beziehen

    src_bmp.h = image->h;       // Ganze Höhe der Bitmap, die wir von unseren Surface beziehen

    src_bmp.x = src_bmp.y = 0;  // Ganze Bitmap zeichen, also von obenan zeichnen (0;0)


    dest_scr = src_bmp;         // Ziel auf dem Bildschirm = Maße der Bitmap


    // Colorkey setzen, also die Farbe, die transparent wird, hier rosa

    SDL_SetColorKey(image, SDL_SRCCOLORKEY, 0xFF00FF);

    // Bild auf den Backbuffer blitten(zeichnen)

    SDL_BlitSurface(image, &src_bmp, screen, &dest_scr);

    // Jetzt vom Backbuffer auf dn Frontbuffer

    SDL_Flip(screen);

    // Jetzt Aktion...

    SDL_Delay(3000);    // 3 Sekunden warten


    // Surface löschen

    SDL_FreeSurface(image);
    SDL_FreeSurface(screen);

    // SDL herunterfahren

    SDL_Quit();

    return 0;
}

Erklärung
Bei diesem Beispiel wird eine Bitmap mit der Funktion SDL_LoadBMP() geladen, danach wird das Image mithilfe der Funktion SDL_BlitSurface() auf den Backbuffer geblittet/gezeichnet und danach einfach nur Backbuffer und Frontbuffer getauscht per SDL_Flip(), somit wird der Backbuffer sichtbar.

Sound mit SDL
Was ist ein Spiel ohne Sound? Richtig langweilig und ohne jegliche Spannung.
Mit SDL kann man "zwar" nur Wave-Dateien laden, aber dies reicht völlig für Soundeffekte aus.
Jetzt könnte man sagen, man kann Sounds mit sndPlaySound laden und abspielen, aber mit dieser FUnktion kann man weder den Sound mixen oder einfach nur laden um selbigen später abzuspielen.

Aufbau von SDL-Sound
Im wesentliches ist es wie die Grafikschnittstelle aufgebaut, d.h. laden und abspielen. Im wesentlichen muss man die Soundschnittstelle öffnen, die Wavedatei in ein Buffer laden und
den Sound abspielen.

Jetzt an einem Beispiel:

C-/C++-Quelltext

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
38
39
40
41
42
43
44
45
46
47
48
#include <SDL.h>

Uint32  audio_len;  // Länge der Audiodatei

Uint8*  audio_pos;  // aktuelle Position


// Callbackfunktion

void audio_msg(void* pReserved, Uint8* stream, int len);

int __stdcall WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
    SDL_AudioSpec   audio;  // Buffer für Audiodatei


    // leeren, da nicht alle Vaiablen gebraucht

    ZeroMemory(&audio, sizeof(SDL_AudioSpec));

    SDL_Init(SDL_INIT_AUDIO);

    // Ausfüllen

    audio.channels  = 2;        // Stereo = 2; Mono = 1

    audio.format    = AUDIO_S16;    // Format

    audio.freq  = 44100;    // 44 kHz

    audio.samples   = 1024;     // 1024 Samples (PCM)

    audio.callback  = audio_msg;    // Callback-Funktion


    // Audioschnittstelle öffnen

    if(SDL_OpenAudio(&audio, NULL) < 0)
    {
        // Fehlerbehandlung

        return 1;
    }

    // Audiodatei laden

    if(!SDL_LoadWAV("C:/WINDOWS/Media/Windows XP-Abmeldesound.wav", &audio, &audio_pos, &audio_len))
    {
        // Fehlerbehandlung

        return 1;
    }


    SDL_PauseAudio(0);

    // eigentlicher Code, irgendeine Aktion


    SDL_Delay(2000);// 2 Sekunden warten, da die Wavedatei nicht besonders lang

    SDL_Quit(); // Schnittstelle herunterfahren


    return 0;
}


Lösungsvorschlag für Behandlungsroutine (Callbackfunktion)

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
// Callbackfunktion

void audio_msg(void *pReserved, Uint8* stream, int len)
{
    // Wenn kein Sound mehr, beenden

    if(audio_len == 0)
        return;

    // Mixen

    len = ( len > audio_len ? audio_len : len );
    SDL_MixAudio(stream, audio_pos, len, SDL_MIX_MAXVOLUME);
    audio_pos += len;
    audio_len -= len;
}

Erklärung zu SDL-Sound
Als erstes wird die Soundschnittstelle mit unseren Parametern erstellt. Danach wird die eigentliche Audiodatei geladen und abgespielt. Die Callbackfunktion übernimmt hier die Funktion des Mixen. Wenn man die Funktion leert lassen würde oder audio_pos und audio_len nicht setzt/verändert würde der Sound immerwieder vom Anfang gespielt werden.

Weitere Links
SDL Dokumentation
SDL downloaden
http://www.pennergame.de/functions/generate_signature_114955.jpg

Wo die Fähigkeiten aufhören ... fängt der Optimismus an

"Unendlichkeit ist der Mangel an Grenzen" Aristoteles

Chase

Alter Hase

Beiträge: 753

Wohnort: Nagaoka / Darmstadt / Düsseldorf

Beruf: fauler Studi

  • Private Nachricht senden

2

25.02.2007, 13:53

Super, danke fuer diese kleine Einfuehrung, ich wollt mir schon laenger zumindest elementare SDL-Kenntnisse aneigenen :)

Hab mir jetzt das SDK runtergeladen; Das Kompilieren der SDL hat auch problemlos geklappt, was mich nur wundert ist die Beschreibung zum Testen: Erstmal heisst es da:

Zitat

Create a project as a Win32 Application.

Dann werden die libs gelinkt, und schliesslich soll man folgenden Code verwenden:

Zitat

Now create the basic body of your project. The body of your program should take the following form:

C-/C++-Quelltext

1
2
3
4
5
6
7
#include "SDL.h"

int main( int argc, char* argv[] )
{
  // Body of the program goes here.

  return 0;
}


Eine Win32-Anwendung, aber keine WinMain? Der Compiler hat auch zu Recht gemeckert, unresolved external symbol [...] usw.

Was mir jetzt ein Raetsel ist: Wieso funktioneren die Beispielprojekte problemlos ? Die sind definitiv nicht als Konsolenprojekte konfiguriert (/SUBSYSTEM:WINDOWS) aber haben auch bloss ein int main(int argc, char *argv[]) als Einstiegspunkt :shock:
"Have you tried turning it off and on again?"

3

25.02.2007, 14:24

Eine Win32 Anwendung kann doch auch eine Konsolenanwendung sein ... wo ist dein Problem? ^^
Devil Entertainment :: Your education is our inspiration
Der Spieleprogrammierer :: Community Magazin
Merlin - A Legend awakes :: You are a dedicated C++ (DirectX) programmer and you have ability to work in a team? Contact us!
Siedler II.5 RttR :: The old settlers-style is comming back!

Also known as (D)Evil

T-VIRUS

Alter Hase

Beiträge: 548

Wohnort: Göttingen(West)/Nordhausen(Ost)

Beruf: Schüler

  • Private Nachricht senden

4

25.02.2007, 14:27

Soweit ich weiß ist sogar Konsolenanwendung besser ;)
Die Grafische Oberfläche wird automatisch erstellt.
Meine Blog:)

Wer Bugs im Text findet kann sie melden, fix erscheint irgendwann :D

MFG T-VIRUS

john

Alter Hase

Beiträge: 786

Beruf: Schüler

  • Private Nachricht senden

5

25.02.2007, 14:40

Jo das ist schon richtig.
Kannst ja auch eine WinMain als Hauptfunktion nehmen, erstell doch einfach mal ein leeres Projekt und spiel dann ein bisschen damit rum..
mfg
john

Chase

Alter Hase

Beiträge: 753

Wohnort: Nagaoka / Darmstadt / Düsseldorf

Beruf: fauler Studi

  • Private Nachricht senden

6

25.02.2007, 15:11

Zitat von »"Deviloper"«

Eine Win32 Anwendung kann doch auch eine Konsolenanwendung sein ... wo ist dein Problem? ^^

Naja, wenn ich ein Projekt erstelle hab ich die Option "Win32" oder "Win32 Console Application", da ausdruecklich gesagt wurde "Win32" bin ich davon ausgegangen das eine normale Win32 Anwendung gemeint war, eben nicht Konsole.

Zwischenfrage zu VS: Ich dachte immer, wenn ich (per Wizard) einen Projekttyp auswaehle, werden nur die entsprechenden Dateien angelegt und das Projekt entsprechend konfiguriert. Daher bin ich davon ausgegangen, dass der einzige Unterschied zwischen Konsolen- und Win32-Anwendungen der Linker-Parameter /SUBSYSTEM ist. Bei Konsolenprojekten wird dieser auf 'CONSOLE' gesetzt, im andern Fall auf 'WINDOWS'. Oder gibt es da noch weitere Unterschiede in den Projekteinstellungen ?
"Have you tried turning it off and on again?"

7

03.12.2007, 18:53

Hi,

ich habe ein Problem beim Abspielen einer .wav Datei.
Ich habe mir folgende Klasse dafür erstellt:

Sounds.hpp, Klassendeklaration:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#ifndef SOUND_HPP
#define SOUND_HPP

#include <SDL.h>
#include <SDL_audio.h>

class cSound
{

 public:
        int OpenAudio ();
        void Callback(void* pReserved, Uint8* stream, int len);


private:
        Uint32    m_Audio_Len;    // Länge der Audiodatei

        Uint8*    m_Audio_Pos;    // aktuelle Position 

        SDL_AudioSpec    Audio;    // Buffer für Audiodatei 


};

#endif


Sounds.cpp, Klassenimplentierung:

C-/C++-Quelltext

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include "Sound.hpp"
#include <iostream>
using namespace std;


// Audio-Gerät öffnen und bereitstellen

int cSound::OpenAudio ()
{

     // leeren, da nicht alle Vaiablen gebraucht

     // ZeroMemory(&Audio, sizeof(SDL_AudioSpec)); 


        // Audioformat, Initalisierung

        Audio.channels  = 2;                   // Stereo = 2; Mono = 1

        Audio.format    = AUDIO_S16;           // Format

        Audio.freq      = 44100;               // 44 kHz

        Audio.samples   = 1024;                // 1024 Samples (PCM)

        Audio.callback  = &cSound::Callback;    // Callback-Funktion


        // Überprüfung ob erfolgreich

   if(SDL_OpenAudio(&Audio, NULL) < 0)
    {
            cout << "Audio-Device konnte nicht geoffnet werden!" << endl << SDL_GetError();
            return 1;
    }

       // Audiodatei laden

    if(!SDL_LoadWAV("C:/Projekte/mario.wav", &Audio, &m_Audio_Pos, &m_Audio_Len))
    {
        cout << "Audio-Datei konnte nicht geoffnet werden!" << endl << SDL_GetError();
        return 1;
    } 

        SDL_PauseAudio(0);                    // Audio Gerät starten

}       




void Callback(void* pReserved, Uint8* stream, int len)
{
    /*
    // Wenn kein Sound mehr, beenden
    if(m_Audio_Len == 0)
        return;

    // Mixen
    len = ( len > m_Audio_Len ? m_Audio_Len : len );
    SDL_MixAudio(stream, audio_pos, len, SDL_MIX_MAXVOLUME);
    m_Audio_Pos += len;
    m_Audio_Len -= len;
    */
    }


-> Die Callback Funktion ist auskommentiert, ist aber eigentlich egal, da es so immer wieder von vorne losspielt.


Das Problem dabei ist, ich bekomme folgende Fehlermeldung:
Error: C2240

C-/C++-Quelltext

1
2
3
4
'=' : cannot convert from 'void (__thiscall cSound::* )(void *,Uint8 *,int)' to 'void (__cdecl *)(void *,Uint8 *,int)'  

-> Line: 19
Line 19:        Audio.callback  = &cSound::Callback;    // Callback-Funktion



Was genau läuft hier falsch/Muss ich ändern?

8

03.12.2007, 19:32

mach die static und es geht ^^
Devil Entertainment :: Your education is our inspiration
Der Spieleprogrammierer :: Community Magazin
Merlin - A Legend awakes :: You are a dedicated C++ (DirectX) programmer and you have ability to work in a team? Contact us!
Siedler II.5 RttR :: The old settlers-style is comming back!

Also known as (D)Evil

9

03.12.2007, 19:38

O.o, daraus werde ich jetzt nicht schlau.
Wen/Was soll ich static machen?

10

03.12.2007, 19:48

Na ok dann einmal vollständig:

C-/C++-Quelltext

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
#ifndef SOUND_HPP
#define SOUND_HPP

#if (_MSC_VER >= 1300)
#pragma once
#endif // (_MSC_VER >= 1300)


#include <SDL.h>
#include <SDL_audio.h>

class Sound
{
    Uint32  m_length;
    Uint8*  m_curentposition;
    SDL_AudioSpec   m_buffer;

public:
    Sound(std::string const&);

private:
    static void _callback(void* inst, Uint8* stream, int len) 
    { static_cast<Sound*>(inst)->_callback(stream, len); }
    void callback(Uint8* stream, int len);
};

#endif // SOUND_HPP
...

C-/C++-Quelltext

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
#include "Sound.hpp"
#include <stdexcept>
#include <algorithm>

Sound::Sound(std::string const& file_name) 
    : m_buffer(), m_length(), m_curentposition(NULL)
{
    m_buffer.channels = 2;
    m_buffer.format = AUDIO_S16;
    m_buffer.freq = 44100;
    m_buffer.samples = 1024;
    m_buffer.callback = &Sound::_callback;
    m_buffer.userdata = this;
    
    if (::SDL_OpenAudio(&m_buffer, NULL) < 0) throw std::runtime_error("audio device could not be created");
    else if (::SDL_LoadWAV(file_name.c_str(), &m_buffer, &m_curentposition, &m_length) == false) throw std::invalid_argument("invalid file");
    ::SDL_PauseAudio(0);
}       

void callback(Uint8* stream, int len)
{
    if (m_length == 0) return;

    len = std::min<int>(len, m_length);
    ::SDL_MixAudio(stream, m_curentposition, len, SDL_MIX_MAXVOLUME);
    m_curentposition += len;
    m_length -= len;
} 

Das Problem ist, das SDL C ist. Da sind Klasse vollkommen unbekannt. Funktionen von Klassen brauchen aber immer einen this-Zeiger (wird an jeden Funktion des Objektes mit übergeben). Also kannst du soeinfach keine Funktion einer Klasse übergeben. Kannst aber der Callback extra-Daten (m_buffer.userdata) übergeben. Da packst du einfach den this-Zeiger rein. static-Funktionen bekommen keinen this-Zeiger und d.h. kannst du die nutzen. Darin rufst du dann einfach die nicht-statische Funktion auf (hast ja durch m_buffer.userdata den this-Zeiger).
Devil Entertainment :: Your education is our inspiration
Der Spieleprogrammierer :: Community Magazin
Merlin - A Legend awakes :: You are a dedicated C++ (DirectX) programmer and you have ability to work in a team? Contact us!
Siedler II.5 RttR :: The old settlers-style is comming back!

Also known as (D)Evil

Werbeanzeige