Oyun Yapımı - 3 (Kullanıcı Etkileşiminin Sağlanması)
Oyun Yapımı - 3

1. Basılan Tuşlara Göre Düzgün Hareket Sağlanması:

Geçen dersimizde Entity sınıfını ve bundan türeyen Ball sınıfını oluşturmuş, Ball nesnelerini ekrana çizdirmiştik. İsterseniz şimdi kullanıcı etkileşimi konusuna değinelim. Oyunlarda kullanıcı etkileşimi klavye, mouse, joystick gibi birimler ile sağlanmaktadır. SDL kütüphanesi bize bu birimlerden girdi almada kolaylık sağlayan yordamları içerir. Teknik olarak bu birimlerden gelen girdiler SDL sisteminde “event queue” olarak adlandırılan yapı içerisine işlenirler. SDL_PollEvent() yordamı ile sırada bekleyen bir “event” i elde edebiliriz. Daha sonra bu “event” in tipine ve özelliklerine göre programımıza yön veririz. Geçen örneklerimizde yine bu fonksiyon aracılığı ile ESC tuşuna basılıp basılmadığını sınamayı görmüştük. Şimdi main.cpp kütüğü üzerinde çeşitli eklemeler yaparak, programımızın çizdiği bir adet top nesnesini ok tuşları ile hareket ettirmeyi öğreneceğiz.

// --- main.cpp --------------------

#include "types.h"
#include "Ball.h"

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

Ball ball;

bool mov_east=false, mov_west=false,mov_north=false,mov_south=false;

void initSDL(int width,int height,int bpp) {

   // Bu yoram SDL kütüphanesini baslatan yordamdir. SDL kütüphanesinin VIDEO
   // ve zamanlama kisimlarini kullanmayi düsündügümüz için buna uygun
   // parametreleri kullandik. Herhangi bir hata durumunda ise SDL_GetError()
   // yordami ile hatanin sebebini ögrenip ekrana (yada stdout.txt kütügüne)
   // yazdiriyor ve programdan çikiyoruz.


   if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) < 0) {
      fprintf(stderr,"SDL baslatiminda hata! %s",SDL_GetError());
      exit(-1);
   }

   // exit() yordami çagirildiginda buna ek olarak SDL_Quit() isimli SDL yordaminin da
   // çagirismasini asagidaki kod ile belirtmis oluyoruz. SDL_Quit() yordami SDL
   // kütüphanesini kapatan yordamdir. Bu sekilde artik program sonunda SDL_Quit()
   // yordamini çagirmak yerine sadece exit() yordami ile bu isin otomatik gerçeklesmesini
   // saglamis olduk.


   atexit(SDL_Quit);

   // Bu yordam ile nihayet genisligini, yüksekligini ve renk çözünürlügünü belirledigimiz
   // bir pencere açiyoruz. SDL_OPENGL  parametresi bu pencereyi OpenGL altindan
   // kullanacagimizi belirtiyor.


   if (SDL_SetVideoMode(width,height,bpp,SDL_OPENGL) == NULL) {
      fprintf(stderr,"SDL ekrani açamadi!");
      exit(-1);
   }

}

void setupOpenGL(int width,int height) {
   
   // poligonlarin cizim sirasinda oncelik kontrolu icin Z Buffer
   // teknigini kullan.

   glDepthFunc(GL_LESS);
   glEnable(GL_DEPTH_TEST);
   
   // poligonlarin icini doldurmada renk gecislerini yumusak yap
   glShadeModel(GL_SMOOTH);
   
   // arka fon rengi siyah olsun
   glClearColor(0,0,0,0);

   // goruntuyu olusturacagimiz alani belirt.
   glViewport(0,0,width,height);

   
   // opengl de goruntu olusturulacakken kullanilacak sanal kameranin
   // lens ayarlarini gerceklestir (bakis acisi ve menzil ayarlari...)

   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
   gluPerspective(45,(float)width/(float)height,0.1f,4000.0f);
   glMatrixMode(GL_MODELVIEW);

}

void renderScene() {

   // Onceki seferde cizilenleri siliyoruz. Ayni zamanda Z Buffer
   // algoritmasinin duzgun islemesi icin Z Buffer daki onceki seferden
   // kalma bilgilerde sifirlaniyor.

   
   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
   
   // Kamera poziyonunu sıfırlıyoruz.
   glLoadIdentity();

   // Kameramızı biraz geri alalımki dünyadaki çizimleri gözlemleyelim..
   // (Yani cisimler kameranın arkasında kalmasınlar...)

   glTranslatef(0.0f,0.0f,-30.0f);
   
   // Z ekseni dogrultusunda bir hareket durumu varsa bunu uyguluyoruz.
   if (mov_north) {
      static const Vec3d deltaz = {0,0,-0.1};
      ball.move(&deltaz);
   }
   else
   if (mov_south) {
      static const Vec3d deltaz = {0,0,0.1};
      ball.move(&deltaz);
   }


   // X ekseni dogrultusundaki hareket durumunu uyguluyoruz.
   if (mov_east) {
      static const Vec3d deltax = {0.1,0,0};
      ball.move(&deltax);
   }
   else
   if (mov_west) {
      static const Vec3d deltax = {-0.1,0,0};
      ball.move(&deltax);
   }


   
   // topu ciz.
   ball.render();
      
   // Bellekte olusturulan cizim pencere icine aktariliyor. Bu sekilde
   // goruntuyu ekranda gorebiliyoruz.

   SDL_GL_SwapBuffers();

}

int main(int argc,char *argv[]) {

   // Ana donguden cikis icin kullandigimiz degiskenimiz.

   bool quit = false;

   // SDL kütüphanesini başlatıp, ekranda bir pencere açiyoruz.
   
   initSDL(640,480,16);
   setupOpenGL(640,480);

   // Her top nesnesi için rastgele koordinat ve renk değerleri
   // belirliyoruz.

   srand(time(NULL));
   ball.setPosition(0.1f * (rand()%100) - 5.0f,
                    0.1f * (rand()%100) - 5.0f,
                    0.1f * (rand()%100) - 5.0f);
   ball.setColor(0.01f * (rand()%100),0.01f * (rand()%100),0.01f * (rand()%100));
         
   while (!quit) {

      renderScene();
      
      // SDL de giris/cikis islemleri event ler vasitasi ile yurutulur.
      // Burada bir event nesnesi tanimi yapiyoruz.

      
      SDL_Event event;

      // SDL_PollEvent() fonksiyonu ile hazirda bekleyen birsonraki event i
      // event yapisi icerisinde elde ediyoruz.


      while (SDL_PollEvent(&event)) {

         // event.type bize event in turunu belirtir. SDL_KEYDOWN tipi tusa
         // basilma durumunda olusur.

         
         switch (event.type) {
         
            case SDL_KEYDOWN:
               
               // event.key.keysym.sym degiskeni basilan tusun kodunu icermektedir.
               // SDL icerisinde tus kodlari SDLK_* biciminde tanimlanmistir. Kod
               // SDLK_ESCAPE ise kontrol degiskenimizi gunleyerek donguden cikma isini
               // gerceklestiriyoruz.
               

               switch (event.key.keysym.sym) {
                  case SDLK_ESCAPE:
                     quit = true;
                     break;

                  // Yukari ok tusuna basiliyor ise kuzeye(-z yonu) gitme olayini aktif et.
                  // Ayni anda hem kuzey hemde guneye(-z yonu) gidilemeyeceginden
                  // guneye gidilme durumunu ortadan kaldiriyoruz.

                  case SDLK_UP:
                     mov_north = true;
                     mov_south = false;
                     break;


                  // Yukarida yaptigimiz islemi bu seferde tersi durum
                  // icin gerceklestiriyoruz.

                  case SDLK_DOWN:
                     mov_north = false;
                     mov_south = true;
                     break;


                  // +x ve -x yonlerindeki hareketler icin yine +z -z
                  // durumlarinda davrandigimiz gibi davraniyoruz.

                  case SDLK_LEFT:
                     mov_east = false;
                     mov_west = true;
                     break;


                  case SDLK_RIGHT:
                     mov_east = true;
                     mov_west = false;
                     break;


               }
               break;

            // Basilan bir tus birakildigi zaman SDL_KEYUP event i olusur.
               
            case SDL_KEYUP:

               switch (event.key.keysym.sym) {
                  
               
                  // kuzeye gidilme durumunu iptal ediyoruz..
                  case SDLK_UP:
                     mov_north = false;
                     break;


                  // guneye gidilme durumunu iptal ediyoruz..
                  case SDLK_DOWN:
                     mov_south = false;
                     break;


                  // batiya (-x) gidilme durumu ortadan kaldiriliyor
                  case SDLK_LEFT:
                     mov_west = false;
                     break;


                  // doguya (+x) gidilme durumu sonlandiriliyor.
                  case SDLK_RIGHT:
                     mov_east = false;
                     break;


               }
               break;

            // Pencerenin kapatma tusuna basilmasi ise yine bir event uretir.
            // Bu event in tipi SDL_QUIT dir. Bu durumda da yine cikis islemini
            // yapiyoruz.


            case SDL_QUIT:
               quit = true;

         }
      }

   }
   
    return 0;
}


Yukarıdaki koda dikkat ederseniz, topa yön verme işlemlerini tuşa basılmaları kontrol ettiğimiz ana yordam içerisinden yapmadığımızı görürsünüz. Eğer böyle yapmış olsaydık kesikli bir hareket elde ederdik (Bunun sebebi klavyede tuşa basılma olayının donanım tarafından kesikli biçimde taranmasıdır.). Kontrol değişkenleri aracılığı ile kesiksiz, düzgün bir hareket elde etmiş olduk.

2. Oyun Alanı Sınıfının Oluşturulması:

Şu ana kadar görüntülediğimiz top nesnesi altında bir yüzey olmadığı için havada asılı olarak duruyordu. Şimdi isterseniz oyun alanı olarak kullanacağımız yüzeyi ifade eden nesneyi oluşturalım. Bu sınıfı yine Ball sınıfında yaptığımız gibi Entity sınıfından türeterek oluşturacağız. "render()" yordaminda ise şimdilik düz bir dörtgen yüzey görüntülemekle yetineceğiz. Bu sınıfımıza "Arena" adını verelim. Aşağıda sınıfa ait kod görüntüleniyor.

// --- arena.h --------------------

#ifndef __ARENA_H__
#define __ARENA_H__

#include "entity.h"

class Arena : public Entity {

public:

   Arena(int width=30,int height=30);
   ~Arena() {};

   void setWidth(int w);
   void setHeight(int h);

   virtual void render();

private:

   int width, height;


};

#endif

// --- arena.cpp --------------------

#include "arena.h"

Arena :: Arena(int width,int height) {
  
   setWidth(width);
   setHeight(height);

}

void Arena :: setWidth(int w) {
   width = w;
}

void Arena :: setHeight(int h) {
   height = h;
}

void Arena :: render() {

   glPushMatrix();
   glTranslatef(position.x,position.y,position.z);
   glRotatef(90,1,0,0);
   glRectf(-width/2,-height/2,width/2,height/2);
   glPopMatrix();

}


Oluşrurduğumuz arena nesnesini görüntüleyen ve kameranın konumunu arena nın tümünü gösterecek şekilde ayarlayan değişiklikler "main.cpp" içerisinde yapılmıştır. Bu kodun tamamını sitemizin dosyalar bölümünden indirebilirsiniz. Gelecek dersimizde oyunun amacına karar vereceğiz(nihayet :) ve bu doğrultuda yeni elemanlar eklemeye devam edeceğiz.
Deniz Aydınoğlu :: 2002





Bu yazının bulunduğu site: OyunYapimi.org
http://www.oyunyapimi.org

Bu yazı için adres:
http://www.oyunyapimi.org/modules.php?name=Sections&op=viewarticle&artid=4