Grafik med SDL

Tillbaka till artikelarkivet

Filer

SDL-Del4.zip (11.46 kB)

Verktyg

Kommentarer (39)
Utskriftsvänligt format

Betyg

markedunmaked 5.9

SDL är ett bibliotek som gör det enkelt att använda grafik och annan media i dina program, i den här guiden visar jag hur du med hjälp utav SDL kan skapa enkla grafiska program i C++.

Navigation:

< Föregående del || Nästa del >

Text - 3 - Händelser

Del 3, händelser


Nu kan vi lite grundläggande saker inom SDL, men det är inte riktigt så att man kan göra något användbart utav det. Men det ska vi ändra på nu! I den här delen ska vi dels snygga till koden lite genom att dela upp den i funktioner, och så ska vi lära oss att få input från tangentbordet. Med hjälp utav detta kan vi låta användaren styra en spelpjäs över skärmen t ex. När man kan detta går det använda sin fantasi för att skapa ganska mycket.

Jag ska börja med att visa hur man gör en funktion som gör det lite smidigare att visa bilder på skärmen. Med denna funktion behöver man bara skriva en rad kod för att visa en bild på skärmen, istället för de 8 raden som vi använde tidigare. Dessutom kan vi nu välja vart på skärmen bilden ska visas! Såhär ser funktionen ut:

  1. int DisplayImage(SDL_Surface* destination, const char*, int x, int y)
  2. {
  3. // Ladda bilden
  4. SDL_Surface* image;
  5. image = SDL_LoadBMP("test.bmp");
  6. if (image == NULL)
  7. {
  8. cout << "Image could not be loaded!" << endl;
  9. return 1;
  10. }
  11. // Skapa en rect
  12. SDL_Rect rect;
  13. rect.x = x;
  14. rect.y = y;
  15. rect.w = image->w;
  16. rect.h = image->h;
  17. // Kopiera över bilden till ytan
  18. SDL_BlitSurface(image, NULL, destination, &rect);
  19. return 0;
  20. }


Det jag har gjort är egentligen bara att klippa ut koden som vi använde förut och stoppat den i en funktion, så att man slipper skriva så herrans mycket kod om man ska ladda in många bilder. Jag har också ordnat så att vi kan välja vart på skärmen bilden ska visas som sagt.

Det som är nytt är alltså SDL_Rect. Det är en variabel (eller egentligen en scruct) som innehåller fyra värden för att definiera en rektangel. X- och y-position samt berdd och höjd. X- och y-position får vi från argumenten till funktionen, och bredd och höjd tar vi ifrån bilden vi laddat in.

Funktionen returnerar en etta om det vart något fel och en nolla om det allt gick som det skulle.

För att anropa funktionen kan man använda sig utav en rad som denna till exempel:
DisplayImage(screen, "test.bmp", 10, 10);

Vill man varna användaren om något gick fel (detta kan minska huvudvärken vid debugging avsevärt) kan man skriva såhär istället:
if (DisplayImage(screen, "test.bmp", 10, 10) == 1) { cout << EURoeImage could not be loaded!EUR << endl; }

För att göra ett användbart spel skulle man ju gärna vilja att programmet inte avslutades efter en speciell tid, utan när användaren är klar. För att uppnå detta måste vi implementera en händelselyssnare, en bit kod som väntar på speciella händelser och då gör vissa saker. Detta går hand i hand med att få input från användaren, händelselyssnaren tar nämligen hand om knapptryckningar från tangentbordet till exempel.

Jag ska nu visa lite kod som använder sig utav en händelselyssnare. Programmet visan en bild som går att flytta på genom att klicka på piltangenterna. Här är koden:

  1. #ifdef WIN32
  2. #pragma comment(lib, "SDL.lib")
  3. #pragma comment(lib, "SDLmain.lib")
  4. #endif
  5. #include "SDL.h"
  6. #include <iostream>
  7. using namespace std;
  8. int DisplayImage(SDL_Surface* destination, const char*, int x, int y)
  9. {
  10. // Ladda bilden
  11. SDL_Surface* image;
  12. image = SDL_LoadBMP("test.bmp");
  13. if (image == NULL)
  14. {
  15. cout << "Image could not be loaded!" << endl;
  16. return 1;
  17. }
  18. // Skapa en rect
  19. SDL_Rect rect;
  20. rect.x = x;
  21. rect.y = y;
  22. rect.w = image->w;
  23. rect.h = image->h;
  24. // Kopiera över bilden till ytan
  25. SDL_BlitSurface(image, NULL, destination, &rect);
  26. return 0;
  27. }
  28. int main(int argc, char *argv[])
  29. {
  30. SDL_Surface *screen; // En yta
  31. SDL_Event event; // Variabel för händelser
  32. int imgX=0,imgY=10; // Variabler för att hålla reda på vart bilden ska ritas ut
  33. // Initiera SDL
  34. if ( SDL_Init(SDL_INIT_AUDIO|SDL_INIT_VIDEO) < 0 )
  35. {
  36. cout << "Error, unable to initialize SDL: " << SDL_GetError() << endl;
  37. SDL_Quit();
  38. return 1;
  39. }
  40. else
  41. {
  42. cout << "SDL initialized successfully!" << endl;
  43. }
  44. screen = SDL_SetVideoMode(640, 480, 16, SDL_HWSURFACE|SDL_DOUBLEBUF);
  45. if (screen == NULL)
  46. {
  47. cout << "Unable to set video mode: " << SDL_GetError() << endl;
  48. SDL_Quit();
  49. return 1;
  50. }
  51. else
  52. {
  53. cout << "Successfully set video mode!" << endl;
  54. }
  55. while (1)
  56. {
  57. SDL_PollEvent(&event);
  58. switch (event.type)
  59. {
  60. case SDL_QUIT:
  61. cout << "Terminating program!" << endl;
  62. SDL_Quit();
  63. return 0;
  64. case SDL_KEYDOWN:
  65. Uint8 *keys;
  66. keys = SDL_GetKeyState(NULL);
  67. if (keys[SDLK_UP])
  68. imgY -= 5;
  69. if (keys[SDLK_DOWN])
  70. imgY += 5;
  71. if (keys[SDLK_LEFT])
  72. imgX -= 5;
  73. if (keys[SDLK_RIGHT])
  74. imgX += 5;
  75. if (keys[SDLK_ESCAPE])
  76. {
  77. cout << "Terminating program!" << endl;
  78. SDL_Quit();
  79. return 0;
  80. }
  81. }
  82. DisplayImage(screen, "test.bmp", imgX, imgY);
  83. SDL_Flip(screen);
  84. SDL_Delay(25);
  85. }
  86. // Stäng ner SDL och frigör resurser
  87. SDL_Quit();
  88. return 0;
  89. }


Som du ser är det en hel del ny kod. Denna kod har fört vårat program ett stort steg framåt, nu är programmet inte uppvisning av några förutbestämda kommandon. Nu kan användaren interagera med programmet och få det att bete sig på olika sätt.

Den stora nyheten i programmet är att den intressanta koden, den koden som ritar ut en bild, ligger i en loop. Bilden kommer alltså att ritas ut flera gånger, och eftersom koordinaterna för bilden är variabler kan vi flytta på bilden i programmet.

Loopen kommer att köras så länge 1 är sant (while (1)), det kanske ser lite lustigt ut om det är förstå gången du ser det, men det är inte så knepigt. Det betyder bara att loopen körs för evigt, för att avsluta den måste något i loopen hända.

Loopen börjar med ett funktionsanrop till SDL_PollEvent och vi skickar variabeln event som argument. Variabeln event deklarerades först i huvudfunktionen och den kommer efter anropet till SDL_PollEvent att innehålla en händelse. Det finns lite olika typer av händelser så vi gör en switch-sats för att ta hand om de olika typerna. Den förstå typen är SDL_QUIT och det inträffar när någon trycker på krysset i övre högra hörnet t ex. Gör någon detta kommer programmet att avslutas. Den andra typen av händelse vi lyssnar efter är knapptryckning. Om en någon klapp blir nertryckt kommer att anropa SDL_GetKeyState, det är en funktion som kontrollerar vilka knappar som är nertryckta och returnerar dem i en array. Keys är en pekare till denna array och kommer nu låta oss kontrollera vilka knappar som är nertryckta. Varje knapp har ett speciellt kodnamn (som går att hitta på http://sdldoc.csn.ul.ie/sdlkey.php#AEN4743 ). ,,r Escape nertryckt så kommer programmet att avslutas, är någon piltangent nertryckt så kommer någon av variablarna imgX och imgY att ändras. Dessa används sedan för att rita ut våran bild.

I slutet av loopen så kopieras bilden vi angett över till ytan som visas på bildskärmen, och sedan uppdateras det som visas på skärmen (SDL_Flip(screen);) Vi avslutar med en delay på 25 ms, detta gör att programmet inte tar så mycket datorkraft. Skulle man inte ta med denna delay så skulle programmet stanna upp hela datorn och systemet skulle bli väldigt långsamt.

Observera att bilden kopieras till våran yta en gång varje gång loopen körs, alltså kommer skärmen att täckas av bilder efter en liten stund. Testa programmet så får du se vad jag menar. För att råda bot på detta kan vi säga åt programmet att fylla ytan screen med svart färg innan vi ritar ut bilden, på detta sätt kommer alla tidigare bilder suddas ut. Detta görs med hjälp utav raden:
  1. SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0x00, 0x00, 0x00));


Med kunskaperna du har nu borde du kunna göra ett litet spel. Testa att rita ut flera bilder, lägg in en bakgrundsbild istället för den svarta färgen och experimentera lite som du vill. Man borde kunna skapa en del enklare spel med hjälp utav detta. Försök göra ett litet plattformsspel om du har lust, en gubbe som hoppar runt mellan olika plattformar och samlar äpplen eller något annat klassiskt.

En sak som kan vara bra att veta är att man normalt sett inte brukar ha en DisplayImage-funktion som laddar in bilden vid varje körning. Istället brukar man ladda in bilden en gång, och sedan köra DisplayImage och skicka med den redan inladdade bilden som argument. Om man kör program som innehåller loopar och laddar in bilden en gång vid varje loop så tar det väldigt mycket kraft från datorn, och det använder fruktansvärt mycket minne om man inte "städar" efter sig.

Slut

Navigation:

< Föregående del || Nästa del >

Till toppen