Oyun Yapımı - 2 (Temel Görsel Sınıflara Giriş)
Oyun Yapımı - 2
1. Ana Sınıfların Oluşturulması:
Projeyi gerçekleştirirken nesneye yönelik bir yaklaşım izleyeceğimizi
söylemiştik. İsterseniz ilk olarak bir 3 boyutlu uygulamada en temel
nesnelerden birini tasarlayarak işe başlayalım. Oyunumuzun türü
ne olursa olsun mutlaka ekranda hareket eden bazı cisimler çizmek,
bunların konumlarını zaman içinde değiştirmek zorunda kalacağız. 3
boyutlu dünyamız içinde yer alacak cisimlerin ortak özellikleri
ne olacaktır diye düşünecek olursak, ilk akla gelen özellik
şüphesiz konum bilgisidir. Her cismin 3 koordinat ile ifade edilen (x,
y, z) bir konumu olmalıdır. Bazı cisimler sabit olmakla beraber birçok
cisimde hareket etme özelliğine sahip olacaktır. Dolayısı ile bir cismi
hareket ettiren yani konumunu değiştiren yordamlara da ihtiyacımız var. Ve
tabi ki her cismin birde görüntüsü olmalı. Yani cisimler
için bir adet de çizim yordamı tasarlamamız gerekmekte.
Ben yukarıda saydığım işlev ve özellikleri destekleyen bir sınıf
oluşturmayı uygun görüyorum. Bu sınıf kendisinden nesne türetilemeyen,
yani 'abstract' bir sınıf olacak ve ekranda çizilebilen nesneler bu
sınıftan türeme yolu ile oluşturulacaklar. Daha sonra ihtiyaç
duydukça bu sınıfa eklemeler yapabileceğiz. Bu arada, projemizi yaparken
artık standartlaşmış bir kütük yapısını uygulayacağız. Bu yapıya
göre; her sınıf tanımı 'sınıf_ismi.h' isimli kütüklerde, gerçekleştirim
yordamları ise 'sınıf_ismi.cpp' isimli kütüklerde bulunacaklar.
Kimi zaman sınıflar kullanmak yerine 'struct' olarak bazı tanımlamalarda
bulunacağız. Bu tanımlamaları da 'types.h' isimli kütükte toplayacağız.
Aynı zamanda bu kütükte OpenGL ve SDL ile ilgili genel 'include'
lar da bulunacak. Böylelikle artık her ihtiyacımız olduğunda sadece
'types.h' ı include etmemiz yeterli olacak.
Bahsettiğimiz ana 3 boyutlu cisim sınıfımıza 'Entity' adını verelim. Aşağıda
bu sınıfın tanımı, gerçekleştirim yordamları ve types.h isimli kütükte
bulunan 'Vec3d' isimli yapı kaynak kod olarak verilmiştir. Vec3d pozisyon
bilgisi tutmakta kullanacağımız bir yapı. Bu yapıyı kullanarak bir seferde
x,y,z koordinatlarının hepsini birden taşıyabileceğiz. Sizde aşağıdaki kodda
yer alan sınıfları, kendi kütüklerinizi proje içine dahil
edip kodu uygun kütüklere kopyalayarak oluşturunuz.
// --- Types.h ------------------------------
#ifndef __TYPES__H__
#define __TYPES__H__
struct Vec3d {
float x,y,z;
};
#endif
// --- Entity.h ------------------------------
#ifndef __ENTITY_H__
#define __ENTITY_H__
#include "types.h"
class Entity {
public:
Entity() {};
~Entity() {};
void move(const Vec3d *delta);
void setPosition(float x,float y,float z);
Vec3d getPosition() const;
// Bu sınıftan türeyen sınıflar
içerisinde render() yordamı
// içeriği oluşturulmak sureti ile cisimlerin çizim
işlemi
// gerçekleştirilecek. Aşağıdaki tanım aynı zamanda
Entity
// sınıfından direk olarak bir nesne türetilmesini de
engellemiş
// oluyor.
virtual void render() = 0;
protected:
Vec3d position;
};
#endif
// --- Entity.cpp ------------------------------
#include "Entity.h"
// Bu yordam ile cismi delta vektörü doğrultusunda
hareket ettiriyoruz
// Başka bir deyişle delta vektörü ile belirtilen değerler doğrultusunda
// cisim x,y, ve z eksenlerinde öteleniyor.
void Entity :: move(const Vec3d *delta) {
position.x += delta->x;
position.y += delta->y;
position.z += delta->z;
}
// Bu yordam ile cismin konumunu belirliyoruz.
void Entity :: setPosition(float x,float y,float z) {
position.x += x;
position.y += y;
position.z += z;
}
// Bu yordam bize cismin konumunu veriyor.
Vec3d Entity :: getPosition() const {
return position;
}
2. İlk Görsel Cisim:
Ana cisim sınıfımızın iskeletini bu şekilde oluşturmuş olduk. İsterseniz
şimdi bu sınıftan çizim kabiliyetine sahip ve direk olarak kullanabileceğimiz
bir alt cisim sınıfı oluşturalım. Hayal gücümüzü fazla
zorlamamıza gerek yok (en azından bu aşamada..). Örnek olarak bir top
sınıfı oluşturacağız. Top nesneleri birçok oyunda çeşitli görevlerde
karşımıza çıkmakta olan vefakar dostlardır :). Bizim top nesnemizde
her iyi top nesnesi gibi yuvarlak, basit ve renkli olacak. Bu top nesnesi
aynı zamanda temel Entity sınıfından türediği için bu sınıfın
konum özelliğini ve ilgili yordamlarını da içinde kalıtım yolu
ile bulunduracak. Aşağıdaki kodları Ball.h ve Ball.cpp kütüklerinin
içine yazın.
// --- Ball.h -----------------------------
#ifndef __BALL_H__
#define __BALL_H__
#include "entity.h"
class Ball : public Entity {
public:
Ball() {};
~Ball() {};
void setColor(float r,float g,float b);
Vec3d getColor();
virtual void render();
protected:
float r,g,b;
};
#endif
// --- Ball.cpp -----------------------------
#include "ball.h"
// Bu yoram ile topun rengini değiştirebiliyoruz.
void Ball :: setColor(float r,float g,float b) {
this->r = r;
this->g = g;
this->b = b;
}
// Bu yordam bize topun rengini bir Vec3d yapısı
olarak geri döndürüyor.
Vec3d Ball :: getColor() {
Vec3d col = {r,g,b};
return col;
}
// Bu yordam ile topun çizim işlemini gerçekleştiriyoruz.
Önce topun
// rengini aktif OpenGL rengi haline getiriyor, daha sonra ise bir GLUT
// fonksiyonunu kullanarak 1 birim çapında, 20 enine 20 de boyuna
çizgi
// kesiti içeren bir içi dolu küre çizdiriyoruz.
void Ball :: render() {
glPushMAtrix();
glTranslatef(position.x,position.y,position.z);
glColor3f(r,g,b);
glutSolidSphere(1.0f,20.0f,20.0f);
glPopMatrix();
}
Artık elimizdeki Ball sınıfından çeşitli
renklerde toplar elde ederek bunları ekranda görüntüleyebilecek
aşamaya geldik. Aşağıdaki yeni main.cpp kodunu yazarak programı derleyip
çalıştırın. Rengarenk toplarımızı doyasıya seyredebiliriz :).
// --- Main.cpp -----------------------------
#include "types.h"
#include "Ball.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define BALL_COUNT 5
Ball balls[BALL_COUNT];
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);
// Tüm topları tek tek çiziyoruz.
for (int i=0;i<BALL_COUNT;++i)
{
balls[i].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));
for (int i=0;i<BALL_COUNT;++i) {
balls[i].setPosition(0.1f * (rand()%100)
- 5.0f,
0.1f * (rand()%100) - 5.0f,
0.1f * (rand()%100) - 5.0f);
balls[i].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;
}
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;
}
Bir sonraki dersimizde cisimlerin
klavyeden alınan girdiler sonucu hareketlendirilmesi konusunu işleyecek
ve yeni görsel cisim sınıfları oluşturmaya devam edeceğiz. Kendinize
iyi bakın.
Deniz Aydınoğlu :: 2002
|
|