1. 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,
Emülatorlerde, 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++ ile 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.
2. SDL Kurulum
Bu konu aslında basit olmasına rağmen yeni başlayanları biraz
zorlayabilir. Yine de elimden geldiğince her platform'da SDL'i nasıl
kurup kullanmaya hazır hale getireceğimizi
yazmaya çalışacağım.
2.1. 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 internette de 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.
2.1.1. Visual Studio
İlk 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.
2.1.2. 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. İş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.
İlk 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. İsterseniz ş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.
2.1.3 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.
2.2. Linux
Daha sonra ekleyeceğim.
3. 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, sekiz 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 ilk ö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.
İlk 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);
}
4. SDL ve Grafik
Sırada grafik komutlarını kullanmayı öğrenmek var. İlk 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. İlk 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 pixel başına düşen bit 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. İlk
olarak oldukça basit olduğu için bir BMP dosyası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.
İç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öntemi 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öntemi
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. İş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. İşte bu yüzden x değerini bpp ile
çarptık. pitch değişkeninde ise yüzeyin bir satırının uzunluğu
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 durumlarda
da 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.İlk ders için bu kadarı yeterli. Şu noktaya kadar
anlattıklarım ile SDL programları yazmaya başlayabilirsiniz.
5. SDL ve Grafik - Bölüm 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. İlk
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.
6. 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. İlk olarak
klavye olaylarından bahsedelim. İç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.
İkisininde 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. İlk 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.
7. 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 imkanı sunuyor. Bu
kütüphaneye SDL'in websitesinden "Libraries" bölümünden
ulaşabilirsiniz. SDL içerisindeki ses fonksiyonlarına
dönelim. İlk 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. İlk ö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ı.
ılk 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ıdaki 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
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.
8. SDL ve Zaman
Sıra geldi SDL içerisindeki zamanla ilgili fonksiyonlara. İlk 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. [ ed: Günümüzde
bu yöntem kesinlikle tavsiye edilmez. Programınızın her bilgisayarda
beklediğiniz sabit hızda çalışmasını istiyorsanız
dökümanlar bölümünde yer alan "Oyun Programlarında Doğru Zamanlama"
isimli dökümanda anlatılan
yönemlere benzer teknikleri uygulamanız gerekir ]
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 {ragnor}
Website: http://www.geocities.com/ragnor_whr/
E-mail: ragnor_whr@yahoo.com
Tarih: 27/06/2005
www.oyunyapimi.org