Herkese merhabalar, uzun bir aradan sonra XNA hakkındaki
yazılarıma devam ediyorum. Malum vizelerdi, Türkiye’ye dönüş heyecanıydı
ödevlerdi … bir sürü bahaneden sonra, tekrar bir seri başlatmayı düşünüyorum.
XNA’ de 2 boyut ve XNA’de multiplayer yazı dizilerim oldukça iyi tepkiler aldı.
Desteğiniz için hepinize teşekkür ediyorum.
Bu yazımın konusu 3 boyuta girişten ziyade XNA’de 3 boyutun
mantığını anlatmak olacak, önce mantığını kavrayalım, sonra zaten bizi kimse
tutamaz. Öncelikle bazı konuları maddeler halinde inceleyelim, gerektiği yerde
açıklayalım.
. XNA sağ el koordinat sistemini kullanır. Yani sağ elinizin
parmak uçları X eksenini gösterirken avucunuz Y eksenini ve baş parmağınız da Z
eksenini gösterir.
. XNA’de 2 tip iz düşüm (projeksiyon) vardır. İz düşüm bir
nesnenin görüntüsünün mesafesine göre nasıl ölçeklendiğidir gibi bilgisayar
oyunlarına özel bir tanım yapalım şimdilik. Bu iki tip sırasıyla;
-
Perspektif: Cisim uzaklaştıkça daha ufak
gözükür, görülebilir genişlik artar.
-
Ortogonal: Bu tipte ise uzaklaşsa bile cismin
boyutu değişmez, 2 boyut gibi düşünelim.
. Köşeler: Diğer bir deyişle “açısal oluşum noktaları”
evrende bir nokta düşünün. Bu noktadan çıkan doğrular aralarında açıları
oluşturacaktır. Örneğin hemen bulunduğunuz odanın tavanının köşesine bakın,
sırasıyla biri X, biri Y, biri de Z ekseninde 3 doğru çıkıyor değil mi? Ve aralarında
90 derecelik dik açı var. Ve her şekil köşelerin birleştirilmsiyle oluşur, çocukken
bulmacalarda noktaları birleştirdiğimizi hatırlayalım. Ve eğer bu noktaların
her birinden 3 boyuta doğru çizgilir çıksaydı ne olurdu? Yanıt basit 3 boyutlu
modeller. Az önceki örnekten devam edelim odadaki her köşeyi birbirine bağlayın
ne oldu? Bir oda yani dikdörgenler prizması, değil mi.
Eh artık 3 boyutun unsurlarını biliyoruz peki bunları nasıl
kullanacağız da bir oyun ortaya çıkacak?
XNA’de köşeler:
XNA’de köşeler, yerlerini ve diğer özelliklerini renk vb.
içeren yapılar (struct) yardımı ile tanımlanırlar.
VertexPositionColor
-> bu yapı ile bir köşe (vertex); yeri (position) ve rengi (color) ile
tanımlanır.
VertexPositionTexture
-> bu yapı ile bir köşe; yeri ve dokusu (texture) ile tanımlanır, desenin
kendi içinde (0,0) desenin sol üst; (1,1) desenin sağ alt köşe limitidir.
VertexPositionColorTexture
-> yukarıdaki iki yapının bileşimi gibi düşünelim.
VertexPositionNormalTexture
-> bir normal vektörü ve bunun yanında yer ve desen tanımlanır.
XNA’de köşeleri birleştirmek için; GraphicsDevice.DrawPrimitives
metodu kullanılır, bu metodun ilkel tipler (Primitive Types) için tanımlanmış
enumaration değerlerine göre köşeler bir düzen içinde birleştirilir. Buradaki
ilkel tipler programlama dillerindeki int, float vb. ilkel tiplerle
karıştırılmamalıdır. Bahsettiğimiz Enum değerleri, 1. Parametre olarak
kullanılacaktır; hepsi PrimitiveType. xxxx şeklinde kullanılabilirler. Bu metodu
kullanmadan önce Vertex buffer dediğimiz bir köşe listesini oluşturmamız
gerekir. Bunu da Enum değerlerini gösterdikten sonra anlatacağım.
.PointList -> Köşe listesinde (Vertex Buffer) bulunan her
vertex birbirinden bağımsız birer nokta şeklinde çizilir.
.LineList -> Köşe listesindeki köşeler sırayla ikişerli
olarak gruplandırılır ve her ikilinin arası bir çizgi ile doğrusal olarak birleştirilir.
.LineStrip -> Köşe listesindeki köşeler sıra ile art arda
birleştirilir.
.TriangleList -> Köşe listesindeki köşeler üçer üçer
gruplandırılır, her üçlü bağımsız bir üçgen oluşturur.
.TriangleStrip -> Her yeni üçgen kendinden önceki üçgenin
son iki noktası kullanılarak oluşturulur, dolayısı ile üçgenler bağlıdır, ilk
üçgen dışındaki üçgenler için tek nokta gerek ve yeterlidir.
.TriangleFan -> İlk köşemiz tüm üçgenlerin tepe noktası
olur ve üçgenler birfan şeklinde oluşur. Buna örnek olarak ikiye kesilmiş
portakalı verebilirim, orta noktasını ilk noktamız kabul edersek, her dilim bir
üçgen oluşturacaktır, tepe noktaları ortak olacaktır.
GraphicsDevice ekranKartı;
Şeklinde tanımlanmış bir aygıtımız olduğunu varsayarak; Bir Vertex buffer ‘ı(Köşe Listemizi) nasıl
oluşturacağız; buna bakalım;
VertexBuffer köşeListesi;
// Diyerek bir
VertexBuffer tanımlamış olalım.
VertexPositionColor[] köşeler= new
VertexPorsitionColor[10];
//10 köşe barındıran bir dizi
oluşturduk;
köşeler[0] = new VertexPositionColor(new
Vector3(0.0f, 0.0f, 0.0f),Color.White);
// gibi bir kodla
tüm köşelerimizi atayacağınızı varsayıyorum;
köşelListesi = new
VertexBuffer (ekranKartı, 10* VertexPositionColor.SizeInBytes,
BufferUsage.WriteOnly);
// diyerek Vertex
Bufferımızı oluşturduk. Buradaki 10 listemizdeki eleman sayısı.
Yukarıda fark etmişsinizdir; Köşemizin yerini
belirlerken Vector3 diye bir yapı kullandık; peki bu yapı nedir?
Adından da anlaşılacağı üzere Vector3 yapısı 3
boyutlu bir vektör tutar, yani X, Y ve Z koordinatlarından oluşan bir vektörü
tutar. Vektör denince kafası karışanlar için, buna noktanın tanımlandığı hacim
(dünya, oda … vb 3 boyutlu bir mekan) içerisindeki yerini belirtir diyelim.
Yani odanın duvarında duran X ve Y koodinatlarına sahip bir sinek değil de
odada uçan, oda hacmi içinde herhangi bir yerde olan sineğin konumu
.
Madem Vector3 yapısına (struct) giriş yaptık
kısaca metotlarının bazılarından bahsedivereyim;
Vector3.Distance() -> verilen iki nokta
arasındaki uzaklığı hesaplar.
Vector3.Add()
Vector3.Subtract() -> bu metotlar, vektörel
toplama ve çıkartma yapar. Yani X değerleri X Değerlerinden, Y değerleri Y değerlerinden …
çıkartılır.
Vector3.Multiply()
Vector3.Divide() -> Bu iki metor iki vektör arası çarpma bölme işlemi veya
verilen bir sayı ile bir vektörü çarpıp bölme işini yapar.
Vector3.Clamp() -> Vektörü verilen
sınırların içinde tutar, sınırın dışarısında bir değer almasını engeller; yani
yukarıdaki örnekteki sinek bu metot yardımı ile odanın dışına çıkamaz.
Vector3.Lerp() -> İki vektör arası doğrusal
interpolasyonu hesaplar.
Vector3.SmoothStep() -> Verilen bir değeri
ağılık kabul ederek, iki vektör arası interpolasyonu hesaplar.
Bunlardan ayrı olarak yine Vector3 yapısına
dahil özelliklerden bazıları da;
Vector3.Zero -> Geriye tüm değerleri 0
olarak atanmış bir vektör döndürür. (0,0,0)
Vector3.Up -> Yukarıya bakan bir vektör döndürür
(0,1,0)
Vector3.Right -> Sağa bakan bir vektör
döndürür (1,0,0)
Vector3.Forward -> Sağ el koordinat
sistemine göre ileri giden bir vektör döndürür (0,0,-1)
Vector3.Left , Vector3.Down, Vector3.Backward
Vector3.UnitX -> bir birim X vektörü
döndürür (1,0,0)
Vector3.UnitY, Vector3.UnitZ
Peki bir nesne (model) köşelerin
birleştirilmesinden oluşuyorsa ve her köşe bir vektör olarak temsil ediliyorsa,
Oyunun geçeceği dünya nasıl temsil edilmeli?
Yanıt matrisler, eğer içinde bulunduğumuz oda
bir 3 boyutlu matrisle temsil ediliyor olsaydı, kağıt üzerinde sineğimizin
nerede uçuyor olduğunu işaretleyebilecektik.
XNA’de matrisler, bir nesnenin rotasyonunda (bir
eksen etrafında dönme hareketleri), ölçeklendirilmesinde (uzağa giden nesne
küçülür) ve translasyonunda (sabit yönlü hareket) kullanılır.
Eğer matris kullanmıyor olsaydık, bir nesnenin
tüm hareketleri için vektörün her bir değerine tek tek ulaşıp o değeri
değiştirecektik. Ama bu işi iki matrisin arasındaki matematiksel işlemler
aracılığı ile yaparsak hem X, Y ve Z değerlerine ayrı ayrı ulaşmak zorunda
kalmayacağız hem de her bir işlemi tek tek uygulamak yerine aynı anda hem
haraket ettirebilecek hem de nesnenin boyutunu değiştirebileceğiz.
Matrislerin kullanılmasının bir diğer sebebi
ise çağdaş ekran kartlarının tümünde GPU üzerinde matris algoritmaları olması,
böylece CPU üzerine binecek yükten de kurtulmuş oluyoruz.
Bir nesneye Matris işlemleri ile haraket nasıl
verilir?
Bir noktamız var koordinatları (20,10,30)
olsun. Bunu 30 birim yukarı hareket ettirmek istiyoruz. Bunu nasıl yapacağız?
Sonunda Lineer Cebir dersleri bir işe yarıyor
ve matrislerde çarpma yapıyoruz.
[1, 0, 0, 0]
[20,10,30,1] x|0, 1, 0, 0| = [20, 40, 30, 1]
|0, 0, 1, 0|
[ 0,30,0, 1]
Gördüğünüz gibi y değeri 30 arttı.
Eğer bu işlemde, ilk matrisimizde en sondaki 1’i
arttırsaydık 60 birim ilerlemiş olacaktık, yani bu ilerleme katsayısıdır.
Eğer, İkinci matristeki köşegende bulunan 1’leri arttırsaydık, nesnemizin boyu büyüyecekti,
yani bu köşegen de nesnemizin ölçeklendirilmesinden sorumlu.
Ve aynı zamanda eğer ikinci matrisimizde sin ve
cos değerleri kullanıyor olsaydık, nesnemiz dönme hareketi yapacaktı.
Gördüğünüz gibi neredeyse hiç uğraşmadan tek matris işleminde nesnemizi hareket
ettirdik.
Yazımı bitirmeden önce son bir şey söylemek
istiyorum, matrisler sizi korkutmasın çünkü bu işlemleri XNA sizin için Matrix
sınıfı aracılığı ile yapıyor, buradaki amacım size işin işleyiş mantığını
göstermekti. Bir sonraki yazımda size Matris sınıfından bazı işlevlerinden
bahsedeceğim, Eğer zaman bulursam Efektlere de tanıtma niyetinde bir giriş
yapmak istiyorum. Hepinize kolay gelsin.