SDL ile Pong Yapalım
(2387 kelime) (585 okuma)
Merhaba, bu dersimizde basit bir pong oyunu yapacağız. SDL ile ilgili
temel bilginizin olduğunu düşünerek derse başlıyoruz.
Öncelikle neler gerektiğine bir bakalım ve global bazı tanımlar
yapalım.Yazacağımız pong oyununu oynayın ve önce ne yapacağımıza
bir bakın. Bu sayede eminim ders daha anlaşılır gelecektir.
Programı indirdiyseniz gördüğünüz üzere 2
raket ve bir top var, raketler sürekli birbiri ile aynı hareketi
yapıyor. Oyun diğer bazı pong lardan farklı olarak tek kişilik. Nedeni
ise hem size oyunda geliştirecek bir alan bırakmak hem de dersi daha
anlaşılır hale getirmek.
Kütüphanelerimizi ekledikten ve linker ayarlarını yaptıktan
sonra, klasik SDL tanımları ile başlayalım :
SDL_Surface *ekran;
//klasik
ekran yüzeyimiz
SDL_Surface *raket;
//raket
SDL_Surface *top;
//top
Sırada gerekli olacak bazı değişken tanımları var. Global değişken
kullanmaktan sakının, ancak bu giriş dersi olduğu ve çok da
büyük bir program olmayacağı için buna gerek yok. İşte
bazı değişkenlerimiz, bunlar şu an için havada şeyler ancak
ilerde kullanırken anlatacağım.
#define RAKETBOYU
100
#define RAKETENI
20
#define RAKETHIZI
300
#define TOPBOYU
32
#define TOPHIZI
100
#define YUKSEKLIK
300 //pencere boyutları
#define GENISLIK
500
int raket_poz;
//iki
raket de aynı pozisyonda olacak
float top_x = GENISLIK / 2, top_y
= YUKSEKLIK / 2; //topun
pozisyonu
int top_hizi_x = TOPHIZI; //top hızları, bunları puana/zamana
göre değişken yaparak oyuna renk katabilirsiniz
int top_hizi_y = TOPHIZI;
int skor = 0; //skor değişkenimiz
float time;
İstenilen raket, top ve ekran boyutlarının/hızlarının ayarlanılabilmesi
için bu değerleri ön işlemci tanımı ile bildirdim.
YUKSEKLIK veya GENISLIK gibi değerleri değiştirmeniz çarpışma
işlemlerine etki etmeyecek çünkü bu değerler
üzerinden işlem yapacağız. "time" değişkenimiz ise FPS den
bağımsız hareket edebilmemizi sağlayacak. Küçük
oyunlarda bu çok önemli, ya fps yi kitlemeli, ya da fps den
bağımsız işlem yapmalısınız. Aksi halde oyun her bilgisayarda farklı
bir hal alacaktır. Klasik resim çizme fonksiyonumuzu da
unutmayalım :
//x,y konumuna resim çizer
void ResimCiz(SDL_Surface *resim,
int x, int y)
{
SDL_Rect dest;
dest.x = x;
dest.y = y;
SDL_BlitSurface(resim,
NULL, ekran, &dest);
}
Sıra geldi esas fonksiyona yani Render(). Bu fonksiyon her ekran
yenileceği sırada çağrılacak ve ekranı silip üstüne
bizim resimleri, yani topu ve raketleri çizecek. Eğer ekranı
sildirmezsek ne olacağını önceki derslerde söylemiştim.
void Render()
{
SDL_FillRect(ekran,NULL,0x000000);
ResimCiz(top,
int(top_x), int(top_y));
ResimCiz(raket,
0, int(raket_poz));
ResimCiz(raket,
GENISLIK - RAKETENI, int(raket_poz));
//top için saydamlık
SDL_SetColorKey(top, SDL_SRCCOLORKEY, SDL_MapRGB(top->format,
255, 0, 255));
//raketin saha dışına
çıkmasını önleyelim
if(raket_poz
< 0) {raket_poz = 0;}
if(raket_poz
> YUKSEKLIK - RAKETBOYU) { raket_poz = YUKSEKLIK - RAKETBOYU;}
//topun yeni pozisyonunu
belirleyelim
top_x +=
top_hizi_x * time;
top_y +=
top_hizi_y * time;
Bu kısma kadar anlaşılmayacak bir yer görmüyorum. Kısaca
açıklamak gerekirse, Resimlerimize uygun pozisyonlar bulduk ve
çizdik, daha sonra top için saydam renk belirledik ve
raketin ekran dışına çıkmasını önledik. Sonrasında ise
topumuzu yeni pozisyonunua atadık. Top yeni pozisyonlarında bir sonraki
frame de çizilecek. Bir de topun saha kenarlarına
çıkmasını engelleyelim. Top saha kenarlarına çarptığında,
yani yukarı veya aşağı çarptığında(çarpma işlemi pozisyon
kontrolü ile hesaplanır) top_hizi_y değerini eksi ile
çarpıyoruz ve en basit sekme efektini elde etmiş oluyoruz.
//topun saha kenarlarına
çarpması
if(top_y <
0) {top_y = 0; top_hizi_y = -top_hizi_y; }
if(top_y >
YUKSEKLIK - TOPBOYU) {top_y = YUKSEKLIK - TOPBOYU;
top_hizi_y = -top_hizi_y; }
Ben çarpışma hesabı için ayrıntılı bir sisteme gerek
görmedim. Pek çok değişken belli olduğu için
karşılaştırmalar ile topun rakete değip değmediğini anlayabiliriz.
Yazacağımız sistem oldukça basit, tabii bu basitliği nedeniyle
bazı eksileri de var. Sonuç olarak bu dersin kolay olması
için çok fazla bu konulara değinmeyeceğim. Eğer
uygulamayı geliştirecek olan olursa çarpışma sisteminde bazı
yerlere (- int(TOPBOYU/2)) gibi ifadelerin yazılması gerektiğini yani
hesapların sol üst köşeye değil, topun en ucuna göre
yapılması gerektiğini görecektir. İşte çarpışma için
kontrol kodları.
//topun
raketlere çarpmasını kontrol ediyoruz
//sol rakete
çarpabilir
if(top_x
< RAKETENI)
{
if
(top_y > raket_poz && top_y < raket_poz +
RAKETBOYU) //yakaladık
{
top_x = RAKETENI;
top_hizi_x = -top_hizi_x;
skor += 3;
SkorYenile();
}
else //kaçırdık
gol();
}
//sağ rakete çarpabilir
if(top_x >
GENISLIK - (TOPBOYU + RAKETENI))
{
if
(top_y > raket_poz - TOPBOYU && top_y < raket_poz +
RAKETBOYU)
{
top_x = GENISLIK - (TOPBOYU + RAKETENI);
top_hizi_x = -top_hizi_x;
skor += 3;
SkorYenile();
}
else //kaçırdık
gol();
}
SDL_Flip(ekran);
}
Bu kodlarda yapılanlar topun sol üst köşesinin raketin x
pozisyonu ile raketin sonunu belirten raket_x + RAKETBOYU değerleri
arasında olmasını kontrol etmek. Eğer bu değerler arasında ise top
rakete değmiştir, değilse gol olmuştur. Bulduğum en basit sistem bu.
:-) tabii isteyenler bounding ile, isteyenler per-pixel hesaplar ile de
çarpışma kontrolü yapabilir. Ben gerek görmedim.
Çarpışmanın tepkisi olarak yapılan işlem ise top_hizi_x
değişkenini eksi ile çarpmak, yani hareketi x ekseninde ters
çevirmek.
Render fonksiyonu içinde bazı fonksiyonlar kullandık bunlar
isimlerinden anlaşılabilecekleri gibi, ekrandaki(üst bardaki) skor
yazısını yenileme fonksiyonu ve gol(); fonksiyonu. Bunların tanımları
şöyle:
void SkorYenile()
{
static char
str[256];
sprintf(str,"SKORUNUZ : %d",skor);
SDL_WM_SetCaption(str,NULL);
}
//top raketi geçmişse yeni
pozisyona getirir ve 10 puan azaltır
void gol()
{
skor -= 10;
top_x =
GENISLIK/2;
top_y =
YUKSEKLIK/2;
SkorYenile();
}
Bu şekilde skorumuzu da en basit yoldan görmüş oluyoruz.
Ancak oyun henüz bitmedi, gerçi bu kısımdan sonrasını
önceki derslere bakarak da yapabilirsiniz, yapılacak işlemler
klasik sdl ayarlamaları:
int main(int argc, char *argv[])
{
Uint8* BasilanTus;
if
(SDL_Init(SDL_INIT_AUDIO|SDL_INIT_VIDEO) < 0 )
{
printf("SDL
baslatilamadi: %sn", SDL_GetError());
exit(1);
}
atexit(SDL_Quit);
ekran =
SDL_SetVideoMode(GENISLIK,YUKSEKLIK,32,SDL_HWSURFACE|SDL_DOUBLEBUF);
if ( ekran == NULL )
{
printf("Ekran
oluşturulamadı: %sn", SDL_GetError());
exit(1);
}
Resim yükleme, skor yenileme gibi oyun başlar başlamaz
çalışan fonksiyonlar:
raket =
SDL_LoadBMP("raket.bmp");
top =
SDL_LoadBMP("top.bmp");
SkorYenile();
ve tabii ana döngü:
bool bitti = false;
while(bitti ==
0) //game loop
{
SDL_Event olay;
while (
SDL_PollEvent(&olay) )
{
if
( olay.type == SDL_QUIT ) { bitti = true; }
if
( olay.type == SDL_KEYDOWN )
{
if ( olay.key.keysym.sym == SDLK_ESCAPE ) { bitti = true; }
}
}
static float
frameTime = 0.0f; // This stores the last frame's time
float
currentTime = SDL_GetTicks() * 0.001f;
time =
currentTime - frameTime;
frameTime =
currentTime;
BasilanTus =
SDL_GetKeyState(NULL);
//basılan tuşu algılar
if (
BasilanTus[SDLK_UP] ) { raket_poz -= RAKETHIZI * time; } //yön tuşları ile
hareket
if (
BasilanTus[SDLK_DOWN] ) { raket_poz += RAKETHIZI * time; }
Render(); //ekranı
çiziyoruz
}
//program bitirildiyse
return 0;
}
İşte bu kadar en basit şekli ile bir pong yaptık. İsteyenler 2 kişi
modu, daha güzel çarpışma sistemi, sesler, font sınıfı...
gibi eklentiler ile oyunu daha güzel bir hale getirebilir. Eklenti
yapmayı deneyin, bu şekilde daha iyi öğreneceğinizden kuşkunuz
olmasın.
[
VC++6.0 proje dosyalarını ve programın çalıştırılabilir halini
indirmek için tıklayın - (11Kb) ]
Görüşmek üzere
+-+-+-+-
STR
Stroma :: 2005 :: www.oyunyapimi.org
|