SDL a šetřič obrazovky - Builder.cz - Informacni server o programovani

Odběr fotomagazínu

Fotografický magazín "iZIN IDIF" každý týden ve Vašem e-mailu.
Co nového ve světě fotografie!

 

Zadejte Vaši e-mailovou adresu:

Kamarád fotí rád?

Přihlas ho k odběru fotomagazínu!

 

Zadejte e-mailovou adresu kamaráda:



C/C++

SDL a šetřič obrazovky

sdl now

28. listopadu 2001, 00.00 | V dalším díle povídání o SDL si předvedeme praktické nasazení a to při tvorbě jednoduchého šetřiče obrazovky. Článek obsahuje i stáhnutelný ukázkový projekt!

A máme tu další díl seriálu o SDL. Poté co jsme SDL nainstalovali si můžeme vyzkoušet něco většího. Tentokrát jsem zaplácl dvě mouchy jednou ranou a udělal opravdu jednoduchý reklamní šetřič obrazovky na svou hru, na kterém vám zároveň ukážu něco málo z SDL unity ;-)). Šetřič zobrazuje nápis, který se postupně cyklicky zviditelňuje a mizí, a vesmírné lodě, které prolétávají ze všech směrů přes obrazovku.

Na začátek vytvoříme nový projekt a do jeho adresáře nahrajeme grafické prvky - soubory bmp. Samozřejmě nastavíme Project\Settings\ C\C++ \Code Generation\Multi-threaded a přidáme do projektu soubory SDL.lib a SDL_main.lib. A pak už můžeme začít tvořit funkci. main.

 

#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <direct.h>
#include "SDL.h"

SDL_Surface *screen,*grafik[5];
char adresar[300];

SDL_Surface *LoadImage(char *soubor,int pruhled)
{
  SDL_Surface *image;
  char cesta[300];
  sprintf(cesta,"SD_SrcSaver\\%s",soubor);
  if ((image=SDL_LoadBMP(cesta))==NULL)
  {
    fprintf(stderr,"Image %s wasn't found\n",cesta);
    exit(0);
  } 
  if (pruhled)
  {
//určí průhlednou barvu obrázku podle prvního pixelu v levo nahoře a barevné hloubky obrázku
    if (image->format->BytesPerPixel==1) SDL_SetColorKey(image, (SDL_SRCCOLORKEY|SDL_RLEACCEL),*(Uint8 *)image->pixels);
    if (image->format->BytesPerPixel==2) SDL_SetColorKey(image, (SDL_SRCCOLORKEY|SDL_RLEACCEL),*(Uint16 *)image->pixels);
    if (image->format->BytesPerPixel==4) SDL_SetColorKey(image, (SDL_SRCCOLORKEY|SDL_RLEACCEL),*(Uint32 *)image->pixels);
  }
  return image;
}

int main(int argc, char *argv[])
{
  SDL_Event event;
  SDL_Rect drect,srect;
  int timer=0,i;
//pokud windows spustí šetřič dávají mu parametr /s, jiné případy náš šetřič neošetřuje a tak raději skončí
  if (argc==1 || strstr(argv[1],"/s")==0) return 0;
//zjistí cestu k programu, tak že oddělí jméno exáče z argv[0], kde je udána cesta i se jménem exáče
  i=strlen(argv[0]); 
  while (argv[0][i-1]!='\\') i--;
  strncpy(adresar,argv[0],i);
//zmeni adresar ,aby se soubory vypisu netrousily po disku
  _chdir(adresar);
//inicializuje grafiku SDL pokud nastane chyba program ji vypíše a ukončí se
  if (SDL_Init(SDL_INIT_VIDEO)< 0 ) {
    fprintf(stderr, "Problem: %s\n", SDL_GetError());
    exit(1);
  }
  atexit(SDL_Quit);
//nastaví rozlišení 800x600 s 32b barevné hloubky
  screen = SDL_SetVideoMode(800, 600, 32,SDL_FULLSCREEN | SDL_HWSURFACE | SDL_DOUBLEBUF);
//skryje se kurzor myši
  SDL_ShowCursor(0);
//nahrají se obrázky
  grafik[0]=LoadImage("pozadi.bmp",0);
  grafik[1]=LoadImage("sdroids.bmp",1);
  grafik[2]=LoadImage("lod1.bmp",1);
  grafik[3]=LoadImage("lod2.bmp",1);
  do{
//vykreslí pozadí
    drect.x=0;drect.y=0;
    drect.w=screen->w;
    drect.h=screen->h;
    SDL_BlitSurface(grafik[0],&drect,screen,&drect);
//nastaví průhlednost
    SDL_SetAlpha(grafik[1],SDL_SRCALPHA | SDL_RLEACCEL | SDL_SRCCOLORKEY,abs(timer++%511-255));
    srect.x=0;srect.y=0;
    srect.w=grafik[1]->w;
    srect.h=grafik[1]->h;
    drect=srect;
    drect.x=(screen->w-grafik[1]->w)/2;
    drect.y=(screen->h-grafik[1]->h)/2;
    SDL_BlitSurface(grafik[1],&srect,screen,&drect);
//prehodí se prední a zadní buffer a tím se obrázek zjeví uživateli
    SDL_Flip(screen);
//opakuje se do stisknutí jakékoliv klávesy, nebo tlačítka myši
 
}while (SDL_PollEvent(&event),event.type!=SDL_KEYDOWN && event.type!=SDL_MOUSEBUTTONDOWN);
  return 1;
}

Tento program už zobrazí pozadí a nápis, a kterého se cyklicky mění průhlednost.  Na začátku programu se otestuje zda byl šetřič spuštěn, pokud totiž windows pouští šetřič v zobrazení přes celou obrazovku přidá mu parametr /s, pokud program tento parametr nedostane, tak ho windows evidentně nevolá, a tak se raději ukončí.

if (argc==1 || strstr(argv[1],"/s")==0) return 0;

Poté program nastaví adresář programu jako aktuální, což zabrání tomu, aby se výpisy stdout.txt a stderr.txt vytvářeli v adresáři, jež má zrovna nastaven windows, tedy vlastně všude možně na disku, a zároveň to umožňuje programu vždy najít cestu ke grafickým datům. Adresář programu zjistíme z argv[0], kde je celá cesta k exe i s jeho jménem. My tedy zjistíme poslední výskyt znaku '\' a řetězec z tohoto místa zkopírujeme do řetězce adresar a pomocí funkce _chdir(char *adresar) nastavíme aktuální adresář. 

if (argc==1 || strstr(argv[1],"/s")==0) return 0;
i=strlen(argv[0]); 
while (argv[0][i-1]!='\\') i--;
strncpy(adresar,argv[0],i);
_chdir(adresar);

Dále inicializuje SDL, nastaví grafiku 800*600 přes celou obrazovku a skryje kurzor myši. Poté se nahrají grafické prvky, u některých prvků se určí klíčová (průhledná) barva, vše pomocí funkce LoadImage.

SDL_Surface *LoadImage(char *soubor,int pruhled)
{
  SDL_Surface *image;
  char cesta[300];
  sprintf(cesta,"SD_SrcSaver\\%s",soubor);
  if ((image=SDL_LoadBMP(cesta))==NULL)
  {
    fprintf(stderr,"Image %s wasn't found\n",cesta);
    exit(0);
  } 
  if (pruhled)
  {
//určí průhlednou barvu obrázku podle prvního pixelu v levo nahoře a barevné hloubky obrázku
    if (image->format->BytesPerPixel==1) SDL_SetColorKey(image, (SDL_SRCCOLORKEY|SDL_RLEACCEL),*(Uint8 *)image->pixels);
    if (image->format->BytesPerPixel==2) SDL_SetColorKey(image, (SDL_SRCCOLORKEY|SDL_RLEACCEL),*(Uint16 *)image->pixels);
    if (image->format->BytesPerPixel==4) SDL_SetColorKey(image, (SDL_SRCCOLORKEY|SDL_RLEACCEL),*(Uint32 *)image->pixels);
  }
  return image;
}

Funkce prvně vytvoří cestu k souboru a pak nahraje soubor bmp do proměnné image, pokud se soubor nenahraje program vypíše chybovou hlášku a ukončí se. Pokud má být obrázek transparentní, tak program pro surface image nastaví klíčovou barvu podle prvního pixelu (tedy toho v rohu vlevo nahoře),. Všimněte si, že se ukazatel void přetypovává na ukazatel Uint8, Uint16 a Uint32 podle počtu bytů k uložení jednoho pixelu.

Teď následuje smyčka, která vykreslí pozadí, pak nápis a nakonec se v ní přehodí buffery (tedy dojde k zobrazení) a vše se opakuje tak dlouho než někdo nestiskne klávesu, nebo tlačítko myši. Zajímavý je řádek

SDL_SetAlpha(grafik[1],SDL_SRCALPHA | SDL_RLEACCEL | SDL_SRCCOLORKEY,abs(timer++%511-255));

Zde se pro surface grafik[1] (nápis) nastaví průhlednost, s akcelerací, s tím že se průhlednost nevyhodnocuje pro klíčovou barvu. Poslední výraz je číslo 0-255, které značí míru průhlednosti 0-plně průhledný, 255-neprůhledný. Zde máme výraz, který si lépe osvětlíme.

abs(timer++%511-255)

prvně dojde ke zvýšení proměnné timer, který se poté upraví na zbytek po dělení 511, tedy číslo v rozsahu 0..510, od něj odečteme 255 a dostaneme číslo -255..255, z toho uděláme absolutní hodnotu a získáme výslednou průhlednost v rozsahu 0..255. Výhodou je, že průhlednost se bude cyklicky měnit a výraz se nám vešel na jednu řádku ;-))). Dále je třeba osvětlit funkci SDL_PollEvent(SDL_event *event), která pokud je ve frontě správ nějaká správa, tak jí vyzvedne a uloží do event a vrací 1, pokud je fronta správ prázdná vrací 0.

Tak a teď by to chtělo do programu přidat ty lodě.

const m_obj=40;  //počet lodí na obrazovce
const double pi=3.141592653;

typedef struct {
double x,y;
int obr;
double uhel;
SDL_Surface *image;
}Tobjekt;

Tobjekt objekty[m_obj];
SDL_Surface *screen,*grafik[5];

//umístí lodě na obrazovku
void Create_Space()
{
  int pos,sx,sy,vz,pom;
  for (pos=0;pos<m_obj;pos++)
  {
    sx=screen->w/4+rand()%(screen->w/2);
    sy=screen->h/4+rand()%(screen->h/2);
    pom=rand()%24;
    objekty[pos].obr=24-(pom+30)%24;
    objekty[pos].uhel=pom*pi*15/180; 
    vz=rand()%2000-1000;
    objekty[pos].x=sx-int(cos(objekty[pos].uhel)*vz);
    objekty[pos].y=sy-int(sin(objekty[pos].uhel)*vz);
    objekty[pos].image=grafik[rand()%2+2];
  }
}

Z tohoto kousku kódu je třeba vysvětlit snad jen generování lodí, to se provádí ve funkci Create_Space. Funkce se spustí na začátku programu a nastaví polohu všech lodí. Poloha se vytváří tak, že se náhodně vytvoří střed pomyslného kruhu pro každou loď jiný, kterým loď prolétne, o souřadnicích sx,sy. Dále se náhodně určí 1 z 24 snímků lodě ,kterým se loď zobrazí (to souvisí s tím, že loď je vyrenderovaná z 3DSMAX v otočení o 360° do 24 snímků, které jsou uloženy v jednom obrázku), a uloží se do proměnné pom. Proměnná pom neodpovídá snímku lodě, který chceme použít, protože snímky v platu obrázků jsou trochu posunuty, proto se upraví a uloží do proměnné obr pro loď. Z proměnné pom se určí též úhel, kterým loď letí. Dále se náhodně určí vzdálenost od středu, aby všechny lodě nepřilétly do prostřed obrazovky zároveň, a pak se nic dlouho zase nedělo, proto zvolíme vzdálenost náhodně a lodě budou přilétat rovnoměrně. Když známe vzdálenost, úhel i střed určíme místo kde se loď nachází. Nakonec určíme jaký obrázek se použije, protože máme dva (lod1.bmp, lod2.bmp).

Dále potřebujeme funkci, která s loďmi pohne.

//pohne se všemi loděmi
void Move_Space()
{
  int pos;
  int sx,sy,pom;

  for (pos=0;pos<m_obj;pos++)
  {
    objekty[pos].x+=cos(objekty[pos].uhel)*2;
    objekty[pos].y+=sin(objekty[pos].uhel)*2;
    if (abs(int(objekty[pos].x)-screen->w/2)>1500 || abs(int(objekty[pos].y)-screen->h/2)>1500)
    {
      sx=screen->w/4+rand()%(screen->w/2);
      sy=screen->h/4+rand()%(screen->h/2);
      pom=rand()%24;
      objekty[pos].obr=24-(pom+30)%24;
      objekty[pos].uhel=float(pom)*pi*15/180; 
      objekty[pos].x=sx-cos(objekty[pos].uhel)*700;
      objekty[pos].y=sy-sin(objekty[pos].uhel)*700;
    }
  }
}

Funkci Move_Space() není třeba víc popisovat stačí jen říct, že funkce pohne všechny lodě podle úhlu a pokud se loď vzdálí od středu obrazovky nad určitou vzdálenost, potom je funkce vytvoří znovu v konstantní vzdálenosti 700 px, tak aby směřovali ke středu obrazovky.

A nakonec zde máme funkci pro vykreslení

//vykreslí všechny lodě
void Draw_Space()
{
  int pos;
  SDL_Rect drect,srect;

  for (pos=0;pos<m_obj;pos++)
  {
    srect.x=objekty[pos].image->h*objekty[pos].obr;
    srect.y=0;
    srect.w=objekty[pos].image->h;
    srect.h=objekty[pos].image->h;
    drect=srect;
    drect.x=int(objekty[pos].x)-objekty[pos].image->h/2;
    drect.y=int(objekty[pos].y)-objekty[pos].image->h/2;
    SDL_BlitSurface(objekty[pos].image,&srect,screen,&drect);
  }
}

Zde stačí jen dodat, že funkce vykresluje tak, aby střed vykresleného obrázku ležel na souřadnicích lodi.

Tak a máme celý šetřič hotový, teď ho stačí jen zkompilovat a přejmenovat ho na SD_SrcSaver.scr a nahrát i s adresářem SD_SrcSaver do adresáře Windows\System\.

Zde je celý příklad ke stažení tutorial2.zip (300KB)

Obsah seriálu (více o seriálu):

Tématické zařazení:

 » Rubriky  » C/C++  

 

 

 

Nejčtenější články
Nejlépe hodnocené články

 

Přihlášení k mému účtu

Uživatelské jméno:

Heslo: