SDL Nedir?

SDL Nedir?

SDL, Simple DirectMedia Layer yani basit direkmedya katmanıdır. Biraz motamot bir çeviri oldu. Anlaşılır bir ifade ile SDL ses, klavye, mouse, joystick, 3d donanımı ve 2d grafik çizimi için hazırlanmış platform bağımsız bir çokluortam (multimedia) kütüphanesidir. Mpeg oynatıcılarda, Emulatorlerde, birçok popüler oyunda ve Linux'a port edilen birçok windows oyununda kullanılmaktadır.

SDL Linux, Windows, BeOS, MacOS Classic, MacOS X, FreeBSD, OpenBSD, BSD/OS, Solaris, IRIX, ve QNX ortamlarını destekler. Ayrıca resmi olmasada kod açık olduğu için gönüllü çalışmalar sonucunda Windows CE, AmigaOS, Dreamcast, Atari, NetBSD, AIX, OSF/Tru64, RISC OS, ve SymbianOS üzerinde de çalışması sağlanmıştır.

SDL C dili ile yazılmış olmasına rağmen C++ ise doğal hali ile çalışmakta ve Ada, Eiffel, Java, Lua, ML, Perl, PHP, Pike, Python, ve Ruby gibi birçok dil içinde uyarlamaları bulunmaktadır.
----------------------

SDL Kurulum

Bu konu aslında basit olmasına rağmen yeni başlayanları biraz zorlayabilir. Yinede elimden geldiğince her platform'da SDL'i nasıl kurup kullanmaya hazır hale getireceğimizi yazmaya çalışacağım.


Windows:
Windows ortamında en yaygın olarak MS Visual Studio, Borland C++ Builder ve Dev-C++ IDE'leri kullanılmaktadır. Ama ben daha önce hiç C++ Builder kullanmadığım ve internettede C++ Builder için yazılmış bir kurulum yazısı bulamadığım için size tahmini bilgi verebileceğim, gerisi size kalmış. Yinede pek sorun yaşayacağınızı sanmıyorum.

Visual Studio:
Ilk olarak MS'in IDE'si Visual Studio ile başlayalım. Ben Dev-C++ kullandığım için bu IDE hakkında pek bilgim yok ama internette yayınlanmış ingilizce derslerden topladığım bilgi ile yardımcı olmaya çalışacağım.

1- http://www.libsdl.org/download-1.2.php adresinden download bölümünen Development Libraries başlığı altındaki Visual C++ için olan dosyayı (SDL-devel-1.2.8-VC6.zip gibi bir adı olması lazım) indirin.
2- Sıkıştırılmış dosyayı açın. Zip dosyasının içinden çıkan dosyalar arasında iki tane önemli klasör var. Bunlar "include" ve "lib" klasörleri. "lib" klasörünün içinden çıkanları C:\Program Files\Microsoft Visual Studio\VC98\Lib (büyük ihtimalle sizin bilgisayarınızdada aynı klasördür ama
eğer MS VC++'ı kurduğunuz yer farklı ise bu yoluda ona göre değiştirin.) klasörü altına kopyalayın. "include" klasörü içindeki dosyalarıda C:\Program Files\Microsoft Visual Studio\VC98\Include\SDL (diğeri ile aynı şekilde farklı bir klasör olabilir sizin ki, artık duruma göre değişikliği siz yaparsınız. Birde include'un altında SDL klasörü yok onu sizin yaratmanız gerek.) klasörü altına kopyalayın.
3- Şimdi Visual C++'ı çalıştırın ve yeni bir proje yaratın. "Win32 Application" seçeneğini ve "empty project" seçeneğini seçin. File menüsündeki New seçeneği ile yeni bir c++ kaynak dosyası (c++ source file) yaratın ve adını main.cpp koyun.
4- Daha sonra proje ayarları (project settings) (project->settings menüsü yolu ile) bölümüne gidin. LINK tabına tıklayın listelenmiş diğer lib dosyalarının altına sdl.lib ve sdlmain.lib dosyalarını ekleyin.
5- Ardından yine proje ayarları bölümünde C/C++ tabına tıklayın. Drop-down menüden "Code Generation" seçeneğini seçin. Ardından da 'Use run-time library' drop-down menüsünden 'Multithreaded DLL' seçeneğini seçin.
6- Son birşey daha. Sıkıştırılmış dosyanın içinde son bir önemli dosya bulunmakta. SDL.DLL dosyası. Bu dosyayı alıp ister işletim sisteminizin system klasörüne ( win 95, 98, ME için c:\windows\system klasörü ya da windows NT, 2000 and XP için c:\windows\system32 klasörü), ister uygulamanızın exe dosyasının çalışacağı klasöre kopyalayın. Eğer DLL dosyasını system klasörüne kopyalarsanız yapacağınız her sdl programı için dll dosyasını baştan baştan kopyalamanıza gerek kalmaz. Aksi takdirde her programın klasörüne koymanız gerekir. Ayrıca programlarınızı dağıtırken de bu dll dosyasını programınız ile vermeniz gerekiyor. Aksi halde yaptığınız program başka bilgisayarlarda çalışmaz. MS Visual Studio ile işimiz bu kadar bütün bu adımları gerçekleştirdikten sonra MS VC SDL ile program geliştirmeye hazır hale geliyor.

Dev-Cpp:
Dev-C++ için SDL yüklemenin iki yolu var. Birincisi http://www.devpaks.org sitesine gidip sdl için gerekli devpak dosyasını indirmek ve Dev-C++ içerisindeki Package Manager ile bu devpak dosyasını yüklemek. Bu sayede hem sdl kütüphanesi sorunsuzca bilgisayarınıza yüklenecek hem de elinizin altında hızla program yazmaya başlamanız için hazır bir sdl kodu olacak. Ama ne yazık ki hazır sdl kodu içerisinde birkaç hata bulunmakta yinede bunları düzeltmek oldukça kolay. Işinizi daha da kolaylaştırmak için ben düzeltilmiş versiyonu sizin için hazırladım, buyrun. Diğer yol ise yukarıdaki gibi geliştirme paketini indirip her dosyayı yerine kurmak. Bu yolu izlerken yapmamız
gerekenler;

1- http://www.libsdl.org/download-1.2.php adresinden download bölümünen Development Libraries başlığı altındaki MinGW32 için olan dosyayı (SDL-devel-1.2.8-mingw32.tar.gz gibi bir adı olması lazım) indirin.
2- Sıkıştırılmış dosyayı açın. Zip dosyasının içinden çıkan dosyalar arasında iki tane önemli klasör var. Bunlar "include" ve "lib" klasörleri. "lib" klasörünün içinden çıkanları C:\Dev-Cpp\lib klasörü altına kopyalayın. "include" klasörü içindeki dosyalarıda C:\Dev-Cpp\include\SDL (include'un altında SDL klasörü yok onu sizin yaratmanız gerek.) klasörü altına kopyalayın.
3- Sıkıştırılmış dosyanın içinde son bir önemli dosya bulunmakta. SDL.DLL dosyası. Bu dosyayı alıp ister işletim sisteminizin system klasörüne ( win 95, 98, ME için c:\windows\system klasörü ya da windows NT, 2000 and XP için c:\windows\system32 klasörü), ister uygulamanızın exe dosyasının çalışacağı klasöre kopyalayın. Eğer DLL dosyasını system klasörüne kopyalarsanız yapacağınız her sdl programı için dll dosyasını baştan baştan kopyalamanıza gerek kalmaz. Ayrıca SDL.DLL dosyasını c:\Dev-Cpp\dll klasörünün altına da kopyalayın. 
4- Dev-Cpp'ı çalıştırın. New Project butonu ile yeni bir proje açın. Eğer Devpak'ı yüklediyseniz proje şablonları arasında Multimedia tabı altında SDL için hazır bir şablon hazır bulunmakta. Verdiğim dosya ile bu şablondaki dosyayı değiştirdiyseniz bu şablon sorunsuzca çalışacaktır. Bu şablon üzerinden dilediğinizce SDL uygulamalarınızı geliştirebilirsiniz. Ama eğer Devpak ile değilde SDL'in sitesinden indirdiğiniz sıkıştırılmış dosyadan kurduysanız proje ayarlarını elle yapmalısınız. Ilk olarak yeni bir proje açın (New Project). Ardından Project Browser'da çıkan projenizin adına sağ tuşla tıklayın ve Project Options'ı seçin. Burda ilk olarak Type bölümünden Win32 GUI seçeneğini seçin. Eğer Win32 Console'u seçerseniz SDL programınız her çalıştığında önce dos penceresi açılır. Ardından programınız çalışır. Devamında aynı pencerede Parameters tabını açın. Compiler başlığı altına -I"<INCLUDE>\SDL" -Dmain=SDL_main parametrelerini girin. Linker başlığı altına ise -lmingw32 -lSDLmain -lSDL parametrelerini girin. Ayarlamalar bu kadar. Isterseniz şimdi projenizi kaydedin ve SDL projesine başlayacakken ayarları hazır olduğundan bu proje üzerinden başlayın. Geriye bir tek kod yazmanız kaldı. O da sonraki bölümlerde anlatılacak.

Mingw32:
Dev-Cpp'ta kendi içerisinde Mingw32 derleyicisi kullandığı için aşağı yukarı aynı ayarlar geçerli. Uzun uzadıya anlatmayacağım. Yukarıdaki açıklamalar yardımı ile sorunsuzca ayarlamaları yapabilirsiniz diye düşünüyorum. 

Linux:
Daha sonra ekleyeceğim.


SDL'e giriş
SDL öğrenmesi oldukça kolay bir kütüphanedir. Bu nedenle hızla kod yazmaya girişeceğim. Kısa sürede öğreneceksiniz zaten. SDL 8 alt sistemin bileşiminden oluşmaktadır. Bunlar Ses(Audio), CDROM, Olay yönetimi(Event Handling), Dosya G/Ç(File I/O), Joystick yönetimi(Joystick Handling), Çoklu Görev(Threading), Zamanlayıcı(Timers) ve Grafik(Video) 'dur. Bu alt sistemleri kullanmak için ik önce bu sistemleri çalıştırmanız gerekir. Bunun için iki komut bulunmaktadır. Bunlar SDL_Init ve SDL_InitSubSystem dir. SDL_Init bütün SDL kodlarından önce çalıştırılmalıdır. Bu komut SDL sistemini çalıştırmaya başlar. SDL_InitSubSystem ise çalışma anında istediğiniz alt sistemin çalışmasını sağlar. Kullanımları şu şekildedir:

SDL_Init ( SDL_INIT_VIDEO );

Bu komutla SDL programı çalıştırılır ve SDL'in video alt sistemi aktif hale getirilir.

SDL_InitSubSystem ( SDL_INIT_AUDIO | SDL_INIT_TIMER );

Bu komutla çalışmakta olan SDL programında ses ve zamanlayıcı alt sistemleri aktif hale getirildi. | işareti ile aynı anda birden fazla SDL alt sistemini seçebiliriz. Bu yönetmi SDL_Init komutu ile de kullanabiliriz.

Alt sistem bayrakları(flag) listesi:
SDL_INIT_TIMER - Zamanlayıcı -
SDL_INIT_AUDIO - ses -
SDL_INIT_VIDEO - grafik -
SDL_INIT_CDROM - cdrom -
SDL_INIT_JOYSTICK - joystick -
SDL_INIT_EVERYTHING - Bütün sistemleri aktif hale getirir -
SDL_INIT_NOPARACHUTE - SDL'in hata sinyallerini yakalamasını önler -
SDL_INIT_EVENTTHREAD - çok görevlilik -

Alt sistemleri çalıştırmayı öğrendik ama ya çalışan sistemleri kapatmayı? Şimdi de çalıştırdığımız SDL programını ve alt sistemleri nasıl kapatacağımızı öğreneceğiz.

Ilk komutumuz SDL_Quit. Bu komut SDL_Init komutunun yaptığı işin tam tersini yapar ve başlattığınız SDL programını kapatır. Bu programı kullanırken herhangi bir argüman girmenize gerek yoktur. Kullanışı:

SDL_Quit();

şeklindedir. Bu komut ile hali hazırda çalışan bütün SDL alt sistemleri ve SDL programı kapanır. Diğer komutumuz ise SDL_QuitSubSystem. Bu komut ile çalışmakta olan istediğimiz alt sistemi kapatabiliriz. Kullanımı:

SDL_QuitSubSystem ( SDL_INIT_TIMER );

şeklindedir. Örneğimizde zamanlayıcı alt sistemini kapattık ama SDL programımız çalışmaya devam etti. Ayrıca SDL_WasInit fonksiyonu ile istediğiniz alt sistemin yüklü olup olmadığını kontrol edebilirsiniz. Bu fonksiyonunun yazılışı şöyledir:

if(SDL_WasInit(SDL_INIT_VIDEO)!=0)
    printf("Video alt sistemi yüklü.\n");
else
    printf("Video alt sistemi yüklü değil.\n"); 

Şimdi öğrendiklerimizle örnek bir SDL programı yazalım.

Örnek program:

#include "SDL.h" /* SDL header dosyası. Bütün SDL programları buna ihtiyaç duyar */
#include <stdio.h>

int main() {

    printf("SDL programı başlatılıyor.\n");

    /* SDL programı başlatılıp Video ve Ses sistemleri aktif hale getiriliyor */
    if ((SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) == -1)) {
        fprintf(stderr, "SDL programı başlatılamadı: %s.\n", SDL_GetError());
        exit(-1);
    }

    fprintf(stdout, "SDL programı başlatılamadı.\n");

    fprintf(stdout, "SDL programı kapatılıyor.\n");

    /* SDL programı ve bütün alt sistemleri kapatılıyor */
    SDL_Quit();

    fprintf(stdout, "Kapatılıyor....\n");

    exit(0);
}

SDL ve Grafik

Sırada grafik komutlarını kullanmayı öğrenmek var. Ilk olarak yapmamız gereken şey video alt sistemini aktif hale getirmek. Ardından bir yüzey (surface) tanımlamalı ve SDL_SetVideoMode komutu ile bu yüzeyi kullanarak istediğimiz çözünürlükte bir pencere yaratmamız gerekir.
Ilk olarak yüzey terimini açıklamak istiyorum. SDL'de ekrana çizdirmek istediğiniz bilgiler bir yüzeyde saklanıır. Bu yüzeyler aslında önceden tanımlanmış yapılardır (struct). Bir yüzeyi şöyle tanımlayabiliriz:

SDL_Surface *screen;

Içeriği ise şöyledir:
typedef struct SDL_Surface { Uint32 flags; /* Salt okunur */ SDL_PixelFormat *format; /* Salt okunur */ int w, h; /* Salt okunur */ Uint16 pitch; /* Salt okunur */ void *pixels; /* oku-yaz */ /* kırpma bilgisi */ SDL_Rect clip_rect; /* Salt okunur */ /* Referans sayacı -- yüzey boşaltılırken kullanılır */ int refcount; /* çoğunlukla okunur */ /* Bu yapı aynı zamanda burda gösterilmeyen bazı özel alanlara sahiptir */ } SDL_Surface;
Gördüğünüz gibi SDL_Surface yapısı ile uğraşırken sadece pixels değişkenini kullanabilirsiniz. Bu değişkende yüzeyin her pixel'inin renk bilgisini taşıyor ve isterseniz her pixel'i teker teker değiştirebilirsiniz. Bu konuya ileride daha detaylı olarak bakacağız ama şimdi devam edelim.
Programın başında ekrana yansıtacağımız görüntüler için bir yüzey tanımlarız ve bu yüzey ile bir pencere açarız. Bunu SDL_SetVideoMode komutu ile yaparız. Kullanımı aşadığaki gibidir.

screen = SDL_SetVideoMode(640, 480, 8, SDL_SWSURFACE);

Bu komut ile screen yüzeyini ekrana yansıtacağımız ana yüzey olarak tanımlar ve 640'a 480 pixel çözünürlükte bir pencere yaratırız. Ekrandaki bit başına düşen pixel sayısı 8 olur. Ve SDL_SWSURFACE bayrağı ile screen yüzeyine ait verilerin sistem belleğinde tutulması sağlanır. Burada kullanmak için birçok farklı bayrak bulunmaktadır. SDL_SetVideoMode komutu ile kullanabileceğimiz bayrakların listesi:

SDL_SWSURFACE -> yüzeye ait bilgilerin sistem belleğinde tutulmasını sağlar.
SDL_HWSURFACE -> yüzeye ait bilgilerin ekran kartının belleğinde tutulmasını sağlar.
SDL_ASYNCBLIT -> asenkron yüzey göstermeyi aktif hale getirir. Bu genellikle tek işlemcili makinalarda bit işlemeyi (blit - bit block transfer - bit bloğu değişimi) yavaşlatır ama SMP sistemlerde hız artışı sağlayabilir.
SDL_ANYFORMAT -> Normalde eğer video yüzeyi kullanılamayacak bir ekran derinliği (bpp) isterse SDL gölge bir yüzey ile bunu emule eder. SDL_ANYFORMAT bayrağı ile SDL'in bunu yapması engellenir ve SDL'in yüzeyin derinliğini umursamadan onu kullanması sağlanır.
SDL_HWPALETTE -> SDL'e ayrıcalıklı palet erişimi verir. Bu bayrak olmadan SDL_SetColors komutu ile istediğiniz renge herzaman ulaşamayabilirsiniz.
SDL_DOUBLEBUF -> Çifte tamponlamayı etkin hale getirir. Sadece SDL_HWSURFACE bayrağı ile beraber kullanılabilir.
SDL_Flip komutu tamponların içeriğini değiştirir ve ekranı tazeler. Eğer çifte tamponlama
etkinleştirilmemişse SDL_Flip bütün ekran üzerine SDL_UpdateRect komutu uygulanmış gibi davranır.
SDL_FULLSCREEN -> SDL tam ekran çalıştırmaya çalışıyor.
SDL_OPENGL -> OpenGL render ekranı yaratır. SDL_GL_SetAttribute komutu ile OpenGL ayarlamalarına başlamadan önce bu bayrağın etkinleştirilmesi gerekir.
SDL_OPENGLBLIT -> Üstteki gibidir ama aynı zamanda blitting (*yardım*) işlemlerine izin verir.
SDL_RESIZABLE -> Boyutlandırılabilir bir pencere yaratır. Pencere boyutları değiştirildiği zaman SDL_VIDEORESIZE
olayı tetiklenir ve SDL_SetVideoMode yeni boyut ile tekrar çağırılabilir.
SDL_NOFRAME -> Mümkün ise çerçevesiz bir pencere yaratır. Tam ekran modu otomatik olarak bu bayrağı etkinleştirir.

Eğer istediğiniz ekran modunun uygun olup olmadığını öğrenmek istiyorsanız SDL_VideoModeOK fonksiyonunu kullanabilirsiniz. Yazılışı:

if (!SDL_VideoModeOK(640, 480, 16, SDL_HWSURFACE))
printf("Ekran modu uygun değil.\n");
else
printf("Ekran modu uygun.\n");


şeklindedir. Bunun dışında SDL_GetVideoInfo, SDL_GetVideoSurface, SDL_GetVideoDriverName ve SDL_ListModes gidi fonksiyonlarda bulunmakta ama şimdilik işin başında olduğunuz için işin başındaki sizlerin ihtiyaç duymadığı fonksiyonlar. Bunlara ileride detaylı değineceğiz ama şimdi sadece başka fonksiyonlarında olduğunu bilin yeter.

Şu ana kadar öğrendiklerimizle bir SDL programını başlatıp SDL penceresini açabiliyoruz. Ama karşımızdaki simsiyah pencere oldukça sıkıcı değil mi? Hadi ortamı biraz renklendirelim. Ilk olarak oldukça basit olduğu için bir BMP dosyayını okuyup ekrana yazdırmayı göstereceğim.

Bunun için ilk olarak okuyacağımız BMP dosyasının içeriğini saklayacağımız bir yüzey oluşturmalıyız. Yüzeyler SDL'de resim bilgisi saklanılacağı heryerde kullanılır.

SDL_Surface *image;

SDL'in kendi içerisinde BMP uzantılı dosyaları okuyup hafızaya alan hazır bir fonksiyonu bulunmakta. Adı SDL_LoadBMP . Kullanışı:

image=SDL_LoadBMP("c:\a.bmp");

şeklindedir. Bu sayede image adlı yüzeye a.bmp dosyasını yüklemiş bulunmaktayız. Şimdi sıra bu resmi ekrana çizdirmekte. Bunun içinse SDL_BlitSurface fonksiyonunu kullanacağız.

SDL_BlitSurface(image, NULL, screen, NULL);

Bu komut ile image yüzeyindeki resmi screen yüzeyine yani ekrandaki görüntünün saklanacağı yüzeye çizdiririz. NULL değer verilen parametrelerde çizdirilecek yüzeylerin boyutları ve koordinatları belirlenir. Eğer iki parametreyede NULL girersek image yüzeyinin tamamı screen yüzeyinin 0,0 noktasından başlayarak çizdirilir. Eğer resmin belirli bir kısmını çizdirmek istersek veya ekranda 0,0 noktasından başka bir noktaya koymak istersek ne yapacağız? SDL_Rect kullanarak. SDL_Rect SDL içerisinde kare alan tanımlamak için kullanılan yapıdır. Içeriğinde sadece enine ve boyuna uzunluğu ile x,y düzlemlerindeki koordinatlarını saklayan değişkenler bulunur.

typedef struct{
Sint16 x, y;
Uint16 w, h;
} SDL_Rect;

x ve y koordinatları üst sol köşenin koordinatlarıdır. w ve h ise genişlik ve uzunluğudur. Oldukça basit ama niye böyle bir yapıya ihtiyacımız var diye düşünebilirsiniz. Bu yapıya ihtiyacımız var çünkü yüzeylerdeki resim alanları aslen dikdörtgen ve bunları kırpmak ya da belirli koordinatlara yerleştirmek isterseniz bu dikdörtgen yapısı oldukça kullanışlı oluyor. Yapmamız gereken şu:

SDL_Rect dikdortgen;

şeklinde tanımlamayılız. Eğer amacımız resmimini belirli bir koordinata koymak ise şu yönetmi kullanmalıyız:

SDL_Rect hedef;
hedef.x = x; // resmi koymak istediğimiz noktanın x koordinatı
hedef.y = y; // resmi koymak istediğimiz noktanın y koordinatı
SDL_BlitSurface(image, NULL, screen, &hedef);

Ama eğer amacımız resmin sadece bir bölümünü çizdirmek ise bu yönetmi kullanmalıyız:

SDL_Rect dortgen1,dortgen2;

dortgen1.x = x; // resmin ekran üzerine yerleştirileceği x noktası
dortgen1.y = y; // resmin ekran üzerine yerleştirileceği y noktası
dortgen1.w = w; // resmin ekran üzerine çizilecek genişliği
dortgen1.h = h; // resmin ekran üzerine çizilecek uzunluğu

dortgen2.x = x2; // resmin çizilirken x düzlemindeki başlangıç noktası
dortgen2.y = y2; // resmin çizilirken y düzlemindeki başlangıç noktası

SDL_BlitSurface(image, &dortgen2, screen, &dortgen1);

Peki ya ekrandaki görüntünün bir kısmını bir yüzeye aktarmak istersek? Onu da bu yöntem ile yapabilirsiniz:

SDL_Rect dortgen1,dortgen2;

dortgen1.x = x1; // ekrandan kopyalanacak parçanın sol üst noktasının x koordinatı
dortgen1.y = y1; // ekrandan kopyalanacak parçanın sol üst noktasının y koordinatı
dortgen1.w = x2; // ekrandan kopyalanacak parçanın sağ alt noktasının x koordinatı
dortgen1.h = y2; // ekrandan kopyalanacak parçanın sağ alt noktasının y koordinatı

dortgen2.x = x1; // yukarıdakinin aynısı
dortgen2.y = y1; // yukarıdakinin aynısı

SDL_BlitSurface(screen, &dortgen2, temp, &dortgen1);

Bunun dışında SDL_Rect kullanarak ekrana bir dikdörtgen çizdirmenizde mümkün. Bunun için bir SDL_Rect tanımlıyorsunuz. Bu dörtgeni yerleştireceğiniz x ve y koordinatlarını, dörtgenin genişliği ile uzunluğunu ve rengini belirledikten sonra SDL_FillRect fonksiyonu ile ekrana istediğiniz koordinata istediğiniz boyutlarda ve istediğiniz renkte bir dörtgen çiziyor. Kod şöyle :

Uint32 renk; // dörtgenimizin renk değeri
SDL_Rect dortgen;
dortgen.x = x; // dörtgeni ekran üzerinde yerleştireceğimiz x noktası
dortgen.y = y; // dörtgeni ekran üzerinde yerleştireceğimiz y noktası
dortgen.w = w; // dörtgenimizin genişliği
dortgen.h = h; // dörtgenimizin uzunluğu
SDL_FillRect (screen, &dortgen, renk);

Oldukça kolay değil mi? Sanırım rengi nasıl belirttiğimizi merak ediyorsunuz. Ama şimdilik renk konularına girmeyeceğim ama ileride (az ilerde ) detaylı olarak anlatacağım. Şimdi ise size ekran tazeleme fonksiyonlarını anlatacağım. 2D grafik programlamasında ekrana çizdireceğimiz görüntüleri önce çizdirmek sonra ekranı tazelemek ve sonra tekrar çizdirmek gerekir. Tazelemezsek ne olacağını basit bir örnek ile açıklayayım. Diyelim ki arkaplanda tam ekran çalışmakta olan bir penceredeki uygulamanız kilitlendi. Onun önündeki daha küçük bir pencerede çalışan uygulamanızın penceresini taşırsanır fark edeceğiniz üzere küçük pencerenin eski bulunduğu yerde görüntüsü (en azından görüntüsünün bir kısmı) hala durmakta. Işte ekrana çizim yaptıktan sonra ekranı tazelemezsek bu ve buna benzer bir sonuç alırız. Peki ekranı nasıl temizleyeceğiz? Bunun iki yolu bulunmakta. Birincisi SDL_UpdateRect fonksiyonu ile.

SDL_UpdateRect(screen, 0, 0, image->w, image->h);

Ekrandaki görüntüyü sakladığımız screen yüzeyinin 0,0 koordinatından ekrana çizdireceğimiz image yüzeyinin genişliği ve yüksekliği boyunca uzanan alanı tazele komutudur bu. Bunun yerine bütün çizim işlemini bitirince ekranın tamamını tazeleyecek bir SDL_UpdateRect komutu daha kullanışlı olabilir. Şöyle ki :

SDL_UpdateRect(screen, 0, 0, 0, 0);

Ekranı tazelemek için kullanabileceğimiz bir diğer yöntem ise SDL_Flip fonksiyonudur. Bu fonksiyon sadece Video modu seçilirken çifte tamponlama ( Double Buffering) bayrağı (SDL_DOUBLEBUF) seçilmiş ise kullanılabilir. Çünkü bu komut tamponların değişmesini sağlamak yolu ile ekranı tazeler. Eğer çifte tamponlama özelliğini kullanamıyorsanız bu komut yerine yukarıdaki bütün ekranı tazeleyen SDL_UpdateRect(screen,0,0,0,0); komutunu kullanın. Ama imkanınız varsa SDL_Flip'i seçmeye gayret edin. Kullanımı:

SDL_Flip(screen);

Bunun dışında birde SDL_UpdateRect fonksiyonunun SDL_UpdateRects adında birden fazla dörtgeni aynı anda tazeleyen farklı bir versiyonuda bulunmaktadır. Bu fonksiyonun kullanımı ise şöyledir:

SDL_UpdateRects(screen, dortgensayisi, *dortgenler);

Şimdi ise birkaç pixel fonksiyonu göreceğiz. Surface'lerin yapısını tanıtırken sadece pixels değişkeninin değiştirilebilir olduğu belirtilmişti. Bu değişkende yüzeyin pixellerinin renk bilgisi saklanmaktadır. Bu değişken dizisinin değerleri değiştirilebilir ve bu değişiklikler sayesinde ekrandaki pixellerin rengi değiştirilir. Bu işi yapan basit iki pixel fonksiyonu yazalım. Biri seçtiğimiz yüzeye istediğimiz renkte bir pixel yerleştirmeye diğeride seçtiğimiz yüzeydeki istediğimiz pixel'in renk değerini almamıza yarayacak.

Uint32 getpixel(SDL_Surface *surface, int x, int y)
{
    int bpp = surface->format->BytesPerPixel;
    /* p renk değerini almak istediğimiz pixel'in adresi */
    Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
    switch (bpp)
    {
    case 1:
        return *p;
    case 2:
        return *(Uint16 *)p;
    case 3:
        if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
            return p[0] << 16 | p[1] << 8 | p[2];
        else
            return p[0] | p[1] << 8 | p[2] << 16;
    case 4:
        return *(Uint32 *)p;
    default:
        return 0; /* Bu sonuç çıkmaz ama ne olur ne olmaz. */
    }
}


Pixel dizisinde koordinatlar ilk 0,0 koordinatından başlar ilk elemanı ve her elemanda ilk olarak x değeri büyür. Bu büyüme pixel başına düşen bit kadar olur. Pixel başına 1 bit düşüyorsa her bit ayrı bir koordinattır. Ama 3 bit düşüyorsa 3 bitte bir koordinatları bir ileri gider. x koordinatı değeri limitine ulaştığında sıfırlanır ve y değeri bir artar.

Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;

Satırında yaptığımız işlemde x değerini bpp değişkeni ile çarpıyoruz. bpp değişkeninde yüzeyin pixel başına düşen byte sayısını saklıyor. Yukarıda dizinin bir sonraki elemanının koordinat sisteminde ki bir sonraki noktanın rengini saklar demiştik ama eğer yüzeyde pixel başına düşen byte sayısı 1 ise doğrudur. Ama bazı durumlarda pixel başına düşen byte sayısı 2 veya 3'e çıkabilir. Mesela RGB renk paleti kullanıldığında her koordinat için ilk byte kırmızı renk değeri, ikincisi yeşil ve üçüncüsü mavi renk değeridir. RGBA renk paletinde ise ilk üçün RGB gibi dördüncüsü ise alfa değeridir. Işte bu yüzden x değerini bpp ile çarptık. Picth değişkeninde ise yüzeyin bir satırının uzunluğu yani tutuluyor. y değeri için bir demek x koordinatının limiti kadar gitmiş olmak ve bir nokta
daha ileri gitmek demektir.

void putpixel(SDL_Surface *surface, int x, int y, Uint32 pixel)
{
    int bpp = surface->format->BytesPerPixel;
    /* p yerleştirmek istediğimiz pixel'in adresi */
    Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;

    switch (bpp) {
    case 1:
        *p = pixel;
        break;

    case 2:
        *(Uint16 *)p = pixel;
        break;

    case 3:
        if (SDL_BYTEORDER == SDL_BIG_ENDIAN) {
            p[0] = (pixel >> 16) & 0xff;
            p[1] = (pixel >> 8) & 0xff;
            p[2] = pixel & 0xff;
        }
        else {
            p[0] = pixel & 0xff;
            p[1] = (pixel >> 8) & 0xff;
            p[2] = (pixel >> 16) & 0xff;
        }
        break;

    case 4:
        *(Uint32 *)p = pixel;
        break;
    }
}


getpixel fonksiyonuna oldukça benziyor. getpixel fonksiyonunda hedef pixel'in adresini bulup ordaki değerleri alıyorduk, burda ise yine hedef pixel'in adresini buluyoruz, ardından hedef pixel'in değerlerini istediğimiz renk değeri ile değiştiriyoruz. Dikkat edilecek nokta ise renk değerini değiştirirken pixel başına düşen bit sayısına göre hesaplama yapıyoruz. Oldukça basit ama kullanışlı.

Bu arada bu pixel fonksiyonlarını kullanırken veya bir yüzeyin pixel verilerilerine direk ulaşırken ilk olarak üzerinde çalışacağınız yüzeyi kilitlemelisiniz. Açıkcası dalgınlık ve merak ile kilitlemeden de çalıştırdığım oldu. Bunun nedeni ise bütün yüzeylerin kilitlenmeye ihtiyacı olmamasıdır. Neden diye sormayın bilmiyorum. Ama öyle. Peki bunu nasıl anlayacaksınız? SDL_MUSTLOCK fonksiyonu ile. En iyisi size bunu yapan bir kod ile açıklamak.

if (SDL_MUSTLOCK(screen)) {
    if (SDL_LockSurface(screen) < 0) {
        fprintf(stderr, "Yuzey kilitlenemiyor: %s\n", SDL_GetError());
        return;
    }
}


Burda ilk olarak screen yüzeyini SDL_MUSTLOCK fonksiyonu ile kontrol eder ve kilitlenmeyi gerektirip gerektirmediğine bakarız. 0 değeri dönerse istediğiniz zaman istediğiniz pixele veri yazabilir, istediğiniz pixelden veri okuyabilirsiniz. Ama 0 verisi dönmezse bu yüzeyi kilitlemeniz gerekir. Böyle oluncada devreye SDL_LockSurface fonksiyonu devreye girer ve parametre olarak girilen yüzey kilitlenir ama bir sorun olurda kilitlenemezse fprintf fonksiyonu ile stderr dosyasına "Yüzey kilitlenemiyor: Hata mesajı" şeklinde bir hata bildirimi yazdırırız. Bu arada SDL 1.1.8 'den beri yüzey kilitlemek rekürsif, yani ardarda istediğiniz kadar kilit atabilirsiniz bir yüzeye ama bu gibi durumlardada attığınız her kilit için yüzeyi bir kez daha açmanız gerekir. Peki kilitlenen yüzeyi nasıl açacağız? SDL_UnLockSurface komutu ile. Kullanımı şöyledir:

SDL_UnlockSurface(screen);

Ama SDL_MUSTLOCK fonksiyonu ile beraber kullanmak istiyorsanız -ki tavsiye ederim- şöyle olacak:

if ( SDL_MUSTLOCK(screen) ) {
SDL_UnlockSurface(screen);
}

Bu kadar basit.
Ilk ders için bu kadarı yeterli. Şu noktaya kadar anlattıklarım ile SDL programları yazmaya başlayabilirsiniz.

--------------------------------

SDL ve Grafik II

Hala grafik konusundayız ama temel konularda fazla birşey kalmadı. Sadece birkaç grafik fonksiyonu ve birkaç yardımcı fonksiyonu anlatacağım. Ardından Event'lara geçeceğiz. Ilk olarak SDL_MapRGB fonksiyonundan bahsedeceğim. Bu fonksiyon girdiğiniz parametreler ile RGB cinsinden belirttiğiniz renk değerini Uint32 tipi bir değişkene atar. Bu değişkenin birde SDL_MapRGBA versiyonu bulunmaktadır. Kullanımı aynıdır. Tek fark RGBA değerini girmeniz gerekir. Kullanımları;

Uint32 renk;
renk = SDL_MapRGB (surface->format, kirmizi, yesil, mavi);

Uint32 renk;
renk = SDL_MapRGB (surface->format, kirmizi, yesil, mavi, alfa);

surface olarak renk paletini kullanacağınız bir yüzeyi ya da en basiti ekrana çizdirdiğiniz yüzeyi kullanın. Renk değeri seçtiğiniz değer yüzeyin pixel formatında belirtilen palet değerlerine göre belirlenir. Pixel format'ı yüzeylerin pixel verilerinin biçiminin saklandığı veri yapısıdır.

typedef struct{
    SDL_Palette *palette; // renk paleti
    Uint8 BitsPerPixel; // pixel başına düşen bit sayısı (8,16,24,32)
    Uint8 BytesPerPixel; // pixel başına düşen byte sayısı (1,3,4)
    Uint32 Rmask, Gmask, Bmask, Amask; /* her renk elementinin tek başına renk değerlerini getirmek için kullanılan
                                       maske değeri */
    Uint8 Rshift, Gshift, Bshift, Ashift; // sola kaydırma değeri gibi bişey önemli değil pek
    Uint8 Rloss, Gloss, Bloss, Aloss; // bu da veri kaybı değeri gibi birşey
    Uint32 colorkey; // transparan olacak rengin değeri
    Uint8 alpha; // bütün yüzeyin alfa değeri
} SDL_PixelFormat;


Pixel formatının içeriğinde colorkey adlı değişkeni ve transparan bir renk sağladığını gördük. Ama bunu nasıl yapabileceğinizi anlatmadım. Şimdi anlatacağım. Bunun yolu SDL_SetColorKey fonksiyonundan geçmekte. Yazılışı;

int SDL_SetColorKey(SDL_Surface *yuzey, Uint32 bayrak, Uint32 maskelenecek_renk);

kullanımı;

SDL_SetColorKey(yuzey, SDL_SRCCOLORKEY,SDL_MapRGB(yuzey->format, r, g, b));

Örnekte de gördüğünüz gibi ilk olarak renk anahtarı belirlemek istediğimiz yüzeyi, ardından da işlem bayrağını giriyoruz, en son olarakta transparan olmasını istediğimiz rengi giriyoruz. Bayrak olarak SDL_SRCCOLORKEY değerini vererek belirttiğimiz rengin, belirttiğimiz yüzeyde transparan olmasını sağlıyoruz. Daha önce belirlenmiş bir colorkey'i kaldırmak için bayrak olarak 0 değerini vermeniz yeterli. Grafik konusunda bahsedeceğim son fonksiyon SDL_DisplayFormat. Bu fonksiyon en basit hali ile uyguladığınız yüzeyin bitlerinin kopyalama ve aynı zamanda çizdirme (sonuçta çizdirirken yaptığınız iş ekrana çizdirdiğiniz yüzeye kopyalamak olduğu için aynı şey oluyor) hızını arttırmaya yarıyor. Yazılışı;

SDL_Surface *SDL_DisplayFormat(SDL_Surface *yuzey);

kullanımı ise;

yeni_hizlandirilmis_yuzey = SDL_DisplayFormat(eskiyuzey);


Bu fonksiyonun yaptığı şey yüzeyin pixel verisini RLE hızlandırma kodlaması ile kodlamak.
Grafik konusunu şimdilik bitiriyorum. Şimdi sırada olaylar (event) var.

----------------------------------------------

SDL ve Olaylar (Events)


SDL'de olay yakalama işlemleri için her tip olaya ait bir yapıyı içerisinde bulunduran bir olay yapısı
bulunmaktadır. Oluşan olaylar bir kuyruğa atılır. Ve bu kuyruktan SDL_PollEvent fonksiyonu yardımı ile çekilir. Kuyruktan alınan bilgide SDL_Event yapısı içerisinde olayın tipi ve olay hakkında bilinmesi gerekilen diğer bilgiler bulunmaktadır. Örnek bir kod için;

SDL_Event olay;

while (SDL_PollEvent(&olay)) {
    switch (olay.type){
    case SDL_KEYDOWN:
        // tuşa basıldı durumu
        break;
    }
}


Yukarıda SDL içerisinde olay yakalama ve işlemeyi gösteren basit bir örnek var. Bu örneği SDL_Event yapısının içeriğinde bulunan diğer tipler yardımı ile geliştirebilirsiniz. Aşağıda SDL_Event yapısının ve bu yapının içinde bulunan diğer alt yapıların içeriklerini bulacaksınız.

SDL_Event yapı tanımlaması:

typedef union{
 Uint8 type;
 SDL_ActiveEvent active;
 SDL_KeyboardEvent key;
 SDL_MouseMotionEvent motion;
 SDL_MouseButtonEvent button;
 SDL_JoyAxisEvent jaxis;
 SDL_JoyBallEvent jball;
 SDL_JoyHatEvent jhat;
 SDL_JoyButtonEvent jbutton;
 SDL_ResizeEvent resize;
 SDL_QuitEvent quit;
 SDL_UserEvent user;
 SDL_SywWMEvent syswm;
} SDL_Event;


Yapı içeriği:

type Olayın tipi
active Aktifleşme olayı
key Klavye olayları
motion Mouse hakeret olayları
button Mouse buton olayları
jaxis Joystick axis motion event
jball Joystick trackball motion event
jhat Joystick hat motion event
jbutton Joystick button event
resize Uygulama pencere yeniden boyutlandırma olayı
quit Uygulama çıkış isteği
user Kullacını tanımlı olay
syswm Tanımlanmamış pencere yöneticisi olayı

Olay Tipi Olay yapısı
SDL_ACTIVEEVENT SDL_ActiveEvent
SDL_KEYDOWN/UP SDL_KeyboardEvent
SDL_MOUSEMOTION SDL_MouseMotionEvent
SDL_MOUSEBUTTONDOWN/UP SDL_MouseButtonEvent
SDL_JOYAXISMOTION SDL_JoyAxisEvent
SDL_JOYBALLMOTION SDL_JoyBallEvent
SDL_JOYHATMOTION SDL_JoyHatEvent
SDL_JOYBUTTONDOWN/UP SDL_JoyButtonEvent
SDL_QUIT SDL_QuitEvent
SDL_SYSWMEVENT SDL_SysWMEvent
SDL_VIDEORESIZE SDL_ResizeEvent
SDL_USEREVENT SDL_UserEvent

SDL'i kullanmaktaki -en azından bu dökümanda- asıl amacımız oyun yapımı olduğu için burada sadece oyun yapımında en çok ihtiyaç duyacağınız olay yapılarının detaylarını vereceğim, diğerleri için SDL'in resmi dökümanına başvurmalısınız. Ilk olarak klavye olaylarından bahsedelim. Içeriği;

Yapı tanımlanması:

typedef struct{
    Uint8 type;
    Uint8 state;
    SDL_keysym keysym;
} SDL_KeyboardEvent;


Yapı içeriği:

type SDL_KEYDOWN veya SDL_KEYUP
state SDL_PRESSED veya SDL_RELEASED
keysym basılan tuşun bilgisini saklar

Burda gördüğünüz üzere iki tip olay bulunmakta. Tuşun basılması ve kaldırılması. SDL_KEYDOWN ve SDL_PRESSED bir tuşa basıldığını, SDL_KEYUP ve SDL_RELEASED ise bir tuşun  bırakıldığını belirtir. Bu ikisi arasında fark yoktur. Sadece farklı değişkenler tarafından belirtiliyorlar. Hangi tuşa basıldığına ait bilgi ise keysym yapısında tutuluyor.
keysym'nini içeriği ise;

Yapı tanımlanması:

typedef struct{
    Uint8 scancode;
    SDLKey sym;
    SDLMod mod;
    Uint16 unicode;
} SDL_keysym;


Yapı içeriği:

scancode donanıma özel tarama kodu sym SDL sanal keysym mod o an ki tuş modifiyesi(kötü çeviri, türkçesi shift, control veya alt tuşuna basıldı mı acaba gibi birşey) unicode dönüştürülmüş karakter

scancode değişkeni klavyeden dönen donanıma özel veridir ve genellikle dokunulmaz, kurcalanmaz. sym en önemli ve en kullanışlı alandır. sym içerisinde SDL'de tanımlı tuş değerleri saklanır. Bu tuşların tanımları için SDLKey yapısının içeriğine bakmak gerekiyor ama bu yapının içeriği çok uzun olduğu için en sona bırakıyorum. Bunlar dışında mod değişkeninde yukarıda da belirttiğim gibi shift, control, alt tuşları gibi tuşlara basılması durumunda basılan tuşa ait veriyi içinde saklar. Bu veriye ulaşmanın bir diğer yoluda SDL_GetModState fonksiyonunu kullanmaktır. Kullanımı;

SDLMod mod;
mod=SDL_GetModState();

SDLMod yapısı içeriği;

typedef enum {
    KMOD_NONE = 0x0000, //herhangi bir modifier'a basılı değil
    KMOD_LSHIFT = 0x0001, // sol shift tuşu
    KMOD_RSHIFT = 0x0002, // sağ shift tuşu
    KMOD_LCTRL = 0x0040, // sol control tuşu
    KMOD_RCTRL = 0x0080, // sağ control tuşu
    KMOD_LALT = 0x0100, // sol alt tuşu
    KMOD_RALT = 0x0200, // sağ alt tuşu
    KMOD_LMETA = 0x0400, // sol meta?? -bu ne beah- tuşu
    KMOD_RMETA = 0x0800, // sağ meta?? -galiba şu yeni windows tuşlarından biri- tuşu
    KMOD_NUM = 0x1000, // numlock tuşu
    KMOD_CAPS = 0x2000, // capslock tuşu
    KMOD_MODE = 0x4000, // bunun ne olduğu konusunda hiç bir fikrim yok
} SDLMod;


SDL içerisinde ayrıca şu tanımlamalarda mevcut;

#define KMOD_CTRL (KMOD_LCTRL|KMOD_RCTRL) // herhangi bir control tuşu
#define KMOD_SHIFT (KMOD_LSHIFT|KMOD_RSHIFT) // herhangi bir shift tuşu
#define KMOD_ALT (KMOD_LALT|KMOD_RALT) // herhangi bir alt tuşu
#define KMOD_META (KMOD_LMETA|KMOD_RMETA) // herhangi bir meta tuşu 


SDL'de SDL_GetModState gibi iki tane de normal tuşların değerini alabileceğimiz bir fonksiyon bulunmaktadır, adı da SDL_GetKeyState ve SDL_GetKeyName. Kullanımları ise aynı. Sadece bu sefer SDLMod değilde SDLKey tipinde bir değişken tanımlıyoruz. SDLKey'in içeriği ise;

SDLKey ASCII değeri Bilinen adı
SDLK_BACKSPACE '\b' backspace
SDLK_TAB '\t' tab
SDLK_CLEAR clear
SDLK_RETURN '\r' return
SDLK_PAUSE pause
SDLK_ESCAPE '^[' escape
SDLK_SPACE ' ' space
SDLK_EXCLAIM '!' exclaim
SDLK_QUOTEDBL '"' quotedbl
SDLK_HASH '#' hash
SDLK_DOLLAR '$' dollar
SDLK_AMPERSAND '&' ampersand
SDLK_QUOTE ''' quote
SDLK_LEFTPAREN '(' left parenthesis
SDLK_RIGHTPAREN ')' right parenthesis
SDLK_ASTERISK '*' asterisk
SDLK_PLUS '+' plus sign
SDLK_COMMA ',' comma
SDLK_MINUS '-' minus sign
SDLK_PERIOD '.' period
SDLK_SLASH '/' forward slash
SDLK_0 '0' 0
SDLK_1 '1' 1
SDLK_2 '2' 2
SDLK_3 '3' 3
SDLK_4 '4' 4
SDLK_5 '5' 5
SDLK_6 '6' 6
SDLK_7 '7' 7
SDLK_8 '8' 8
SDLK_9 '9' 9
SDLK_COLON ':' colon
SDLK_SEMICOLON ';' semicolon
SDLK_LESS '<' less-than sign
SDLK_EQUALS '=' equals sign
SDLK_GREATER '>' greater-than sign
SDLK_QUESTION '?' question mark
SDLK_AT '@' at
SDLK_LEFTBRACKET '[' left bracket
SDLK_BACKSLASH '\' backslash
SDLK_RIGHTBRACKET ']' right bracket
SDLK_CARET '^' caret
SDLK_UNDERSCORE '_' underscore
SDLK_BACKQUOTE '`' grave
SDLK_a 'a' a
SDLK_b 'b' b
SDLK_c 'c' c
SDLK_d 'd' d
SDLK_e 'e' e
SDLK_f 'f' f
SDLK_g 'g' g
SDLK_h 'h' h
SDLK_i 'i' i
SDLK_j 'j' j
SDLK_k 'k' k
SDLK_l 'l' l
SDLK_m 'm' m
SDLK_n 'n' n
SDLK_o 'o' o
SDLK_p 'p' p
SDLK_q 'q' q
SDLK_r 'r' r
SDLK_s 's' s
SDLK_t 't' t
SDLK_u 'u' u
SDLK_v 'v' v
SDLK_w 'w' w
SDLK_x 'x' x
SDLK_y 'y' y
SDLK_z 'z' z
SDLK_DELETE '^?' delete
SDLK_KP0 keypad 0
SDLK_KP1 keypad 1
SDLK_KP2 keypad 2
SDLK_KP3 keypad 3
SDLK_KP4 keypad 4
SDLK_KP5 keypad 5
SDLK_KP6 keypad 6
SDLK_KP7 keypad 7
SDLK_KP8 keypad 8
SDLK_KP9 keypad 9
SDLK_KP_PERIOD '.' keypad period
SDLK_KP_DIVIDE '/' keypad divide
SDLK_KP_MULTIPLY '*' keypad multiply
SDLK_KP_MINUS '-' keypad minus
SDLK_KP_PLUS '+' keypad plus
SDLK_KP_ENTER '\r' keypad enter
SDLK_KP_EQUALS '=' keypad equals
SDLK_UP up arrow
SDLK_DOWN down arrow
SDLK_RIGHT right arrow
SDLK_LEFT left arrow
SDLK_INSERT insert
SDLK_HOME home
SDLK_END end
SDLK_PAGEUP page up
SDLK_PAGEDOWN page down
SDLK_F1 F1
SDLK_F2 F2
SDLK_F3 F3
SDLK_F4 F4
SDLK_F5 F5
SDLK_F6 F6
SDLK_F7 F7
SDLK_F8 F8
SDLK_F9 F9
SDLK_F10 F10
SDLK_F11 F11
SDLK_F12 F12
SDLK_F13 F13
SDLK_F14 F14
SDLK_F15 F15
SDLK_NUMLOCK numlock
SDLK_CAPSLOCK capslock
SDLK_SCROLLOCK scrollock
SDLK_RSHIFT right shift
SDLK_LSHIFT left shift
SDLK_RCTRL right ctrl
SDLK_LCTRL left ctrl
SDLK_RALT right alt
SDLK_LALT left alt
SDLK_RMETA right meta
SDLK_LMETA left meta
SDLK_LSUPER left windows key
SDLK_RSUPER right windows key
SDLK_MODE mode shift
SDLK_HELP help
SDLK_PRINT print-screen
SDLK_SYSREQ SysRq
SDLK_BREAK break
SDLK_MENU menu
SDLK_POWER power
SDLK_EURO euro

Gördüğünüz gibi uzun. Fonksiyonun kullanımı ise;

SDLKey key;
key=SDL_GetKeyState();

şeklindedir.
Son olarak keysym veri yapısı içerisindeki son değişken olan unicode'da ise basılan tuşun unicode verisi saklanır. Tabii bunun için önceden SDL_EnableUNICODE fonksiyonu ile Unicode'u aktif hale getirmek gerekir. Detaylar için SDL'in resmi dökümantasyonuna bakın. Oyun yapımı konusunda çokta gerekli olmadığı için burda anlatmayacağım. Bunun yerine size çok işe yarar bulduğum bir fonksiyonu tanıtmak istiyorum, SDL_EnableKeyRepeat foksiyonunu. Bu fonksiyon basılı tuttuğunuz tuşun tekrar oranını belirler veya tekrar özelliğini etkisiz hale getirir. Kullanımı;

int SDL_EnableKeyRepeat(int delay, int interval);

şeklindedir. delay değişkeni tuş tekrar edilmeden önce ne kadar basılı tutulması gerektiği belirtir. interval ise tekrar hızını belirtir. Ikisininde değeri milisaniye cinsindendir. Eğer delay değişkenini 0 yaparsanız tekrar özelliği tamamiyle etkisiz hale gelir. Uygun varsayılan değerler SDL içerisinde SDL_DEFAULT_REPEAT_DELAY ve SDL_DEFAULT_REPEAT_INTERVAL adları ile tanımlanmıştır. Klavye olayları ile ilgili anlatacaklarım bu kadar sırada mouse olayları var.
Mouse ile ilgili olaylar için kullanılan iki tane yapı var. Bunlar SDL_MouseMotionEvent ve SDL_MouseButtonEvent. Bu yapılar SDL_Event yapısı içerisinde motion ve button adları ile tanımlanmışlardır. motion mouse'un hareket etmesi ile oluşan olayları kapsar. button ise mouse'un tuşlarına bastığınızda oluşan olayları kapsar. Ilk olarak motion'dan bahsedelim. SDL_MouseMotionEvent yapısının içeriği;

typedef struct{
    Uint8 type;
    Uint8 state;
    Uint16 x, y;
    Sint16 xrel, yrel;
} SDL_MouseMotionEvent;


şeklindedir. Ve;

type için tek bir tip bulunmaktadır ve o da SDL_MOUSEMOTION 'dur. state o an ki mouse tuşlarının durumları x, y mouse'un X/Y koordinatları xrel, yrel mouse'un X/Y koordinatlarındaki göreceli hareketi

temsil eder.

SDL_MouseButtonEvent yapısının içeriği ise;

typedef struct{
    Uint8 type;
    Uint8 button;
    Uint8 state;
    Uint16 x, y;
} SDL_MouseButtonEvent;


şeklindedir. Açıklamak gerekirse;

type SDL_MOUSEBUTTONDOWN veya SDL_MOUSEBUTTONUP tipleri
button Olayın hangi tuşla alakalı olduğu (SDL_BUTTON_LEFT, SDL_BUTTON_MIDDLE, SDL_BUTTON_RIGHT) state SDL_PRESSED veya SDL_RELEASED yani tuşa basıldı mı? yoksa tuş bırakıldı mı? x, y Basılma veya bırakılma anındaki X/Y koordinatlarıdır.

Mouse olaylarını yakalamak için klavye olaylarında olduğu gibi en başta belirttiğimden farklı bir yol daha vardır. Bu yol ise SDL_GetMouseState fonksiyonundan geçer.

Uint8 SDL_GetMouseState(int *x, int *y);

şeklinde yazılır. X veya Y koordinatlarını girmek istemezseniz NULL değeri girebilirsiniz. Bu fonksiyon geriye basılan tuşa ait bilgiyi geri döndürür. Tuşa ait bilgiye ulaşmak için SDL_BUTTON(X) makrosunu kullanabilirsiniz. Bir örnek vereyim.

SDL_PumpEvents();
if(SDL_GetMouseState(NULL, NULL)&SDL_BUTTON(1))
printf("Sol Mouse tuşuna basıldı.\n");

Yukarıda SDL_PumpEvents fonksiyonunu kullandık. Bu fonksiyon oluşan olaylardan gelen bilgileri derleyip olay kuyruğuna koyar. Bir olayın olay kuyruğuna konması için bu fonksiyonun çağrılması gerekir ama SDL_PollEvent ya da SDL_WaitEvent fonksiyonlarını kullanıyorsanız bu fonksiyonu kullanmanıza gerek kalmaz. SDL_WaitEvent fonksiyonu ise özel olarak belirtilmiş bir olayın gerçekleşmesini bekler. Bu olay gerçekleşene kadar programın kapanmasını engeller. Kullanımı;

int SDL_WaitEvent(SDL_Event *event);

Kullanımı nerede ise SDL_PollEvent'ın aynısıdır ama aradaki fark SDL_WaitEvent bir olay gerçekleşene kadar programı bekletir. SDL_PollEvent ise gerçekleşen olay var mı diye bakar, varsa işlem yapar, yoksa devam eder.

----------------------------------------------

SDL ve Ses (Audio)

SDL içerisindeki ses fonksiyonları genel olarak aşağı seviye (low level) ses programlaması kullanılacak yazılımlar için tasarlanmıştır. Bu nedenle ses konusunda SDL'e kendi yazdığınız kod ile taklalar attırmak istemiyorsanız ya da tek yapacağınız basit bir wav dosyasını açıp çalmak değilse size kolaylıklar ve gelişmiş özellikler sunan bir ses kütüphanesi olan SDL_Mixer'ı kullanmanızı tavsiye ederim. Bu kütüphane SDL üzerinden çalışıyor ve size oldukça gelişmiş bir çok özelliği kolayca kullanım ile size sunuyor. Bu kütüphaneye SDL'in websitesinden "Libraries" bölümünden ulaşabilirsiniz. SDL içerisindeki ses fonksiyonlarına dönelim. Ilk olarak bilmeniz gereken bu fonksiyonları kullanmak için önce SDL_INIT_AUDIO alt sistemini aktif hale getirmelisiniz. Basitçe açıklamak gerekirse ilk olarak ses sistemini açacağız. Ardından ses dosyasından veriyi ki bu dosya  Wav uzantılı olmalı, hafizaya yükleyeceğiz. Daha sonra ise bu veriyi ses sistemine çalması için ileteceğiz. Şimdi fazla detaya inmeden basitçe ses sistemini nasıl açacağınızı ve bir ses dosyasını nasıl yükleyip, çalacağınızı anlatacağım. Ilk önce ses sistemini nasıl açacağımızdan bahsedeceğim.

SDL_AudioSpec fmt; /* SDL_AudioSpec ses ayarlarına ait bilgilerin saklandığı veri yapısı. Ilk olarak bu tip bir yapı tanımlayıp ona istediğimiz değerleri giriyoruz. Ardındanda bu yapıyı SDL_OpenAudio fonksiyonunda parametre olarak kullanarak istediğimiz değerlerde ses sistemini açıyoruz */

/* 22 Khz 'de 16 bit stereo ses */
fmt.freq = 22050; // frekans değeri
fmt.format = AUDIO_S16; // veri formatı
fmt.channels = 2; // kanal sayısı 1- mono, 2- stereo
fmt.samples = 512; // sample'ların tampon büyüklüğü
fmt.callback = mixaudio; // ses tamponunu doldurmak için çağırılacak fonksiyon
// bu fonksiyonu aşağıda yazacağım
fmt.userdata = NULL; // fonksiyona girilecek parametrelerin işaretçisi

/* Yukarıda ki değerler oyunlar için uygun değerlerdir */

/* SDL_OpenAudio fonksiyonuna parametre olarak yukarıda değerlerini girdiğimiz SDL_AudioSpec veri yapısı verilir ve "Voila!" ses sistemi açılır. Geriye -1 değeri dönerse bir sorun çıkmış ve ses sistemi açılamamış demektir. */

if (SDL_OpenAudio(&fmt, NULL) < 0) {
    fprintf(stderr, "Ses sistemi açılamıyor : %s\n", SDL_GetError());
    exit(1);
}

/* Bu satırı girmeden programınızdan ses gelmeyecektir.
Bunun nedeni SDL'in program hazır olana kadar istediğiniz
ses ayarlarını yapabilmenize imkan tanımasıdır */

SDL_PauseAudio(0);
....

/* ses ile işiniz bitince SDL_CloseAudio fonksiyonunu kullanarak ses sistemini kapatmayı unutmayın. */
SDL_CloseAudio();



Ses sistemini açmak kısaca böyle. Sırada bir ses dosyasını hafizaya yüklemek ve çalmak var.

Örnek kodumuzda ilk olarak ses verisini saklayacağımız veri yapısını tanımlıyoruz.

#define NUM_SOUNDS 2
#define NUM_SOUNDS 2
struct sample {
    Uint8 *data;
    Uint32 dpos;
    Uint32 dlen;
} sounds[NUM_SOUNDS];


Ses stereo olacağı için ses sayısını 2 yaptık. data ses verileri için, dpos verilerin pozisyonu için ve dlen ise ise verinin uzunluğuna ulaşmak için kullanılıyor.

Bu fonksiyon ile yaptığımız şey temel olarak SDL_LoadWav fonksiyonu ile hafızaya yükleyeceğimiz bir ses dosyasının verilerini SDL için uygun şekilde formatlamak. Bu fonksiyon yukarıdaki ses sistemini açmak için kullanabileceğimiz kodlar arasında kullanılıyor.

void mixaudio(void *unused, Uint8 *stream, int len)
{
    int i;
    Uint32 amount;

    for (i = 0; i<NUM_SOUNDS; ++i) {
        amount = (sounds[i].dlen - sounds[i].dpos);
        if (amount > len) {
            amount = len;
        }
        SDL_MixAudio(stream, &sounds[i].data[sounds[i].dpos], amount, SDL_MIX_MAXVOLUME);
        sounds[i].dpos += amount;
    }
}


Bu fonksiyon ise kendisine parametre olarak girilen dosyayı yükler ve çalar. Ama önceden ses sisteminin açılması gerekir. Yukarıdaki fonksiyonu ise ses sistemini açarken kullandığımız kodlar arasında kullanmıştık. Aklınızda bulunsun.

void PlaySound(char *file)
{
    int index; //sayaç değişkeni
    SDL_AudioSpec wave; //ses verisi ayarları
    Uint8 *data; //ses verisi
    Uint32 dlen; //uzunluğu
    SDL_AudioCVT cvt; //ses verisinin formatını değiştirirken kullanılan veri yapısı

    /* Boş veya bitmiş bir ses slotu aranır */
    for (index = 0; index < NUM_SOUNDS; ++index) {
        if (sounds[index].dpos == sounds[index].dlen) {
            break;
        }
    }
    if (index == NUM_SOUNDS)
        return;

    /* Ses dosyası yüklenir ve 22kHz 16-bit stereo ya çevrilir */
    if (SDL_LoadWAV(file, &wave, &data, &dlen) == NULL) {
        fprintf(stderr, "Yüklenemiyor %s: %s\n", file, SDL_GetError());
        return;
    }
    SDL_BuildAudioCVT(&cvt, wave.format, wave.channels, wave.freq,
        AUDIO_S16, 2, 22050);
    cvt.buf = malloc(dlen*cvt.len_mult);
    memcpy(cvt.buf, data, dlen);
    cvt.len = dlen;
    SDL_ConvertAudio(&cvt);
    SDL_FreeWAV(data);

    /* Ses verisini slota yerleştir (hemen çalmaya başlar) */
    if (sounds[index].data) {
        free(sounds[index].data);
    }
    SDL_LockAudio();
    sounds[index].data = cvt.buf;
    sounds[index].dlen = cvt.len_cvt;
    sounds[index].dpos = 0;
    SDL_UnlockAudio();
}


Fazla detaylı değil biliyorum. Ama yakın zamanda sizin için daha kullanışlı olacağını düşündüğüm SDL_Mixer kütüphanesi içinde bir dökümantasyon hazırlayacağım. Orda istediğiniz miktarda bilgiye erişebileceksiniz. Şimdilik hepsi bu kadar.

------------------------------------------------

SDL ve Zaman

Sıra geldi SDL içerisindeki zamanla ilgili fonksiyonlara. Ilk olarak SDL_Delay fonksiyonunu anlatacağım. Yazılışı;

Uint32 zaman=10;
SDL_Delay(zaman);

Bu fonksiyon girilen parametre kadar milisaniye süresince programı bekletir. Ekrana yeni bir çizim yapmadan hemen öncesine ya da bütün çizimler bittikten hemen sonrasına bir adet SDL_Delay(1); satırı eklemeniz programınızın güçlü makinalarda beklediğinizden hızlı çalışmasını engeller.
Bunun dışında SDL_GetTicks fonksiyonu oldukça işinize yarayabilecek bir fonksiyondur. Bu fonksiyon SDL programı çalıştırıldığından beri kaç milisaniye geçtiği değerini verir. 49 günden sonra değer başa dönüyormuş sanırım. Kullanımı;

Uint32 gecenzaman;
gecenzaman=SDL_GetTicks();

Bunlar dışında 3 tanede timer fonksiyonu bulunmaktadır. Bunlar SDL_AddTimer,  SDL_RemoveTimer ve SDL_SetTimer dır. Ve SDL_Delay ve SDL_GetTicks fonksiyonları için gerekmese de, bu fonksiyonları kulllanabilmek için SDL_INIT_TIMER alt sistemini aktif hale getirmeniz gerekmektedir. SDL_AddTimer fonksiyonu belirtilen miktarda milisaniye geçince belirtilen bir fonksiyonu çağıran bir timer ekler. Yazılışı;

my_timer_id = SDL_AddTimer(30, cagrilan_fonk, fonk_parametreleri);

şeklindedir.
SDL_RemoveTimer() fonksiyonu ise eklenmiş bir timer'ı kaldırmak için kullanılır. Bunun için fonksiyona parametre olarak kaldırmak istediğiniz timer'ın id'si girilir. Bu fonksiyonun yazılışı;

SDL_RemoveTimer(my_timer_id);

şeklindedir.
SDL_SetTimer fonksiyonu ise belirtilen miktarda milisaniye geçince belirtilen fonksiyonu çağırır. Yazılışı;

SDL_SetTimer(30, cagrilan_fonk);

şeklindedir. SDL_SetTimer ile çalıştırılan bir timer'ı iptal etmek için;

SDL_SetTimer(0, NULL);

kodunu kullanmalıyız. Aslında bu fonksiyon eski sürümlerle uyumluluğu bozmamak için saklanmıştır ama SDL_AddTimer ve SDL_RemoveTimer fonksiyonları bu fonksiyondan daha yeni ve daha üstündür ayrıca birden çok timer'ı kullanmanıza imkan tanırlar. Dikkat edilmesi gereken nokta Timer fonksiyonları çalışırken ayrı bir thread veya alarm sinyalleri (alarm signals) kullanırlar ve makinaya bağımlıdırlar. Bu nedenle kullanırken dikkatli olunmalıdır.

Ozan Emirhan Bayyurt

Yorumlar

  1. Temayı eski haline getirirseniz çok daha okunaklı olur kodlar anlaşılmıyor .

    YanıtlaSil
  2. # -*- coding: utf-8 -*-

    from kivy.app import App
    from kivy.uix.label import Label


    class ilkUygulama(App):

    def build(self):
    return Label(text='Merhaba Dünya!')


    ilkUygulama().run()


    [CRITICAL] [Text ] Unable to find any valuable Text provider. Please enable debug logging (e.g. add -d if running from the command line, or change the log level in the config) and re-run your app to identify potential causes
    sdl2 - ImportError: DLL load failed while importing _text_sdl2: Belirtilen modül bulunamadı.
    File "C:\Python\lib\kivy\core\__init__.py", line 58, in core_select_lib
    mod = __import__(name='{2}.{0}.{1}'.format(
    File "C:\Python\lib\kivy\core\text\text_sdl2.py", line 13, in
    from kivy.core.text._text_sdl2 import (_SurfaceContainer, _get_extents,

    pil - ModuleNotFoundError: No module named 'PIL'
    File "C:\Python\lib\kivy\core\__init__.py", line 58, in core_select_lib
    mod = __import__(name='{2}.{0}.{1}'.format(
    File "C:\Python\lib\kivy\core\text\text_pil.py", line 7, in
    from PIL import Image, ImageFont, ImageDraw

    [CRITICAL] [App ] Unable to get a Text provider, abort.

    python kivy ile yukarda ki kodu yazıyorum ve sonuç olara bu hatayı alıyorum nasıl düzelteceğim. Yardımcı olursanız sevinirim.

    YanıtlaSil

Yorum Gönder

Bu blogdaki popüler yayınlar

Doxygen Kullanımı

OpenGL'de Line (çizgi) Çizdirme