Pazar, Kasım 07, 2010

SDL Per Pixel Çarpışma Algoritması

Daha önce Oyunlarda Basit AI(Yapay Zeka) Uygulamaları yazımda Rectangle çarpışma algoritmasından bahsetmiştim. Bu algoritmanın mantığında resmi dik dörtgen olarak algılayıp o sınırlar içine başka bir resim girerse çarpışma var demektir. Aşağıdaki resimlerdeki gibi.











Burada şöyle bir sorun çıkar karşımıza. Nesnelerimiz her zaman bu şekilde dik dörtgen olmaya bilir. Yuvarlak,üçgen, çokgen veya belli bir biçimi olmaya bilir. Bu durumda rectangle algoritması yukarıda belirttiğim gibi resmin etrafında bir çerçeve varmış gibi farz eder ve başka bir resim o çerçevenin sınırları içerisine girerse çarpışma algılar. Aşağıdaki resimlerdeki gibi.












Halbuki ikinci resimde tuğla ile kaya arasında temas yok. Yani çarpışma henüz gerçekleşmedi. Kayaya deydiği anda çarpışma var olarak algılatmamız lazım. İş de bu tip durumlar için Per Pixel Çarpışma algoritmalarına ihtiyaç duyarız.

Per Pixel algoritmalarında öncelikle bilmemiz gereken şey arka planı şeffaf(transparan) resimler kullanmamız gerektiği. Bu algoritmada Rectangle Çarpışma algoritmasında olduğu gibi yine resmin sınırları bir çerçeve gibi belirlenir(resimlerdeki yeşil ve kırmızı arkaplanları ben anlaşılması açısından koydum). Başka bir resim bu sınır içerisine girdiğinde her iki resim içinde kesişen yerlerdeki pixeller taranmaya başlar. Her iki resim içinde taranmakta olan mevcut pixel transparan değilse çarpışma var demektir.












İlk resimde resmin sınırlarına girmiş durumda ama henüz bir temas yok. İkinci resimde ise temas var ve çarpışma algılanmış durumda. Bu işlemi gerçekleştiren kod aşağıdaki şekilde:

SDL_Rect Resim::temasAlani(SDL_Rect rect)
{
SDL_Rect temasRect;
temasRect.x = rect.x - this->getX();
temasRect.y = rect.y - this->getY();
temasRect.w = rect.w;
temasRect.h = rect.h;

return temasRect;
}

bool Resim::perPixelCarpismaKontrol(Resim* hedef)
{
bool durum = false;

int x1 = Maximum(getResimKoord().x, hedef->getResimKoord().x);
int y1 = Maximum(getResimKoord().y, hedef->getResimKoord().y);

int x2 = Minimum(getResimKoord().x + resimW, hedef->getResimKoord().x + hedef->resimW);
int y2 = Minimum(getResimKoord().y + resimH, hedef->getResimKoord().y + hedef->resimH);

int width = x2 - x1;
int height = y2 - y1;

SDL_Rect carpismaRect = {0,0,0,0};

//Eğer width ve height 0 dan büyükse ise resimler temas halinde demektir
if(width > 0 && height > 0)
{
//Ne kadarlık bir alanın temas halinde olduğu
carpismaRect.x = x1;
carpismaRect.y = y1;
carpismaRect.w = width;
carpismaRect.h = height;

/*İlgili resmin hangi koordinatlarında kesişme olduğunu öğreniyoruz
Bu sayede resmin tamamını değil sadece kesişen kısmı kontrol ediyoruz*/
SDL_Rect kaynakRect = temasAlani(carpismaRect);
SDL_Rect hedefRect = hedef->temasAlani(carpismaRect);

//pixel pixel resim taramasına başlıyorum
for(int y = 0; y <= carpismaRect.h; y++)
{
for(int x = 0; x <= carpismaRect.w; x++)
{
if(GetAlphaXY(this, kaynakRect.x + x, kaynakRect.y + y) &&
GetAlphaXY(hedef, hedefRect.x + x, hedefRect.y + y))
durum = true;
}
}
}
else
durum = false;

return durum;
}


Yukarıdaki kodlarda temasAlani() fonksiyonuna dikkat çekmek istiyorum.Ne kadarlık bir alanda temas olduğunu öğrendikten sonra temasAlani() fonksiyonu ile temasın ilgili resmin hangi koordinatlarında yani hangi pixeller arasında olduğunu öğreniyoruz. Bu sayede sadece o alandaki pixellerin şeffaflığını kontrol etmiş oluyoruz.












Aşağıda örnek uygulama anlamanıza yardımcı olacaktır. Farkı görmeniz açısından hem Rectangle hemde Per Pixel çarpışma algoritmalarını beraber verdim.











Not: Program Code::Blocks kullanarak GCC ile derlenmiştir. Proje klasörünün içinde çalıştırılabilir(executable) halleri vardır. SDL'yi kurmadan deneyebilmeniz için.

Linux(Ubuntu) için Kaynak Kod: SDL_Per_Pixel
Windows için Kaynak Kod: SDL_Per_Pixel

2 yorum:

Adsız dedi ki...

Diline sağlık olsun Esat.

Gayet yalın bir şekilde çok güzel açıklamışsın, tebrikler...

Ayşen KARAMETE

Esat ARSLAN dedi ki...

Teşekkür ederim hocam :)