Oyun Yapımı - 1
Oyun Yapimi - 1
1. Giriş:
Oyun yapımı ile ilgili bu döküman serisinde sizlerle birlikte
adım adım basit bir oyun yazılımı gerçekleştireceğiz. Yazılımın programlanması
sırasında yeri geldiğince kullanılan teknikler hakkında açıklayıcı
bilgilerde bulunup, ilgili grafik teknikleri hakkında bilgiler verilecek.
Bu şekilde hem bir oyun projesinde uygulanan yöntemler hakkında bilgi
sahibi olacak, hemde sonuçta üzerinde istediğimiz değişimleri
yapabileceğimiz bir oyun elde edeceğiz.
Geliştirme dili olarak C++ kullanacağız. Yazılımı nesneye yönelik
bir programlama mantığı ile geliştirmek düşüncesindeyim. Böylelikle
üzerinde eklemeler yapmak daha kolay olacaktır. Grafik kütüphanesi
olarak ise SDL kullanacağız. Geliştireceğimiz oyun 3 boyutlu olacağı için
bu alanda da OpenGL ‘den faydalanacağız. SDL kütüphanesini bir
pencere oluşturmak ve kullanıcıdan girdi almak için kullanacağımız
için aslında SDL kullanmak istemeyen arkadaşlar bu kısımları kendi
istedikleri API ile yazıp, diğer oyun ile ilgili nesneleri kendi programlarına
ekleyebilirler. Projenin SDL ye bağımlı kısımları en alt düzeyde olacağından
bu ayrım işini gerçekleştirmek inanıyorum ki çok zor olmayacaktır.
GLUT kütüphanesindeki bazı OpenGL yordamlarından da faydalanmak
istiyorum. Böylelikle daha az kod yazarak daha çabuk sonuçlara
ulaşabiliriz. Dileyen arkadaşlar GLUT, WinAPI gibi ortamları pencere yönetimi
ve girdi/çıktı için SDL yerine kullanabilirler. Kodumuzun
geri kalan kısımları zaten standart olan C++ yapıları ve OpenGL çağırımlarından
oluşacak. Ben gerçekleştirim platformu olaran Windows ve VC++ kullanacağım.
Windows platformunu bu ortamın yaygın kullanımı nedeniyle seçmiş
bulunmaktayım. İsteyen arkadaşlar daha farklı derleyici ve ortamları kullanabilirler.
SDL ve OpenGL kullanımı bu konuda bir sorun yaratmayacaktır. Ben kod içerisinde
derleyici ve işletim sisteminden bağımsız şekilde yapılar kullanacağım.
Bu projenin amacı sizlere C++ ya da OpenGL kullanımını öğretmek
değil. Bunu önceden belirtmek istiyorum. Amacımız bir oyun yazılımı
gerçekleştirme aşamalarını programlama yaparak özümsetmek.
Tabiki elimden geldiğince OpenGL çağırımlarını açıklayacağım,
SDL ile ilgili kısımlarda da gerekli açıklamalarda bulunacağım.
Fakat yinede özellikle OpenGL ile ilgili hiçbirşey bilmiyorsanız,
bu konuda bir kaynaktan başlangıç bilgilerini edinmenizi tavsiye
ediyorum. SDL kullanımı konusunda ise bir kaynağa ihtiyacınız olacağını
düşünmüyorum. Açıklamalarım başlangıç için
yeterli olacaktır. Evet, artık başlayabiliriz :).
2. SDL Kurulumu ve Derleyici Ayarları:
SDL kütüphanesini www.libsdl.org adresinden indirin ve örneğin
bilgisayarınızın c:\SDL dizini altına kurun. Kurulum işlemi indirdiğiniz
zip kütüğünü bu dizine açmaktan ibarettir. Bu
işlemden sonra yapacağınız bazı kopyalama işlemleri bulunmakta. İlk olarak
SDL içindeki include dizinini VC++ ın include dizinine kopyalamamız
gerekmekte. Bu işlem için öncelikle VC++ ın include dizinini
bulun ve bu dizin altında SDL isimli bir alt dizin oluşturun. Bu dizin içerisine
SDL include kütüklerini kopyalayın. SDL içerisindeki lib
dizininde bulacağınız sdl.lib ve sdlmain.lib isimli kütükleri de
yine VC++ ın lib dizini altına direk olarak kopyalayın. Sdl.dll isimli kütüğü
ise bilgisayarınızın Windows\System dizinine kopyalayın. Artık geriye sadece
VC++ derleyici ayarlarını yapmak kaldı.
VC++ ı açın ve yeni bir proje oluşturun. Proje tipi olaran Win32
application veya Win32 consol application seçebilirsiniz. Bu aşamadan
sonra çıkan diyaloglardan boş bir proje oluşturma seçeneğini
seçin. Proje oluşturulduktan sonra Project->Settings menüsüne
girin. Burada Link tabında bulunan Object/library modules kısmına sdl.lib
sdlmain.lib opengl32.lib glu32.lib glut32.lib satırlarını ekleyin. Sisteminde
opengl kütüphanesi ve glut kütüphanesi bulunmayan arkadaşlar
bunları sistemlerine kurmuş olmalıdırlar. VC++ kurulu olduğu için
OpenGL ile ilgili kütüphaneler zaten sisteminizde hazır olarak bulunuyor
olacaktır. Fakat GLUT kütüphanesini eğer daha önce kurmadıysanız
http://www.xmission.com/~nate/glut.html adresinden son sürümünü
indirip sisteminize kurun. Daha sonra C++ tabındaki code generation
bölümüne girin. Burada use runtime library kısmında multithreaded
DLL seçeneğini seçin. Evet böylece derleyici ayarlarını
da tamamlamış bulunuyoruz.
3. Proje Gerçekleştirim Yöntemi Hakkında:
İsterseniz kod yazma işine hemen dalmadan önce gerçekleştireceğimiz
yazılımın ana hatlarına karar verelim. Her yazılım projesinde olduğu gibi
oyunları da gerçekleştirirken programlama öncesi yapılan düzenli
bir tasarım aşaması daha sonra işimizi çok kolaylaştır. Biz burada
çok kapsamlı bir tasarım yapmayacağız. Çünkü proje
adım adım gelişirken kafamızda yeni fikirler oluştukça bunları ekleyeceğimiz
bir yöntem izliyoruz. Amacımız oyun yapımı teknikleri yönünde
ders nitelikli bir çalışma yapmak olduğu ve işin yazılım mühendisliği
kısmı ile ilgilenmeden öncelikli olarak oyun yapımının grafiksel gerçekleştirimi
yönü daha ağır bastığı için bu seçimi yaptım, lütfen
bu konuda acımasız yargılarda bulunmayın :). Derslerimiz ilerledikçe
gereksinim duyacağımız modülleri nesneye yönelik olarak tasarlayacak
ve kodlayacağız. Bu şekilde ilerleyerek kodlarımızı birbirine entegre etmemiz
kolaylaşacaktır. (Bakın, en azından yazılım mühendisliğinin ilkelerinden
birine sadık kalıyoruz ;) ).
Şimdi kod yazma işine girişelim isterseniz. Bu kadar teorik bilgi yeterli...
4. SDL ve OpenGL e İlk Adımlar:
Herşeyden önce üzerinde görsel işlemlerimizi yapacağımız
bir pencereye ihtiyacımız var. Bu iş için SDL kütüphanesini
kullanacağımıza göre, şimdi nasıl kullanacağımızı kod yazarak öğrenelim.
İlk olarak projemize main.cpp isimli bir kütük ekleyelim. Bu
kütük içerisinde C++ programımızın main() giriş yordamı
bulunacak. Aşağıdaki kodu bu kütük içerisine yazın.
#ifdef WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
#include <gl/gl.h>
#include <gl/glu.h>
#include <gl/glut.h>
#include <gl/glut.h>
#include <sdl/sdl.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc,char *argv[]) {
return 0;
}
Bu kodu hatasız bir biçimde derlediğinizden emin olun. Burada
yaptığımız tek şey SDL ve OpenGL ile ilgili include ları yapmak ve Win32
platformunda çalışıyor isek olmazsa olmaz olan windows.h ı include
etmek. Şimdi SDL ile ilgili başlangıç ayarlarını gerçekleştiren
ve bir pencere açan yordamı yazalım.
void initSDL(int width,int height,int bpp) {
// Bu yoram SDL kütüphanesini
başlatan yordamdır. SDL kütüphanesinin VIDEO
// ve zamanlama kısımlarını kullanmayı düşündüğümüz
için buna uygun
// parametreleri kullandık. Herhangi bir hata durumunda
ise SDL_GetError()
// yordamı ile hatanın sebebini öğrenip ekrana (yada
stdout.txt kütüğüne)
// yazdırıyor ve programdan çıkıyoruz.
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) < 0) {
fprintf(stderr,"SDL başlatımında hata!\n%s",SDL_GetError());
exit(-1);
}
// exit() yordamı çağırıldığında
buna ek olarak SDL_Quit() isimli SDL yordamının da
// çağırışmasını aşağıdaki kod ile belirtmiş oluyoruz.
SDL_Quit() yordamı SDL
// kütüphanesini kapatan yordamdır. Bu şekilde
artık program sonunda SDL_Quit()
// yordamını çağırmak yerine sadece exit() yordamı
ile bu işin otomatik gerçekleşmesini
// sağlamış olduk.
atexit(SDL_Quit);
// Bu yordam ile nihayet genişliğini,
yüksekliğini ve renk çözünürlüğünü
belirlediğimiz
// bir pencere açıyoruz. SDL_OPENGL parametresi
bu pencereyi OpenGL altından
// kullanacağımızı belirtiyor.
if (SDL_SetVideoMode(width,height,bpp,SDL_OPENGL) == NULL)
{
fprintf(stderr,”SDL ekranı açamadı!”);
exit(-1);
}
}
Şimdi main() fonksiyonu içerisinde yer alması gereken ana program
döngüsünü gerçekleştirelim. Tüm yazacağınız
oyun projelerinde şu yada bu şekilde bir ana döngünüz olmak
zorunda. Sonuçta devamlı olarak kullanıcıdan girdi almak, işlemler
yapmak ve ekrandaki görüntüyü tazelemek zorundasınız.
Bizim yazacağımız basit ana döngü kullanıcı ESC tuşuna basıncaya
kadar hiç bir şey yapmadan programı devam ettiriyor. Böylelikle
SDL tarafından oluşturulan pencereyi gözlemleyebiliyoruz. Main() yordamimiza
aşağıdaki şekilde eklentileri yapalım. Bu arada artık yazdığımız koda yeni
bir eklenti yapıldığı zaman bunu farklı bir biçimde yazarak belirteceğiz.
Böylelikle yeni yazılan satırları daha rahat takip edebilirsiniz
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);
while (!quit) {
// 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;
}
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;
}
Evet artık bir penceremiz var ve kullanıcıdan tuşa basılmaları almayı
da görmüş olduk. Fakat henüz penceremize birşey çizmediğimiz
için pencere içi sizinde gördüğünüz gibi
ekranda varolan görüntüyü aynen koruyor. Grafik uygulamalarında
görüntü oluşturma mantığı aslında çok basittir. Bir
görüntüyü devamlı olarak pencerenize çizersiniz,
işte bu kadar. Yani yapmamız gereken bir yordam hazırlamak, bu yordam içerisinde
pencere içine çizim yapmak ve bu yordamı ana program döngümüz
içerisinden devamlı olarak çağırarak görüntünün
tazelenmesini sağlamak. Fakat çizim işlemlerinde OpenGL kullanacağımız
için öncelikle OpenGL ile ilgili bazı başlangıç ayarlarını
yazmamız gerekmekte. Aynen SDL için yaptığımız gibi OpenGL içinde
bu ayarları gerçekleştireceğimiz bir yordam yazalım. Aşağıda bu
yordamı görüyorsunuz.
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,4,4000);
glMatrixMode(GL_MODELVIEW);
}
Bu yordam içerisinde çok basit olarak ilgili açıklamalar
yapıldı. Açıkçası bu açıklamalarda çok teknik
konuşmamaya dikkat ettim. Daha fazla bilgiyi OpenGL ile ilgili kaynaklarda
bulabilirsiniz. Aslında yukarıdaki kodu ilk seferde anlamanızda pek gerekli
değil. Yani anlamadım diye fazla üzülmeyin, bunlar neredeyse
standart olmuş OpenGL ayar prosedürleridir.
Şimdi çizim işlemini yapacak olan yordamı yazalım.
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);
// Bellekte olusturulan cizim pencere
icine aktariliyor. Bu sekilde
// goruntuyu ekranda gorebiliyoruz.
SDL_GL_SwapBuffers();
}
Bu yordam içerisinde ekranı sildik ve bellekte oluşturulan görüntüyü
çizmesi için SDL kütüphanesine ilgili komutu verdik.
Bu noktada teknik bir açıklama daha yapmak gerekiyor. Dikkat ederseniz
bellekte oluşturulan görüntü dedim. Bunu şöyle kafanızda
canlandırabilirsiniz. Bilgisayarınızın belleğinde bir alan üzerinde
çizim işlemlerinizi gerçekleştiriyorsunuz. Bu aşamada ekranda
bir görüntü göremessiniz. Çünkü bu
bellek bölgesi henüz grafik kartınızın çizimi gösterme
ile ilgili kesimlerine aktarılmamıştır. Çizim işlemlerimizi tamamladıktan
sonra SDL_GL_SwapBuffers() yordamı ile bu bellek bölgesini gösterme
talimatını veriyoruz. Böylelikle görüntü bir seferde
grafik kartına yönlendirilerek pencere içine çizilmiş
oluyor. Bu yönteme Double Buffering denilir. Peki neden direk grafik
kartının belleğini kullanarak çizim yapmıyoruz da, vakit kaybederek
önce başka bir belleğe çizip sonra bu belleği grafik kartına
aktarıyoruz? Grafik kartları görüntü belleklerini devamli
olarak tarayıp bulduğu veriler ile görüntüyü oluştururlar.
Eğer siz bir tarama işlemi sırasında görüntü belleği üzerinde
oynamalar yaparsanız ekrana çıkan görüntü üzerinde
çeşitli tutarsızlıklar oluşur. Bu tutarsızlıklar titreme biçiminde
kullanıcıya yansır ve hiçde güzel bir görünüm
oluşturmazlar. Yani siz siz olun bu tekniği kullanın, inanın zararını görmessiniz
:)
Yordamımızı yazdık. Şimdi tek iş bu yordamı ana döngümüz
içerisinden devamlı olarak çağırmakta. Hatırlarsanız daha önce
devamlı olarak görüntüyü tazelememiz gerektiğinden bahsetmiştik.
Bunu gerçekleştirmenin en kolay yolu ana while döngüsü
içinde renderScene() yordamını çağırmak. Aşağıda main() yordamının
bu ufak ekleme ile oluşturulmuş son hali bulunmakta. Programı çalıştırdığınız
zaman artık siyah bir zemine sahip küçük penceremizi izleyebileceksiniz...
int main(int argc,char *argv[]) {
bool quit = false;
initSDL(640,480,16);
setupOpenGL(640,480);
while (!quit) {
// devamli olarak
ekran üzerinde çizim işlemi gerçekleştiriyoruz
renderScene();
SDL_Event event;
while (SDL_PollEvent(&event)) {
switch (event.type)
{
case
SDL_KEYDOWN:
switch (event.key.keysym.sym) {
case SDLK_ESCAPE:
quit = true;
break;
}
break;
case
SDL_QUIT:
quit = true;
}
}
}
return 0;
}
Oyun yapımı ile ilgili bu döküman serisinin ilk bölümü
için sanırım bu kadar yazı yeterli olacaktır. Bu ilk bölümde
SDL kütüphanesinden Windows altında VC++ kullanarak yararlanmak
için gerekli olan ayarlamaları öğrendik. Daha sonra SDL kütüphanesini
ve OpenGL i başlatmayı, ana program döngüsünü, klavyeden
tuşa basılmaları almayı (bu konu daha sonra ayrıntılı olarak işlenecektir)
, ve ekranda pencere ile görüntü oluşturmayı gördük.
Önümüzdeki derslerde çizim işlemine geçecek
ve oyun projemizin ilk sınıflarını tasarlama ve kodlamaya başlayacağız.
Son olarak şunu söylemek istiyorum. Lütfen bu yazı ile ilgili
görüş, eleştiri ve düzeltmelerinizi bize sitemizdeki sistem
aracılığı ile ulaştırın. Böylelikle ilerki yazılarda daha faydalı ve
düzgün içerikler oluşturabilirim. Aynı zamanda anlamadığınız
ya da size karışık gelen noktaları sitemizdeki forumlar aracılığı ile biz
ve sitemizin diğer üyeleri ile paylaşabilirsiniz. Kendinize iyi bakın,
kodlamak ve araştırmaktan uzak kalmayın :) ...
5. Tüm Kaynak Kod:
// main.cpp
// *****************************************
//
// Oyun Yapimi Dersleri [www.oyunyapimi.org]
//
// Deniz Aydinoglu :: 2002 ::
//
// deniz@oyunyapimi.org
//
// *****************************************
#ifdef WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
#include <gl/gl.h>
#include <gl/glu.h>
#include <gl/glut.h>
#include <gl/glut.h>
#include <sdl/sdl.h>
#include <stdio.h>
#include <stdlib.h>
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!\n%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,4,4000);
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);
// 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);
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;
}
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;
}
Deniz Aydınoğlu :: 2002
|
|