Delphi İle Oyun Programlamaya Giriş
(2624 kelime) (1369 okuma)
Delphi İle Oyun Programlamaya Giriş:
Bu dokümanda Delphi ile oyun programlamaya basit bir örnekle giriş yapacağız.
Delphi günümüz programlama dilleri arasında oldukça popüler ,ancak gariptir ki bir o
kadar da tercih edilmeyen bir dildir. Bunun sonucu olarak Delphi ile oyun programlama
konusunda internette çok fazla kaynak bulunmaz. Özellikle Türkçe kaynak birkaç forum
yazışması ve PC Magazine'in özel oyun programlama köşesi(mişli geçmiş zaman)
dışında hiç yoktur.Bu eksikliğin yerini bir parça olsun doldurabilmek için bu
dökümanı hazırladım.Bu dökümanda mümkün olduğu kadar basit bir dil kullanmaya ve
yapılan herşeyi açıklamaya çalıştım ancak gene de temel bir Delphi bilgisine ve
programlama mantığına ihtiyacınız var.
Peki, önemli misyonumuzun temellerini anlattıktan sonra öncelikle malzeme listesini
verelim ve kolları sıvayıp işe koyulalım.
Gerekenler:
- Delphi 3 ve yukarısı olmak şartıyla bir Delphi geliştirme ortamı:(www.borland.com
dan freeware geliştirmek şartıyla, ücretsiz temin edebilirsiniz.)[140 MB]
- DelphiX component package:[800-900 KB]:Bu bileşen paketi Delphi
altından DirectX kullanımını sağlar,tabi ki tek alternatif değil,ancak şu anda en
derli toplu paket bu.Bu paketi kullanabilmek için sisteminizde DirectX 7 veya daha
yüksek runtime'ın kurulu olması gerekir.Yani SDK'ya ya da header ihtiyacınız yok,bir
başka deyişle sisteminizde DirectX kullanılarak yapılmış oyunları
oynayabiliyorsanız başka bir şeye ihtiyacınız yok demektir.
Delphi 6 için: http://Delphigamedev.name2host.com/files/Delphix_d6.zip
Diğer sürümler için: http://www.yks.ne.jp/~hori/
- En azından basit düzeyde Delphi bilgisi.(Yani,projeye bir DXDraw nesnesi ekleyin
dediğim zaman bunu sorunsuzca uygulayabilmeniz.)
- Klasik olarak, biraz sabır.
Öncelikle DelphiX bir DirectX bileşeni olduğu için temel olarak DirectX hakkında
biraz söz etmemiz gerekiyor.İsterseniz soru cevap şeklinde gidelim,böylece daha
eğlenceli ve eğitici bir yazı olur.
Sık sorulan sorular:
Soru 1) DirectX nedir?
Cevap 1) DirectX, Microsoft tarafından üretilen multimedia donanımına doğrudan ve
hızlı erişim sağlayan ,özellikle oyun yapımında kullanılan bir kütüphaneler
topluluğudur.
Soru 2) DirectX tam olarak nelerden oluşuyor?
Cevap 2) DirectX'in temel bileşenleri;
- DirectDraw: 2 boyutlu çizimleri sağlayan bileşen
- DirectSound:Ses (Wave,Midi) çalınmasını sağlayan bileşen
- DirectInput:Kontrol aygıtları arayüz bileşeni
- DirectMusic:DirectSound'dan türeme bir bileşen(DelphiX tarafından desteklenmiyor,çok
işe de yaramıyor açıkçası)
- DirectPlay: Çokluoyuncu desteği sağlayan bileşen
- Direct3D: Tüm 3D çizim arayüzünü yöneten bileşen.
Soru 3) Neden DirectX( ya da openGL,yani neden multimedia APIsi) kullanmak gerekir ?
Cevap 3) Pek çok avantaj nedeniyle,kısaca saymak gerekirse:
- Kolaylık: Mesela multiplayer bir oyun yapmaya çalıştığımızı düşünelim,eğer
DirectX kullanmazsak bilgisayarlar arasındaki iletişimi sağlayacak kodu da bizim
yazmamız gerekir. Bu fazladan efor demek olur,ve de hata olasılığını artırır.
Burada DirectX herşeyi bizim yerimize yapar gibi de düşünmemek gerekir, ancak işleri
önemli bir biçimde hafiflettiği ortadadır.
- Hız: DirectX bilgisayarın grafik donanımına doğrudan erişir,bu da
uygulamalarımızın çok daha hızlı işlemesini sağlar.
- Yaygınlık: yaygın 3D API'ler için örnek kaynak kod bulmak da oldukça kolaydır.
Soru 4) Peki , ben Delphi üzerinden DirectX kullanarak her türlü oyunu rahatlıkla
programlayabilir miyim?
Cevap 4) Valla rahatlıkla mı olur onu bilemem ama,Delphi (ve DelphiX ) her türlü
projede kullanabilir ve hız açısından hiçbir dezavantaj yaşamazsınız.
Soru 5) DelphiX'in en önemli avantajı nedir?
Cevap 5) Kullanımının son derece kolay olması.İnanın VB daha kolay bir dil olmasına
rağmen, VB'de DirectX kullanmaktan çok daha kolay DelphiX.
Soru 6) Öyleyse neden Delphi ticari oyun programlamada kullanılmıyor?
Cevap 6) Bu bir kullanım alışkanlığı meselesidir,"Bu sektördeki insanlar daha
çok C ve C++ tercih ettikleri için" yanıtı çok yanlış olmaz.
DelphiX kurulum:
Şimdi sisteminizde herhangi bir Delphi sürümünün kurulu olduğunu varsayarak
DelphiX'i kuruyoruz.Bu noktada en önemli şey sizin sahip olduğunuz sürüme uygun
paketi kurmak.Ben Delphi6 kullanmama rağmen bulamadığım için birkaç kez eski
paketlerden kurmaya çalıştım ancak hep sorun çıkartıyordu,ve sonunda 6. sürüm
için olanını bulup rahat ettim.
DelphiX'i kurmak için indirdiğiniz zip dosyasını boş bir klasöre açıp sizin
sürümünüze uygun dpk uzantılı dosyayı (mesela DelphiX_for3.dpk) çift
tıklayın.Karşınıza gelen diyalog kutusunda Install'ı tıkladıktan sonra DelphiX ile
çalışmaya başlayabilirsiniz.
Öncelikle yeni install ettiğimiz bileşenlere bir bakalım:
Bileşen Adı |
Bileşen Açıklaması |
TDXDraw |
Global DirectX bileşeni DelphiX kullanabilmek için her projede bir tane
tanımlı olması gerekir.Bütün surface'ler ve diğer bileşenlerin parent object'idir. |
TDXDib |
Bir tane aygıttan bağımsız bit eşlem resimini (Device Independent
Bitmap-DIB) tutar. |
TDXImageList |
Birden fazla DIB resmini saklamaya yarar,özellikle iki boyutlu oyunlarda
grafik yönetimini kolaylaştırır.İçeriğini DelphiX'in özel formatı olan *.dxg
dosyalarına saklayabilir ya da bu dosyalardan içerik yüklenebilir.(Quake 1-2 ya da Half
Life'ın *.wad formatı gibi düşünebilirsiniz.) |
TDX3D |
Bu bileşen eski DirectX sürümleriyle uyumluluk amacıyla
konulmuştur.Kullanılmasına gerek yok. |
TDXSound |
DirectSound nesnesinin kullanımını sağlar,TDXWave veya
TDXWaveList'teki dosyaların çalınabilmesi için projede bir tane bulunmalıdır,ve bu
bileşenlerin DXSound özelliklerine bu bileşen seçilmelidir. |
TDXWave |
Bu bileşen aynı TDXDIB gibidir,ve tek bir wave dosyasını saklayabilir. |
TDXWaveList |
Bu bileşen de aynı TDXImageList gibi birden fazla wave dosyasının
saklanmasında kullanılır,kendi dosya uzantısı *.dxw'dir. |
TDXInput |
DirectInput nesnesinin kullanımını sağlar,yani klavyeden mouse'tan ve
joystickten gelen mesajları algılamamızı sağlar. |
TDXPlay |
DirectPlay nesnesinin kullanımını sağlar,Multiplayer oyunlarda
kullanılır. |
TDXSpriteEngine |
Özellikle Sprite içeren iki boyutlu oyunlarda kullanılır,Resim
saklama,koordinat tutucu ve çarpışma denetimi gibi gelişmiş özellikler içerir. |
TDXTimer |
Delphi'nin System bileşenleri altında bulunan Timer'dan çok daha hassas
bir timer,genellikle her oyunda bulunan genel döngüyü içerir. |
TDXPaintBox |
Delphinin standart TImage'inin DelphiX versiyonu. |
İşte ilk projemiz:
1.Delphi'de File->New Application'ı seçerek yeni bir proje
oluşturun. Öncelikle DirectX çizim kütüphanelerini kullanabilmek için Formumuza bir
adet TDXDraw Nesnesi eklemeliyiz.Bu nesne varsayılan olarak 100x100 boyutlarında bir
kontrol olarak gelir.DxDraw1'in ve Formunuzun Width ve Height
özelliklerini 640x480'e ayarlayın.DXDraw1'in Forma otomatik olarak yerleşmesini
istiyorsanız DXDraw1'in Align özelliğini alClient
olarak değiştirin.DXDraw1'i formumuzun istediğimiz herhangi bir yerine taşıdıktan
sonra,ikinci önemli bileşen olan TDXTimer'dan formumuza bir adet eklemeliyiz.Bu
bileşeni eklememizin nedeni yukarıda da belirttiğim gibi hemen her oyunda bir genel
döngünün bulunması gerekliliğidir.(Bunu aşağıda daha ayrıntılı
açıklayacağım) DXTimer'ımızın Interval özelliği normal
Timer'larda olduğu gibi saniyenin 1000de kaçında işlem yapacağını gösterir.Biz
mümkün olan en yüksek FPS'yi(frame per second,yani bir görüntünün 1 saniyede kaç
kez çizildiği) elde etmek için bunu 0 olarak ayarlıyoruz
2. Şimdi ekrana bir resim dosyasını çizebilmek için bir Surface
tanımlayacağız(Surface nedir:Surface DirectDraw'un temelidir denebilir,surface üzerine
çizim yapabileceğimiz,ya da bir dosyadan içerik yükleyebileceğimiz bir hafıza
alanıdır,Surface'leri TImage bileşenleri olarak düşünebilirsiniz,tek fark siz
istemedikçe Surface'lerin ekranda görünmemesidir.Surfacelerin içeriği siz tersini
seçmedikçe video kartın belleğinde saklanır.),aslında bu işi bir DxImageList
kullanarak çok daha kolay bir biçimde halledebiliriz,ancak ben Surface tanımlamanın
çok daha faydalı ve ileriye dönük bir yöntem olduğunu düşünüyorum.Neyse kod
pencerenizde TForm1 class'ının private yordamlarının
altına alttaki eklemeyi yapın :
ilkyuzeyim:TDirectDrawSurface;
Yani şöyle olmalı:
type
TForm1 = class(TForm)
DXDraw1: TDXDraw;
DXTimer1: TDXTimer;
private
ilkyuzeyim:TDirectDrawSurface; //Bizim
yaptığımız ekleme
{ Private declarations }
public
{ Public declarations }
end; |
3. Şimdi bu Surface'i kullanmak için gereken kodları
yazacağız.Öncelikle Delphi'de herhangi bir nesneyi kullanmak için her zaman gerektiği
gibi bu Surface'i de (create) yaratmalıyız.Bu işlem için en uygun yer DXDraw
nesnemizin onInitialize event'idir.Formumuzda DXDraw1'i seçtikten sonra
Object Inspector'un Events sekmesine gelip onInitialize kısmındaki
boşluğu çift tıklayın.Önünüze gelecek kod penceresine aşağıdaki kodları
ekleyin:
procedure TForm1.DXDraw1Initialize(Sender:
TObject);
begin
ilkyuzeyim:=TDirectDrawSurface.Create(Dxdraw1.DDraw); //Surface'i DXDraw'un DirectDraw nesnesini
kullanarak yarat.
ilkyuzeyim.LoadFromFile(extractfilepath(application.ExeName)+'gordon.bmp');//Surface'e uygulamamızla
aynı klasörde bulunan "gordon.bmp" dosyasını yükle.
Extractfilepath fonksiyonu herhangi bir dosya
isminden dosya yolunu ayırır,yani extractfilepath
("c:\levent.txt") ,"c:\" değerini döndürür.
end; |
4. Şimdi final kısma geliyoruz.Çizim nesnesini(DXDraw1) ekledik,çizim
kaynağı olan surface'i(ilkyuzeyim) yarattık ve içeriğini yükledik,şimdi surface'den
DXDraw'a çizim yapacağız,gerekli kodu DXTimer'ın onTimer metodunun
altına yazmamız gerekiyor.Burada küçük bir parantez açıp neden bir kez ilk
çalışma anında çizim yapıp daha sonra bunun üzerine devam edemeyeceğimizi (yani
neden bir döngüye ya da Timer'a ihtiyacımız olduğunu) kısaca açıklayayım,eskiden
DOS'ta programlama yaparken DOS'un single-tasking yapısı sayesinde programınız(ya da
oyununuz) çalışırken ekrana erişen başka hiçbir yazılım olmayacağını
bilirdiniz,bu da ekrana önceden ne çizildiğini hafızada tutma gerekliliğini ortadan
kaldırıyordu.Ancak Windows multi-tasking bir işletim sistemi olduğu ve sizin
programınızla beraber pek çok uygulamayı da çalıştırdığı için sizin pencereye
kendini yeniden çizmesi gerektiği emrini belirli aralıklarla vermeniz gerekir,yoksa
ekrana erişen başka uygulamalar görüntüde kalır. Ayrıca oyun içinde yeri değişen
objelere göre arkaplan ve objeler yeniden çizilmezse(ve bu işlem çok hızlı
yapılmazsa) ekranda flicker denilen bir önceki karenin gölge gibi arkadan gelmesi
sorunu ortaya çıkar.Biz de burada bunları önlemek için DXTimer kullanıyoruz ve
Interval'i düşük tutuyoruz.Neyse çok konuştuk biraz da iş yapalım,alttaki kodu
DXTimer1'in onTimer event'ine ekleyin.
procedure TForm1.DXTimer1Timer(Sender:
TObject; LagCount: Integer);
begin
dxdraw1.Surface.Draw(0,0,rect(0,0,640,480),ilkyuzeyim,false);// dxdraw1 esas çizim nesnemiz olduğu
gibi,ona ait surface'e yapacağımız her çizim de ekranda
görünecektir.Burada Draw metoduyla,sırasıyla
ekrana çizim yapılacak koordinatları (x,y) kaynak dörtgeni (yani kaynak
surface'deki resmin ne
kadarını) ve de kaynak surface'in adını ve son olarak ta transparan
olarak çizilip çizilmeyeceğini
belirtiyoruz.Biz burada Draw diyerek 0,0 koordinatına kaynak resmin 0'dan
640'a genişlik ve 0'dan 480'e
boy olarak transparan olmadan çizilmesini sağladık.
dxdraw1.Flip;// Bu kısım dxdraw'a bütün
çizim bittikten sonra çağırılmalıdır.Dxdraw'un ekrana
basılmasını söyler.(yani flip edilmesini) Eğer eklemezseniz sistem
kilitlenebilir.
end; |
5. Herşey tamamsa Run'a basın ve Gordon Freeman'ı tüm karizmasıyla
görün.Tebrikler Delphi'de DirectX kullanarak ekrana bir resim çizdirmeyi başardınız.
Biraz derinlere:
6. Herşey bu kadar mı?,evet, en basit uygulama bu kadar,ancak ben bu
basit uygulamaya biraz daha özellik eklemek istiyorum.şöyle basit bir ateş etme efekti
fena olmaz değil mi?,bunun için 2. basamakta yaptığımız gibi yeni bir surface daha
ekleyeceğiz. Yine private yordamının altına
silah:TDirectDrawsurface;
kısmını ekleyin.ve 3. basmaktaki kodlara alttaki eklemeleri yapın
silah:=TDirectDrawSurface.Create(Dxdraw1.DDraw);
silah.LoadFromFile(extractfilepath(application.ExeName)+'Gun.bmp');
bu noktaya kadar bir farklılık yok,ancak şimdi ufak bir ekleme yapacağız,yukarıdaki
kodların hemen altına şu tek satırı ekleyin,bu silah surface'inin hangi rengi
transparan göstereceğini, yani çiz emri verdiğimizde hangi rengi çizmeyeceğini
belirtir:
silah.TransparentColor :=silah.Pixels[1,1];
DXDraw1 in onInitialize event'inin altı şöyle olmalı:
procedure TForm1.DXDraw1Initialize(Sender:
TObject);
begin
ilkyuzeyim:=TDirectDrawSurface.Create(Dxdraw1.DDraw);
ilkyuzeyim.LoadFromFile(extractfilepath(application.ExeName)+'gordon.bmp');
silah:=TDirectDrawSurface.Create(Dxdraw1.DDraw);
silah.LoadFromFile(extractfilepath(application.ExeName)+'Gun.bmp');
silah.TransparentColor :=silah.Pixels[1,1]; //Pixels[1,1]
surface'in 1,1 koordinatındaki rengi transparan
renk olarak seçer.Bu bizim resim dosyamızda eflatundur.
end; |
Şimdi bu surface'i de DXTimer'ın onTimer eventinin altında
çizdirmenin bir manası olmayacak o yüzden daha farklı bir uygulama
yapacağız.Aşağıdaki kodu DXDraw1'in onMouseUp eventinin altına
ekleyin:
procedure TForm1.DXDraw1MouseUp(Sender:
TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
ilkyuzeyim.Draw(x-16,y-16,rect(0,0,32,32),silah,true); // burada ilkyuzeyim surface'imizin üstüne silah
surfaceini transparan olarak çiziyoruz. X ve y koordinatları mouse'un
ekrana tıklandığı koordinatlar,x
ve y'den 16 çıkartarak gun.bmp resmini tam ortalıyoruz(resim 32x32
çünkü).Burada silah surfaceini
ilkyuzeyin üstüne çizmemizin nedeni (daha doğrusu doğrudan DXDraw1'in
üstüne çizmememizin nedeni)
DXDraw saniyenin 1000'de 1nde yeniden çizilirken buradaki kod sadece biz
ekrana tıkladığımızda
işleyecek bu yüzden eğer DXDraw'a çizersek biz tıkladıktan 1-2
milisaniye sonra ilkyuzey yeniden
DXDraw1'e çizilmiş olacak ve biz hiçbir şey göremeyeceğiz.Bu etkiyi
incelemek için timer'ın Intervalını
yükseltip DXDraw'a çizmeyi deneyebilirsiniz.
end; |
tamam yeniden çalıştırıp gordon abinize birkaç el ateş edin bakalım.
7. Bir de ses mi ekleyelim? Tamam bana uyar, ses efekti için bir wave
dosyasına ,sesleri çalabilmek için bir DXSound nesnesine ihtiyacımız var.projeye bir
tane DXSound nesnesi ekleyin. Yine 2. basmakta açıklanan private
yordamının altına gidin ve şu eklemeyi yapın:
silah_ses:TDirectSoundBuffer;
sonuç şöyle olmalı:
private
ilkyuzeyim:TDirectDrawSurface;
silah:TDirectDrawsurface;
silah_ses:TDirectSoundBuffer;
{ Private declarations }
public
{ Public declarations }
end; |
Şimdi 3.basamaktaki gibi bu ses buffer'ını yaratmalıyız,DXDraw1 onInitialize
olayına gidin ve aşağıdaki eklemeyi yapın:
silah_ses:=TDirectSoundBuffer.Create(DxSound1.DSound );
silah_ses.LoadFromFile(extractfilepath(application.ExeName)+'gun.wav');
sonuç şöyle olmalı:
procedure TForm1.DXDraw1Initialize(Sender:
TObject);
begin
ilkyuzeyim:=TDirectDrawSurface.Create(Dxdraw1.DDraw);
ilkyuzeyim.LoadFromFile(extractfilepath(application.ExeName)+'gordon.bmp');
silah:=TDirectDrawSurface.Create(Dxdraw1.DDraw);
silah.LoadFromFile(extractfilepath(application.ExeName)+'Gun.bmp');
silah.TransparentColor :=silah.Pixels[1,1];
silah_ses:=TDirectSoundBuffer.Create(DxSound1.DSound ); //mantık
çizim surface'i ile tamamen aynı
sadece burada ses objelerine referans veriyoruz.
silah_ses.LoadFromFile(extractfilepath(application.ExeName)+'gun.wav'); //Dosyamızı da yükledik.
end; |
Şimdi DxDraw1'in onMouseUp eventine gidip son eklemeyi yapın:
silah_ses.Play(false);
Yani sonuç böyle olmalı:
procedure TForm1.DXDraw1MouseUp(Sender:
TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
ilkyuzeyim.Draw(x-16,y-16,rect(0,0,32,32),silah,true);
silah_ses.Play(false); //false parametresi sesin
loop etmesini yani sürekli çalınmasını engeller,eğer true
verirseniz bir kere tıkladığınızda ses sürekli çalar.
end; |
Eveet, böylece işimiz tamamlandı.Umarım hoşunuza gitmiştir.İsterseniz sitemizde "dosyalar/döküman_ek" bölümünde de yer alan örnek proje dosyasını [ buradan ] indirebilir ve inceleyebilirsiniz.En karizma oyun
kahramanlarından olan Gordon Freeman'ı da böylece harcamış olduk ama olsun, önemli
olan bu yazıdan birşeyler öğrenmiş olmanız.Benden şimdilik bu kadar,herhangi bir
şey sormak için buradaki forumu kullanabilir ya da leventbbayar@operamail.com
adresine mail atabilirsiniz.Derslerim izin verdikçe daha başka DelphiX dökümanları
yazmayı düşünüyorum.Tabi bu sizden gelecek talebe de bağlı.
Levent Baykan Bayar :: 2003-03-03 :: www.oyunyapimi.org
|