////////////////////////////////////////////////////////////
//
//    RUH KAPANI v0.1
//
//    Mehmet Deniz Aydinoglu . 2004
//    (deniz@oyunyapimi.org)
//
//    Lisans: GPL (license.txt)
//
//    www.oyunyapimi.org
//
////////////////////////////////////////////////////////////

Ruh Kapani, oyunyapimi.org sitesinde oyun ve 3d progamlamaya yeni
baslayanlara ders olmasi amaci ile yayinlanmak uzere gerceklestirilmis bir
oyun projesidir. Asagida oyunun kod yapisi hakkinda bazi ust seviyeli aciklamalar
bulunmakta. Ayrica oyunun kodu icerisinde bulunan olan aciklama satirlarina da
bakilmasini tavsiye ediyorum (ozellikle *.h dosyalari ve App.cpp icerisinde bulunan
aciklama satirlari incelenmelidir.)

-----------------------------------------------
* Genel Olarak Kullanilan Siniflarin Aciklamasi
-----------------------------------------------

Oyun dongusu ve tum kontrol ve yonlendirme islemleri App sinifi eleman fonlsiyonlari
ile gerceklestirilmekte. App sinifi Lib3d::BaseApp sinifindan turuyor ve bu sayede
birkac fonksiyon cagirmasi ile bize ayarlanmis bir pencere ve persfektif camera
kontrolu sagliyor. main.cpp programin giris noktasi. Buradan hemen bir App nesnesi
yaratilip, ekran ozelliklerini ayarlayan birkac parametresi degistirilerek start() 
yordami ile oyun dongusune giriliyor. Oyun dongusu boyunca bir tusa basma durumunda
app sinifinin event fonksiyonlari cagiriliyor. Ayrica daha da onemlisi devamli olarak
onRender() yordami cagiriliyor. 

Oyunumuzun tum kontrol ve yonlendirme islemlerini tus ve mouse eventleri sonucu
cagirilan App sinifi event fonksiyonlari icerisinden gerceklestiriyoruz. Bu
fonksiyonlar: onKeyPressed() ve onMouseButtonPressed() 

Oyunda cizim ve zamana bagli degisimleri ayarlayan, ayrica oyunun mantiginin isledigi
bolum ise onRender() fonksiyonun icerisinde bulunuyor. Bu fonksiyon devamli olarak
cagiriliyor (Kullandigimiz Lib3d frameworku bu isi otomatik olarak yapiyor.). Biz bu
fonksiyon icerisinde bir zamanlayici kullanarak oyunun akisinda zamana bagli degisen
fonksiyonlari gerceklestiriyoruz. Ayrica oyun isleyisini olusturan obje hareketlerinin
gerceklestirilmesi, cizim islemleri, carpisma kontrolleri gibi tum mantiksal ve grafiksel
ogelerin isleyisi de yine onRender() fonksiyonu icerisinde gerceklestiriliyor.

Oyundaki kurukafalar (Head) ve bonuslar (HealthBonus ve DestroyerBonus) MovingObject isimli
siniftan turemekteler. MovingObject sinifi ekranda cizilebilen, belirli waypointler arasinda
hareket edebilen, belirli durumlara sahip olabilen (hareket halinde, waypointe geldi,
hareket bitti gibi..) bir pozisyone ve yonelim bilgisine sahip yapida. Ayrica kendi icinde
basit carpisma kontrolude yapabiliyor. HealthBonus ve DestroyerBonus siniflari bu siniftan
turemekle beraber MovingObject sinifinin yonelim fonksiyonelligini kullanmiyorlar. Bu
cisimler sabit olarak haritada duruyor, carpisma durumunda ise yok oluyorlar. render()
fonksiyonu icerisinde ise kendilerini ekrana cizmekle yukumluler. Ayrica devamli olarak
cagirilan update() fonksiyonunda zamana gore sekillerini de degistirebiliyorlar
(donme efekti..)..

Head (kurukafa) sinifi ise yine MovingObject sinifini temel alan bir sinif. Fakat bu sinif
kendi icinde MovingObject sinifinin hareket etme ve waypointler arasinda dolasabilme
ozelligini kullaniyor. Oyunda bir kurukafa yaratildigi zaman gidecegi pozisyon waypoint
olarak ekleniyor. Boylelikle kurukafa o pozisyona gidip, daha sonra bekler duruma otomatik
olarak gecebiliyor. Yine render() fonksiyonu icerisinde kurukafa tipine gore cizim
islemi yapiliyor. update() de ise kurukafanin gittigi yone gore donmesi isi yapiliyor.
MovingObject sinifi icerisinden waypointler arasinda dolasma isi otomatik olarak gercekles
tirildigi icin bu islemin Head sinifinin update() yordaminda birdaha gerceklestirilmesi
gerekli olmuyor.

Target ve Trap siniflari ise cok daha basit siniflar. Target; mouse ile yonetebildigimiz
hedef tahtasini belirtiyor. Bu sinif pozisyon bilgisine sahip. render() fonksiyonu ile
kendini ekrana ciziyor. updateMouseCoordinates() fonksiyonu her cagirildiginda yeni
mouse kordinatlarina gore 3 boyutlu kordinatlarini ayarlayabiliyor. Trap sinifida Target
a benzeyen bir sinif. render() fonksiyonu ile kendini ciziyor. update() fonksiyonu icerisinde
tuttugu bir zamanlayici sayesinde yasam suresini hesaplayabiliyor. Yasam suresi sonunda
kendini -olu- durumuna getiriyor. Ayrica yine zamana gore kendi durum degiskenlerini
ayarlayabiliyor. (Ornegin ilk olusum asamasinda konumunu degistirmek ve particle sistemini
baslatmak gibi...)

SoundManager sinifi tum ses calinmasi ile ilgili islemlerin denetlenmesinden sorumlu.
Bu siniftan sadece bir adet yaratiliyor. Ses seviyesinin degistirilmesi ve acilip-kapanmasi
gibi alt seviye fonksiyonlarin yaninda, istenilen bir sesin calinmasi (mesela oyuncunun
aci cekme sesi...) gibi yuksek seviyeli islemleride yerine getirebiliyor. Bu sinif FMOD
kutuphanesine bagimli olarak calisiyor.

UIManager sinifi ise oyundaki kullanici arabirimi ogelerini kontrol etmemizi saglayan
baska bir yonetici sinif. Bu sinifin fonksiyonlarini kullanarak basit kullanici arayuzleri
olusturabiliyoruz. Label, Button, Banner, Menu gibi gerceklestirilmis alt arabirim
siniflari bulunmakta. Button ve Menu lerden gelen event lerin iletim isini de yine UIManager
fonksiyonlari ve callback mekanizmasi kullanarak gerceklestiriyoruz. App sinifi ayni zamanda
bir UIOwner oldugu (bu siniftanda turedigi -yani multiple inheritance..-) icin UIMAnager
tarafindan gonderilen eventleri UIEvent_Callback() fonksiyonu ile alabiliyor.

Intro sinifini yine bir manager sinif olarak gorebiliriz. Bu sinifin gorevi; oyunun ana
menusu sirasinda arkada yer alan logoyu ve kurukafa akisini saglamak. update() yordami
ile akisi denetlerken, render() icerisinde logo ve kurukafalari cizme isini hallediyor.

HighResTimer sinifi ise adindan da kolayca anlasilacagi uzere genel kullanima sahip yuksek
cozunurluklu bir zamanlayicidan baska birsey degil. Oyun icerisinde zamanin gecisini kontrol
etmek isteyen tum siniflar bir sekilde HighResTimer dan tureyen bir nesneyi kullaniyorlar.

-----------------------------------------
* Oyunun Isleyis Dongusu ve Temel Mantigi
-----------------------------------------

App sinifi main icerisinde yaratildiktan sonra start() yordaminin cagirilmasi ile oyun
dongusune giriliyor. Burada BaseApp sinifindan tureme ve dolayisiyla lib3d framework une
bagli olarak calismanin sagladigi bazi kolayliklardan otomatik olarak faydalaniyoruz.
Ornegin pencere acma ve basit olarak kamera konumlandirma gibi isleri otomatik hallediyoruz.
BaseApp::simpleCamera nesnesini kamera konumlandirmasi icin kullaniyoruz. Ayrica framework
bir tus yada mouse eventi sirasinda bizim ilgili event fonksiyonlarimizi cagiriyor. Ve en
onemliside onRender() fonksiyonumuz devamli olarak cagiriliyor, ki bu fonksiyonda oyunun
mantigi ve cizim islemleri gibi onemli fonksiyonellikler gerceklestiriliyor.

Oyunda kurukafa, bonus ve kapan gibi nesneler dinamik olarak yaratilip isleri bittiginde ise
yokediliyor. Bu nesneleri listeler ile bir arada tutuyoruz. App sinifinda yer alan
headList, trapList ve bonusList listeleri bizim icin cok onemli. Her yeni yaratilan
kurukafa headList, kapan trapList ve bonus ise bonusList e ekleniyor. Oyunda cizim islemleri,
carpisma kontrolleri ve update islemleri sirasinda bu listeler dolasilarak liste elemanlari
ile islem yapiliyor.

Mouse ile yonettigimiz hedef sembolu yaratilan bir Target nesnesi ile kontrol ediliyor.
SoundManager olarak kullanilmak uzere bir adet SoundManager nesnesi yaratiliyor.
Yazi yazma islemleri icin ise glFont kutuphanesinin kullandigi GLFONT yapisi olusturuluyor.
Tum zamanlama islerini ustlenecek timer nesnesi ve intro yu goruntulemekten sorumlu intro
nesneside yaratilan onemli nesneler arasinda.

Oyunda basit bir durum makinesi (state machine) kullanaliyor. Bu sayede oyunun o anda
hangi durumda oldugu denetlenebiliyor. Icinde bulundugu duruma gore yapilacak islemler
secilebiliyor. Ornegin oyun GS_INTRO durumunda iken intro goruntuleniyor, GS_GAME durumunda
ana oyun mantigi isletiliyor ve GS_RENDER_SCORES da ise skor tablosu goruntuleniyor.

renderGame() yordami tum oyun ici akisi denetleyen bir konumda. Bu fonksiyon icinde
carpisma kontrolleri ilgili listeler taranarak gerceklestiriliyor (head - trap kontrolleri).
Ayrica yine tum listeler taranarak nesnelerin update() ve render() yordamlari ayri ayri
cagiriliyor. Target nesnesinin mouse durumuna gore pozisyonunun degistirilmesi ve ekrana
cizilmeside yine bu fonksiyondan cagirilarak gerceklestiriliyor. Oyunda ne zaman bir kurukafa
yada bonus yaratilacagi gibi islemlere karar verilmesi ise runGameLogic() fonksiyonundan
gerceklestiriliyor. Bu fonksiyon ise renderGame() foksiyonu icerisinden cagiriliyor.

--------
* Notlar
--------

Terrain sinifi -kullanilmamaktadir-. Oyunu tasarlarken duz bir zemin kullanmak yerine 
bir yukseklik haritasina gore calisan bir yuzey (terrain) kullanmayi dusunmustum.
Lib3d icerisindeki Terrain sinifi tam olarak bu ise uygundu. Fakat oyunu kodlama sirasinda
o an icin pekte ugrasmak istemedigim bazi problemler ile karsilastim ve simdilik terrain
yerine duz bir yuzey kullanarak durumu gecistirdim. Daha sonra ise acikcasi tekrar
terrain kullanan bir yapiya gecmek icimden gelmedi (-yani biraz tembellik yaptim-)

Golge kullanimi oyun yayinlanma asamasina geldikten sonra eklendi ve acikcasi biraz
yama gibi oldu. Bazi buglari var ama idare ediyor. Sagdan soldan copy+paste yaptim ve
uzerinde kendi sistemime oturtmak icin de bikac modifikasyonda bulundum. Yontem
plane projected shadows olarak adlandiriliyor. Basitce: yer poligonu uzerine projekte
edecek sekilde bir transformasyon matrisi aktif hale getiriliyor. Kurukafa texture suz halde
ve blending acik olarak bu yuzeye render ediliyor. Islem bu kadar basit. Stencil ise
ustuste 2 golgeyi basmamak icin kullaniliyor. (ama islemiyor :/ neden??)
Bu kullanim ile her Head cizimi icin 2 pass yapmak gerekiyor ama sistem fazla kasilmiyor
yinede. Golge render ederken Head geometrisi yerine daha basit bir poligon mesh
kullanilabilir ama yapmadim - evet tembelim-.. Ayrica bu golge olayinin gerceklestiriminde
bikac ufak bug da var, duzeltmek size dusuyor..

Head sinifini olustururken waypoint e gore hareket etmeyi dusunmemistim acikcasi. Bu yuzden
MovingObject sinifi sadece bir hedefe hareket edebilecek fonksiyonellikte tasarlandi. Daha
sonra farkli bir dusman (mavi kurukafa) ekleme fikri kafamda olustu. Bu dusman bir kapana
rastladiginda kendi belirledigi baska bir yone gidecekti, yani kacacakti. Bu isi genel
bir yontemle gerceklestirmek icin waypoint ozelligini kullanmak aklima geldi. MovingObject
sinifini bu ozellige gore yeniden duzenledim, halihazirdaki Head sinifini tekrar isler
duruma getirmek biraz vaktimi aldi -ve acikcasi biraz kirli bir is oldugunu kabul ediyorum-.
Mavi kurukafa istedigim gibi hareket ediyordu artik fakat oyun icerisindeki hareket tarzi
sonradan pek hosuma gitmedi ve oyundan kaldirdim. Kod olarak tam destek bulunuyor sadece
runGameLogic() fonksiyonu icerisinde yaratilmiyor. Bir satir degistirmek ile oyuna tekrar
eklenebilir. Fakat waypointde gezmede bi bug var sanirim, bazen 2 waypoint arasinda sonsuz
donguye girip devamli olarak dolasabiliyor.

Oyunda ayrica yuksek skorlari internet uzerinden bir server a gonderme imkanida bulunuyor.
Bunu yazdigim ufak bir ek kutuphane ile gerceklestirdim. Ama bunun kodunu yayinlayamiyorum.
Ileride baska projelerde de hile ile savasmak icin bu mantigi kullanmak istiyorum
ve acikcasi sifreleme tarzini belli etmekde isime gelmiyor tabiki. Sifreleme ve veri
aktarimi ile ilgili tum fonksiyonellik http.lib isimli kutuphanede bulunuyor.

config.ini icerisinde bazi konfigurasyon ve oyun mantigi ile ilgili parametreler
degistirilebiliyor. Bunlarin tam olarak etkilerini ve kullanimini ogrenmek icin
main.cpp yi incelemeniz gerekiyor.

***********************************************

Uzun bir yazi oldu. Umarim yardimci olur. :)

Mehmet Deniz Aydinoglu :: deniz@oyunyapimi.org :: www.oyunyapimi.org

* son gozden gecirilme tarihi:
[4.ocak.2005]
[deniz]

