"bir yazılımcının not defteri.."

25 Mayıs 2011 Çarşamba

Unreal Development Kit (UDK) - UnrealScript ve GameFramework alt yapısı




Herkese tekrar merhaba,

Bu yazımızda UnrealScript in sınıf yapısını ve genel yapıyı biraz daha detaylı inceleyeceğiz. Önceki yazılarda bir çok sınıfı (şablon düzeyinde de olsa) yarattık. Yani az çok biliyoruz. Ve ben de sizin gibi bir an evvel daha ileri uygulamalara geçmek istiyorum. Ama farkettim ki olayın alt yapısını iyice anlamazsam ileri örnekleri yeterince iyi anlayamıyorum. UnrealScript in bütün sınıflarının %80 ini bildiğini idda eden bir arkadaşım google dan yardım almadan (hazırına bakmadan) tek bir satır dahi kod yazamıyor; sadece hazır koda baktığında “bu bununla ilgili”, “şu da şununla ilgili” diyebiliyor. Yorumu size kalmış. Ama şunu rahatça söyleyebilirim : bu teknolojinin geliştiricisi olacak kişi olayı yüzeysel değil, derinlemesine öğrenmelidir. Bunun için de öncelikle alt yapıyı iyi anlamak gerektiğini düşünüyorum. O yüzden bu ve takip eden bir kaç yazımızda doğrudan uygulama örnekleri yazmak yerine UnrealScript in yapısını hakkında daha derinlemesine sohbet edeceğiz. Belki biraz sıkıcı olacak. Ama işimiz bittiğinde ayağımız yere daha sağlam basar hale gelecek :)


UDK ile ilgili kaynakların ve görsel videoların büyük bir çoğunluğu görsel tasarım editörü ile ilgilidir. Zaten UnrealEd (tasarım editörü) o kadar gelişmiştir ki hiç kod yazmadan dahi oyun üretilebilir demiştik. Dolayısı ile editöre yoğunlaşan bu geniş kaynakların arasında UnrealScript, yani işin programarik kısmı biraz geri planda kalmış görünüyor. Ama bilinmesi gereken birşey var ki : Editör belli bir noktaya kadar programlamanın yerini tutabilir. Daha fazla esneklik, daha fazla fonksyonellik, kısaca belirlenen sınırların dışına çıkma ihtiyacınız artıkça UnrealScript e duyulan ihtiyaç da artacaktır.

UDK nın tüm alt yapısı, Epic Games in yıllardır C++ ile geliştirdiği core (karmaşık ve yoğun) kodlardan oluşur. Epic in bu alt yapısına native, yani doğal kod demektedir. Doğal çünkü asıl işi yapan katman bu katmandır. UnrealScript ise bu katmanın üzerinde doğrudan olmayan (!) şekilde çalışır.

C++ kodlanması / uygulanması kolay olmayan ancak çok güçlü bir dildir. Bu dilin seçildiği durumlar performans ihtiyacının kod karmaşası ve geliştirme zorluğuna ağır bastığı durumlardır ki,, oyunlar tam da bu durumlara örnektir.

Eğer biz bu C++ kodları ile oyun geliştirme yapmak zorunda kalsa idik, öğrenim ve geliştirme çok zorlaşacak, içimizdeki en profesyönel C++ programcısı için bile katlanılması güç bir durum olacaktı. İşte Epic, tüm bu karmaşayı ortadan kaldırmak için yoğun C++ alt yapısının üzerine daha üst seviyeli bir programlama arabirimi yaratmıştır. Alt yapıda hala yoğun C++ kodları sessiz ve derinden çalışsa da, bize sunulan kodlama katmanı çok daha basit, sade ve üst seviyelidir. Bu arabirimin adı : UnrealScript tir. Bir zamanlar Microsoft XNA için DirectX in toparlanmış hali diyorlardı. Aslında XNA, directX tabanı üzerinde çalışan ama daha sade kodlama arabirimi olan zenginleştirilmiş bir framework den başka birşey değildir. Yani altta hala directX vardır. İşte UnrealScript de Core Unreal C++ kodlarının üzerinde çalışan üst seviyeli ve çok daha basitleşmiş bir arabirimin adıdır. Bunun anlamı odur ki : Yazım kolaylıği ve alt yapıdaki C++ ın tartışılmaz performansı bir aradadır.

Malum bilgisayar oyunları dünyanın en zor ve de karmaşık programlarıdır. Bu tanım bazılarının hoşuna gitmeyebilir ama hiçbir şektörün yazılımları bilgisayar oyunlarının kodlarından daha karmaşık değildir. Bir oyunun sadece görselliği ile ilgili kodlamaları (2D veya 3D) apayrı bir dünya iken, network işlemleri, yapay zekası, oyun esnekliğini sağlayan diğer işlevler her biri başlı başına ciddi problemler ve ayrı ayrı uzmanlık alanlarıdır. Bu yüzden eskiden oyun üretme işine ancak büyük bir ekip kurarak ciddi yatırım yapabilecek şirketler girebilirdi. Ancak günümüzde bu durum değişmiştir. Çünkü UnrealScript gibi geliştirme ortamları tüm bu zor işlemlerin çözümlerini ve hazır kodlarını alt yapısında barındırmaktadır(!). Güçlü OOP desteği ve kullanıma hazır üst düzey kütüphaneleri ile ister tek developer, ister bir yazılım ekibi ile birlikte çalışalım, gereksiz detaylarla hiç uğraşmadan sadece oyunumuza konsantre olarak profesyönel sonuçları hızlıca ortaya çıkarabiliriz. Çünkü UDK nın malum core alt yapısı bütün can sıkıcı işleri halletmektedir :)

UnrealScrtipt in yazım şekli (syntax ı) tasarlanırken günümüzün en modern OOP dillerinin (C++ ve Java) güçlü yönleri alınmıştır. Karmaşıklığa yol açtığı düşünülen unsurlar ise alınmamıştır. Ve sonuç olarak adına script dense de gayet ciddi bir yazılım geliştirme arabirimi ortaya çıktığını söyleyebiliriz.

Java dan devralınan bazı özellikler :

  • Pointer yapısı yerine daha kolay olan Reference tipi
  • Garbace Collection (otomatik çöp toplama)
  • basit - tekil class devralma yapısı (kalıtım)
  • Soyut veri tipleri için güçlü tip kontrolü (abstraction)
  • İstemci taraflı güvenlik önlemleri




UnrealScript geliştirilirken güçlü OOP dillerinden esinlenildiğini söylemiştik. Konu irdelenirken Java nın C++ ın üzerine pek de yararlı bir şey eklemediği, bir çok vadesi dolmuş sinir bozucu özellik eklendiği ve java nın genel performansının düşük olduğu sonucuna varılmıştır. (Tabi şu anda platform olarak değil yanlızca yazım şekli sytax olarak olayı konuşuyoruz.) Visaual Basic ise genel olarak sonuc başarılı olsa bile yazım olarak profesyönellikten uzak :), hele ki OOP dünyasının babası C++ programcıları için küfür anlamına gelen bir syntax ı (yazım şekli) vardı. Nihai kadar olarak hem hız, hem basitlik hemde modern kodlama sistematiklerine yakın olması için özellikle C++ ve de Java dillerinden bir sentezleme yapılarak UnrealScript in temel syntax yapısı oluşturulmuştur.

Şimdi biraz eski bilgileri hatırlatacak ve akabinde yeni terimler vereceğim :
UnrealScript de sınıf ve OOP un kalıtım (miras alma) yapısı yoğun bir şekilde kullanılır; herşey ama herşey sınıflardan, ve bu sınıfların kalıtım ile devralınması ile üretilen yeni sınıflardan ibarettir. Öyleki UnrealScript de sınıf tanımlamalarının dışında hiçbirşey yoktur! Sadece bu bile OOP ne derece güçlü desteklendiğinin göstergesidir. Yani sizi spagetti kod (kod karmaşası) üretmek yerine OOP kullanmaya mecbur bırakır.

UDK da bir oyun çeşitli modüllerden oluşur. Bu modüllerin her birine UDK literatüründe package (paket) denir. Yaratılan / tanımlanan her class derlenme öncesi bir pakettir. Her paket de içindeki sınıf adı ile aynı adı taşıyan bir .uc uzantılı bir kaynak dosyasına karşılık gelir.

UnrealScript kodlamasında tüm fonksyonlar ve değişkenler (variables) class ların içinde olur. OOP çok iyi desteklendiği için class tanımlarının dışında fonksyon veya değişken olamaz. Tüm fonksyon ve değişkenlere o sınıfa ait bir obje üzerinden erişilebilir. Sistem çapında global değişkenler ve fonksyonlar yoktur. (Olsa idi zaten emin olun beladan başka birşey getirmezdi :)

UnrealScript çok çeşitli değişken tiplerini destekler. Bunlar daha çok C++ ve Java da kullanılan temel tipler, referans tiplerı, yapılar (struct), diziler (Array) vs.. Yani modern programlamadaki tip yapısının çoğu destektelnmiştir. Ve binlerce de soyut veri tipi (class) mevcuttur.

Bazen tasarımcıların unreal tasarım editörü içinden değişkenlere (kod tarafında tanımlanan objelere) erişmesini isteyebiliriz. Herkesin programcı olmasını bekleyemeyiz. Oyunlardan bahsediyorsak tasarımcıları ve unreal tasarım editörünü kullanan sanatçıların bizim kodlarımızdaki bazı tanımlamalara editörden erişmesini mümkün kılmalıyız.Bunun için örneğin değişken tanımlarken “var” yeirne “var()” syntax ını kullanırız. Sınıf düzeyinde editörde görünürlük içinde ilgili sınıf niteleyicilerini kullanırız. Sınıf niteleyicileri UnrealScript e özgü güçlü bir kullanımdır. Makalenin sonunda tam listesini vericem.

UnrealScript de fonksyonlar (functions) bir veya daha çok parametre alabilir ve isternirse değer döndürebilir. Yahutta bunları yapmayıp sadece yalın bir kod bloğu olarak da çalışabilir. Fonksyonlar dahil olduğu class içindeki lokal değişkenlere kolaylıkla erişebilir. Bazı fonksyonlar UDK özel bir isme sahip olabilir ve onlar değişik durumlarda unreal engine in kendisi tarafından çağrılırlar, örneğin BeginPlay fonksyonu gibi. Bazıları ise diğer script olaylarında çağrılabilir örneğin triggerlar (tetikleyiciler) de veya state lerde bu konuyu ilerleyen makaleler de daha detaylı ele alacağız.

Kodlamanın genelinde standart C / C++ / Java syntax ının çoğu desteklenmektedir : for, while, break, switch, if vs... komut sonlarına da aynı şekilde noktalıvirgül (semicolon) eklenir.

UnrealScript de referans veri tipi özellikle de objeler içlerindeki fonksyonlara diğer objelerden erişirken kullanılmaktadır. Bu olay moren OOP dillerindeki pointer (işaretçi) / referans veri tipi kavramı ile aynıdır. Birazdan örnek vereceğim.


UnrealScript e “state” yani durum keyword ü eklenmiştir. Oldukça etkili bir özellik olan bu deyim, söz konusu Actor objelerine belirlenen durumlarda işlemek üzere belirlenen kod blokları tanımlayabilirsiniz. Bu konu da ilerleyen makalelerde detaylı incelenecek.

Son olarak UnrealScript deki tüm isimeldirmeler case-sensitive dir yani büyük küçük harf duyarlıdır. diğer tüm modern OOP dillerinde olduğu gibi..

Bunlar küçük ama önemli detaylar. Fazla teorik gibi görünsede hayat kurtarabilecek teoriler.



UDK alt yapısı (GameFramework) :
Piyasa da pek çok oyun firması kendi oyun alt yapılarını (GameFramework) üretmiştir, Unity, Microsoft XNA, CryENGINE, Half Life Engine, ID Software in Quake motoru vs.. vs.. vs.. ve bunların bazıları o firmanın ürettiği bütün oyunlara ortak alt yapı olacak şekilde işletim sistemlerine kurulur. Unity Web Player, bu işin en başarılı örneklerinden biridir. Unity i günahım kadar sevmem :) ama Unity ile üretilen tüm oyunlara framework teşkil edecek standart alt yapı zekice tasarlanmıştır. Siz sisteminize bir kez kurarsınız ve artık az yer kaplayan unity oyunları web den sisteminizdeki kurulu yapıyı kullanarak çalışır. Burada oyun browser pencersinde görünse bile aslında doğrudan işletim sistemi tarafından yürütülür. Neyse konuyu çok dağıtmadan söylemek istediğim o ki : her oyunun altında motora ait bir framework çalışır. Ama sisteme yerleşik yapıda olsun, ama her oyun dağıtımında ayrı ayrı bulunsun, o hep vardır. Bütün profesyönel oyun geliştirme ortamlarında bu alt framework oyunun kendisinde mümkün mertebede ayrılmış olarak bulunmalıdır. Hazır prof. ortamlardan bahsetmişken Unreal Development Kit de de elbette bu böyledir :) Yani herşey bir alt yoğun (core) bir framework ün üzerinde çalışır. Şimdi dilerseniz UDK nın alt yapısını oluşturan bileşenler hakkında biraz sohbet edelim, daha sonra daha detaylara gireceğiz :

UDK da bir oyunu çıktı aldığınızda her defasında tüm bileşenler her bir oyun için çıktıda yer alır. Yani framework oyunun ile birlikte ve ona dahil olarak verilir. Bu çıktı da 4 temel kategori bileşen (component) bulunmaktadır :


  • Server Components
  • Client Components
  • Renderin Engine (render motoru)
  • ve motoru destekleyici diğer modüller


bunlarım hepsine birden Unreal Virtual Machine yani unreal sanal makinesi denmektedir. (Bu java dan kalma "sanal makine" lafına da ayar olurum :)) Gerçi biz sadece tüm oyunların alt yapısını oluşturan bileşenler olduğunu bilelim yeter.

şimdi bu componentler hakkında kısaca konuşalım :

1 - Server controlleri oyuncuların birbirleri arasında ve oyuncunun oyundaki tüm actor ler arasındaki etkileşimini kontrol eder, merkezden yönetir. (Oyunlardaki tüm programlanmış, yarı zeki, oyuncu ile etkileşime girebilen herşey Actor dür.) Tek kişilik oyunlarda (single-player game) ise oyuncunun makinesi hem hem client hem de server durumundadır. Uzaktaki bir sunucuya gerek yoktur. Bu durumda server bileşenleri de oyuncunun makinesinde çalışır. Oysa bir internet oyununda tüm oyunu organize etmek üzere atanmış bir uzak sunucu gerekir ve tüm oyuncular (client olarak) bu sunucuya bağlanmak zorundadırlar. Online oyunların çok moda olduğu ve dahası tek kişilik oyunların artık anlamını yitirmeye başladığı günümüzde bu uzak sunuculu modelinin ve UDK server bileşenlerinin önemi daha iyi anlaşılır.

2 - İstemci (client) kontrolleri olarak Levelleri düşünebiliriz. Bir oyun level lerden oluşur ve tüm oyun akışı da bu levellerin içindedir. E bunu zaten biliyoruz diyeceksiniz :) UDK için her Level kendi kendine yeten bir ortamdır. UDK eşzamanlı olarak bir den fazla Level i çalıştırma yeteneğine sahip olsa da, her level birbirinden bağımsız ve ayrı olarak çalışır. Mesela actorler bir level den ötekine geçemez. Ve bir level içindeki bir actor başka bir level içindeki actor ile iletişim kuramaz. Yani bir Level deki bilgiyi anlık olarak başka bir Level e aktaramıyoruz.

3 - Bir de oyundaki tüm actor objelerin etrafında onları işleyen scriptler ve olaylar (events) vardır. UnrealScript in akışını anlamanın temel yolu zaman yönetiminden geçer. Unreal her oyundaki her bir saniyenin içinde adına “tick” dediği zaman olayları yaratır. Bir “tick” oyundaki en küçük zaman birimidir. her bir tick de level içindeki tüm actor objelerinin durumları update edilir. Eminim içinizde Microsoft XNA ile boğuşmuş... e....şey yani uğraşmış olanlarınız vardır :)) Hatırlayacaksınız oradaki olaylar da daima bir update döngüsünde sürer giderdi. Her bir update de oyun objeleri güncellenirdi. Elbette bir bu zaman yönetiminin çok daha gelişmiş hali UDK da mevcuttur. UDK da bir tick genellikle bir saniyenin onda biri ile yüzde biri arasında değişebilen bir zaman aralığıdır. Tick zamanı aynı zamanda actrorlerin güncellenme sıklığı anlamına geldiğinden bilgisayarın işlemci hızı ile sınırlıdır. Yani ne kadar kısa tick o kadar çok CPU harcanıyor diyebiliriz. Zaman yönetimi bir oyun için olmazsa olmaz kavramlardan biridir. Bu da konuyu ilerleyen yazılarda ele alacağım.

Bu arada hazır konu açılmışken şunu da ekliyim ki, UDK içindeki bazı fonksyonların koşturulması sıfır tick te gerçekçeşir. Yani çalışmaları, işlevlerini yapmaları anlıktır. Öyle kabul edilir. Diğerleri ise belli zaman parametrelerine bağlıdır. Yani çağrılmaları ve işleve başlamaları için belirlenen bir miktar tick (süre) geçmesi gerekir. UnrealScript de bunlara "Latent Functions", yani gizli /örtülü fonksyonlar denir. Örneğin : Sleep, FinishAnim, MoveTo vs.. UnrealScript de Latent fonksyonlar sadece “state” bloğu içinden çağrılabilirler. O yüzden bu fonksyonlara bir anlamda state kodu, ya da state fonksyonları da denilmektedir. Bir actor bir latent fonksyonu çağırdığında state bloğu nun işleyişi çağrılan latent fonksyon tamamlanana kadar durur. Ve bu kritik bir bilgidir. Ama öteki aktörler aktörün içindeki fonksyonları çağırabilir. Sonuç olarak diyebiliriz ki tüm UnrealScript fonksyonları her hangi bir zamanda çağrılabilir; (latent fonksyonlar askıda / beklemede olsa bile.) Ama State kod bloğu içindeki latent fonksyonun işi bitmeden bir sonraki kod satırına atlayamaz. Bu konunu da detaylarını ileriki yazılarda işlemek üzere burada kesiyorum.


UDK Thread Sistemi :
Bir level içindeki her obje nin yaşam döngüsü ayrı bir thread içinde yürütülür. Yani her actor ün kendi thread i vardır. Multithread programming bilenler bunu daha kolay kavrayacaklardır: Multithread, çok kaba bir özetle bir programın birden fazla program parçası olarak sistemde çalışmasıdır. Örneğin  programımızın çok zaman alan fonksyonlarını ayrı bir thread olarak programlarız; böylece o fonksyon işini görürken ana program durmaz ve çalışmasını sürdürür. Program parçalarını Thread lere bölmek programın genel performansını artırmaktadır. UDK da hem performans artışı hem de oyunun akışının hiçbir zaman duraksamaması için her Actor objesinin çalışması ayrı bir thread olarak yürütülür. Ancak UDK windows thread lerini kullanmaz!! Epic in resmi sitesindeki dökümantasyonda bunun sebebi olarak windows servislerinin kullanılması durumunda verimin azalacağı söylenmiştir :) Güldüğüme bakmayın, haksız da sayılmazlar. Gerçekten biliyorum ki windows lar kullanımı ne kadar rahat sistemler olursa olsun, arka tarafta 150 task (görev) den sonra kafası karışmaya başlıyor; 1.000 ve daha ileri rakamlarda ise sistem sizlere ömür... Eski windows versiyonlarında durum daha da kötü idi. Bu yüzden microsoft hiç kusura bakmasın, yoğun multi thread gerektiren sistemlerin hemen hiçbirinde windows tercih edilmez; bu tür işler için UNIX tabanlı işletim sistemleri (unix, linux, solaris, ve IBM in tüm işletim sistemleri) çok daha sağlam bir yapıdır. Genelde sorulan sorudur bir hava alanı kontrol sisteminin sunucusu neden hiçbir zaman windows değildir? işte bu yüzden. 1.000 in üzerinde task / thread gerektirecek hiçbir sistem için windows tercih edilmemektedir. Şu an bu blogu bir windows 7 sisteminden yazıyorum; yanlış anlaşılmasın, windows düşmanı değilim :) hatta bir son kullanıcı olarak windows u seviyorum; ama ortada bugüne kadar süregelen bazı gerçekleri de yeri geldiğinde söylemek durumundayım.

Gelelim UDK oyunlarımıza; oyunumuzdaki her actor ün bir thread yaratacağını düşünürsek bir level içinde de 1.000 den fazla actor varsa (ki neden olmasın?) bir windows sistemi ne hale gelecektir?? Epic games bu durumu hesaba katarak windows thread sistemini kullanmadan kendi thread sistemini simule ederek çözmüştür. Ve tüm UnrealScript leri birbirinden bağımsız objeler şeklinde çalışır(!). Örneğin level içinde 1000 tane askerin yürüdüğünü hayal edelim? Tüm bu objelerin scriptleri de her bir “Tick” de aynı anda ve birbirinden bağımsız bir şekilde işlemektedir, ve bu hiçbir sistemde (windowsta bile :) sorun yaratmayacaktır.


UnrealScript Object Hiyerarşisi:
Unreal ile kodlamaya başlamadan önce yüksek seviyeli (abstracted) )objelerin ilişkilerini ve hiyerarşisini anlamak önemlidir. Geçmişteki çoğu oyun bütünsel (yeterince modüler olmayan) bir şekilde kodlanarak oluşturulmuştur. Bu onların sonradan genişleyebilir olmalarına engel teşkil etmiştir. Unreal in başlıca faydası ise OOP tasarımında ve birbirinden yalıtılmış modül yapısındadır. Öyleki çalışma anında Unreal e yeni Obje tipleri ve fonksyonellikler eklenebilir!! Runtime da eklenen sınıf ve alt sınıflar yerine mevcut olan bazı kodları değiştirmekte işimize yarayabilir ve bu da mümkündür. Bu esneklik UnrealScript e çok ciddi boyutta güç katmaktadır. Ve bu hayati bir bilgidir..

UDK sınıf hiyerarşisinde “Object”, en üst düzey tiptir ve bütün sınıfların atasıdır. Yani kalıtımdaki en üst düzey parent (ebeveyn), ya da nam -ı diğer base (temel) sınıftır. Object sınıfı içindeki tüm fonksyonlar kodun herhangi bir yerinden erişilebilir durumdadır. Neden?? Çünkü herşey bu sınıftan türetilmiştir :) Hangi sınıfta olursanız olun en temel seviyesinde mutlaka object den miras alınan üyeler vardır ve onlar artık sizin sınıfınızın bir parçasıdır. Şunu da ilave edelim ki : Object tipi bir abstract olarak tanımlanmış olduğundan için tek başına bir anlam ifade etmez.Tüm işlevlerini onu devralan alt sınıflarda gösterir.

Actor sınıfı da Object sınıfından türemiştir ve Actor oyundaki tüm objelerin parent class ıdır. Ya da şöyle diyelim : Kod tarafında oyundaki tüm objeler Actor sınıfı ile temsil edilir ve Actor ler bu objeleri programlamak için gerekli tüm işlevlere sahiptirler. Bir Actor ün hareket etmesi, başka bir actor ile etkileşime girmesi, çevreyi herhangi bir şekilde etkilemesi, vs..vs.. Şimdilik genel tanımlamalarda ve teorik bilgilerde ilerliyoruz. Ama ileride kodlara daha fazla girdiğimizde neyin ne olduğunu daha iyi biliyor olacağız.

Bir de şu ünlü Pawn sınıfımız var tabiki. Önemli ve en çok kullanılan, adı en çok geçen sınıflardan biri. Pawn sınıfı da Actor sınıfından devralınarak türetilmiştir. Oyunda görünen tüm oyuncular, tüm yaratıklar vs. Pawn sınıfının ürünüdür. Temelinde Actor olan bu sınıf Actor ün daha yüksek düzeyde özelleştirilmiş halidir, yani Actor den daha gelişkin kontrol mekanizması, daha gelişkin yapay zeka vs. sunar. Oyuna yeni bir objesinin katılmasını (spawn oluşunu) kod tarafında actor sınıfından devralınan yeni bir sınıf şeklinde kodlayabiliriz. UnrealScript de herhangi bir Object sınıfı için de kod yazabiliriz, ama pratikte yazdığımız kodların büyük bir çoğunluğunu Actor sınıfından devralınan sınıflar için yazacağız. Yani genellikle daha yüksek seviyeli sınıflar ile uğraşacağız. UnrealScript in en kullanışlı yönü actor ler arasındaki paylaşım - etkileşim - alışveriş tir.




Şimdi sınıflar hakkında biraz daha derinlemesine inersek, şöyle bir örnek üzerinden devam edelim :



class MyClass extends MyParentClass


//sınıf niteleyiciler
placeable;
abstract;
config();




// sınıfa ait değişken tanımlamaları
var() degisken1; //editörde görünecek
var bool degisken2;
var actor Trigger; //aktör objesi, statelerden durumlarına kod bloğu bağlanabilir




//ilk atamaların yapıldığı blog
defaultproperties
{
      degisken2 = true;
}


//Motora ait fonksyonlar , Oyun başlangıcında çağrılırlar
function BeginPlay()
{
     .......
}


function Tick( float DeltaTime )
{
    .......
}
//vs..


// Durum states kontrolleri
state() TriggerTurnsOn
{
….......
}
state() TriggerTurnsOff
{
….......
}
state() TriggerToggle
{
….......
}


//vs... her actor ün pek çok durumu için kod bloğu burada tanımlanabilir



Yukardıda bir sınıfın sade bir şablonunu verdim. Bir sınıf tanımlama satırı ile başlıyor, hemen ardından sınıf niteleyici ifadelerin olduğu kısım ile devam ediyor; ardından değişken (variables) tanımlamaları yer alıyor
sonra defaultproperties kod bloğu yer alıyor, değişkenlere ilk atamalar ve sınıfın ilk kullanım hazırlıkları yapılıyor, varsa oyun motorunun otomatik olarak çağırdığı özel fonksyonlar tanımlanıyor. bu fonksyonlarda söz konusu durumlarda ne yapılacağı belirtiliyor. ki budumlar oyun düzeyindeki olaylardır. Detayları başka yazılarda inceleyeceğim, elbette sizin tanımladığınız normal fonksyonlar olacaktır.

Çok önemli başka bir alan ise, “state()” durumlarıdır. State ler actor lerin çeşitli durumları için kod bloklaro tanımlamamıza olanak sağlar. Nasılki oyun düzeyinde olaylarda belirli fonksyonlar çağrılır, aynı şekilde actor düzeyinde olaylar için de sate ler kullanılır. Her actor ün ayrı ayrı sate durumları tanımlanabilir. State ler UDK ya oldukça güç katan bir kullanımdır.



Şimdi biraz eski makalelerdeki bazı bilgilerimizi anımsayalım: Her script dosyası tek bir class tanımına karşılık gelir. Ve her sınıf tanımı .uc uzantılı bir kod dosyasına karşılık gelir demiştik. Dolayısı ile her .uc bir sınıf tanımı (declerasyonu) ile başlar, ve her birininin içinde tek bir sınıf tanımı mevcut idi. Bu tanım genellikle başka bir sınıftan miras alma şeklinde dir ve şöyle dir :

class MyClass extends MyParentClass

burada MyClass adında bir sınıf tanımlandı. En sondaki kelime ise parent sınıfın adıdır. Bu adı daha belirgin hale gertirmek için parent class adının önüne paket adını yazabilriiz, yani şöyle olabilir :

class MyClass extends MyPacket.MyParentClass


her türetilen class parent sınıfın tüm değişkenlerini, fonksyonlarını ve state (durum tanımlarını) devralır. Türetilen deklarasyon içinde yeni değişkenler, fonksyonlar, stateler eklenebilir ve eskilere de halen erişilebilir. Örneğin oyunumuza bir yaratık eklemek istiyorsak burada devralacağımız class yüksek olasılıkla Pawn class ı olacaktır. Yani pek çok şey zaten hazır yapılmış ve tekerleği yeniden icat etmemize gerek yok. Tüm yapmamız gereken ilgili sınıfı bulup devralmak ve genginleştirmek, kendimize göre özelleştirmektir. Özelleştirirkende varolan yapıyı bozmuyoruz. Onlar aynı şekilde kullanılabilir kalmaya devam ediyorlar, ama eğer istersek onları taban alan yeni fonksyonellikler elde edebiliyoruz, ya da bazılarını değiştirebiliyoruz. Bu sistem özellikle UDK nın yapay zeka (Artificial IntelligentI) sistemini kullanırken çok işe yarar. Çünkü yapay zeka başlı başına apayrı bir dünya, payrı bir uzmanlık alanıdır, öğrenilmesi ve uygulanabilecek kadar uzmanlaşılması zaman alır ve AI oyunların vazgeçilmez parçalarından biridir. Oysa UDK da AI için yazılmış çok sayıda hazır modül vardır! Tüm yapmanız gereken kendi yarattığınız kodlarda bu modülleri çağırıp özelleştirebilmemizdir.

UnrealScript de class tanımlarken extra özellik katmak için bazı özel belirteç ler (özel kelimeler) vardır demiştik. Yukarıdaki listede bunları 2. sırada saymıştık. Şimdi dilerseniz bunlardan bazılarını kısa bir biçimde tanımlayalım. Bu makalede detaylı örnek yapmayacağım ama en azından ne olduklarını tanım itibari ile bilelim :




Native(PaketAdı)
Bildiğiniz gibi UDK C++ ile yazılmıştır. İşte UDK nın bu C++ alt yapısıne erişmek istersek Native, yani doğal kod deyimini kullanmamız gerekir. Yani UDK nın doğal kodu olan C++ yapısında yazıyor ve o düşük düzeye erişiyoruz. Native class lar mutlaka doğal C++ düzeyindeki bir class ı devralmak zorundadır. Derleme sonunda Native class otomatik olarak C++ header dosyası (.h) oluşturulur. UnrealScript içinden üretilen bu doğal C++ kodu .uc içindeki değişken ve fonksyonlar ile etkileşime girebilir. (ne esneklik...) Native deyiminin parametresi olan paket adı ise default olarak class ın bulunduğu paketin adıdır. Örneğin .uc dosyasının adı X ise, burada üretilen Native class ın üreteceği C++ kodunun dosya adı XClasses.h olacaktır.


NativeReplication 
Sadece native sınıflara özgü olan bu deyim, kullanıldığı sınıf içindeki değişken değerlerinin C++ doğal kodu ile olacağını belirtmektedir.

DependsOn(ClassName1, ClassName2, ….)
Parantez içinde belirtilen sınıf(lar)ın bu sınıftan önce derlenmesini derleyiciye bildirir. Parametrede belirtilen sınıf adı başka bir paketten yada aynı paketten olabilir. Tek bir class ın önden derlenmesini istiyorsak sadece o sınıf adını yazabiliriz. Ama birden çok sınıfın önceden derlenmesini istiyorsak ya aralarına virgül koyarak yan yana yazarız ya da her bir sınıf adı için DependsOn(ClassNameX) komutunu alt alta yazabiliriz.

Abstract
Sadece Base class olacak, yani sadece başka sınıflar tarafından devralınacak sınıf taratmak için kullanılan belirteçtir. Abstrack olarak tasarlanmış bir sınıfın aktörlerini UnrealEd 'deki (design editördeki) dünyaya ekleyemezsiniz. Ya da oyun süresince bu sınıfın örneklerini yaratamazsınız. Neden? Çünkü bu sınıf kendi içinde anlamlı değildir. Anlam kazanabilmesi için mutlaka başka kalıtım ile alınmalı ve zenginleştirilmelidir. Zaten OOP bilenleriniz Abstact sınıfları daha önce duymuştur ve bunu daha iyi anlayacaklardır. Abstact ifadesi ile derleyiciye ürettiğiniz sınıfın sadece kaltım içinde kullanılabileceğini söylüyorsunuz. Örneğin UDK içinde “Actor” sınıfı da bir BASE class tır. Hatırlayacaksınız çoğu sınıf Actor den türetilmiştir demiştik. Başka türlü Actor sınıfını kullanmaları da mümkün değildir zaten. Actıor Abstract bir sınıf olduğundan ne doğrudan örneklenebilir (nesne oluşturulabilir), ne de doğrudan tasarım editöründe görünebilir. Bunu ancak Actor ü devralarak özelleştirmiş daha üst düzey sınıflar yapabilir.

Config(IniName)
Config ifadesi o sınıfın datalarını .ini dosyalarının içinde tutmasına izin verileceğini belirtmektedir. Eğer konfigüre edilebilmesini arzu ettiğimiz datalarımız var ise bu dataları içeren sınıfı “config” veya "globalconfig" ifadesi ile birlikte yazarız. Pratikte bir çok bilgimizin configuration dosyalarında saklanmasını isteyeceğiz. Bir sınıfı config ile tanımladığı zaman onu devralan yeni sınıflarda da aynı özellik devam eder. Yani türetilen sınıfta config deyimi kullanılmasa bile base (temel) sınıfın config özellikleri aynı şekilde devam eder. Çocuk sınıflar bu özelliği kaldıramazlar. Ama değiştirebilirler. config özelliklerini yeniden tanımlayarak yeni IniName verebilirler. Buradaki IniName verilerin depolanacağı .ini dosyasının adıdır. Ama bir kaç özel kelime daha farklı anlamlara gelir. Şöyle ki :

Config(Engine) : Bu deyim Engine konfigürasyon dosyasının kullanılacağını söyler. Bu dosyanın
adı OyunAdiEngine.ini gibi bir isme sahip olacaktır.

Config(Editor) : Editör config dosyasını kullanır. Bu dosyanın da ismi OyununismiEditor.ini gibi bir isim olacaktır.

Config(Game) : Bu da malum Game konfigürasyon dosyasını kullanır. Oyununuzdaki muhtemel adı yine OyunadiGame.ini şeklinde olacaktır. Oyun adı malum burada sizin oyununuzun adı.

Config(Input) : Bu da aynı şekilde giriş çıkış kontrolleri ile ilgili konfig dosyasını kullanır. O da aynı şekilde OyunAdiInput.ini şeklinde olacaktır.

Yani “Config” belirtecinden sonra Engine, Editor, Game ve Input özel kelimelerini kullanırsanız ilgili config dosyası kullanılıyor. Yok bunların dışında bir kelime kullanırsanız örneğin : Config(fasulye) derseniz, fasulye.ini adında bir dosya oluşturulacak ve verileriniz orada tutlacaktır.


EditInlineNew
Tasarım editörü ile ilgilidir. Bu deyim ilgili sınıftan yaratılan objelerin Editördeki UnrealEd property window undan da yaratılabileceğine işaret etmektedir. Normalda Editör sadece varolan objelere değer atamak içindir. Ama bu belirteç ile tanımlanan sınıfların objeleri UnrealEd in özellik penceresinden de yaratılabilir. Bu davranış devralınan çocuk sınıflarda da sürdürür. Eğer çocuk sınıflar bu özelliği değiştirmek isterlerse “NotEditInlineNew” keyword ünü kullanırlar.

NotEditInlineNew
az önce de söylendiği gibi bu ifade çocuk sınıfta kullanılır. Base (temel) (parent) sınıfta tanımlanan EdirInlineNew özelliğini devre dışı bırakır. Eğer base sınıfta bu özellik yok ise bir etkisi yoktur.

Placeable
Yine level tasarım editörünü ilgilendiren bir ifadedir. Tanımlanan sınıfın objelerinin EdirInlineNew da olduğu gibi yine editörden yaratılabileceğini, ek olarak obje nin level (sahne) de konumlandırılacağını (place edileceğini) hatta eğer veri tipi uygun ise kısmet penceresinde de yer alacağını bildirir. Çocuk sınıflarda da benzer özellik devam eder. Türetilen sınıflarda bu özelliği değiştirmek için NotPlaceable keyword ü kullanılır.

NotPlaceableBase sınıfın Placeable özelliğini devre dışı bırakır. Bu sınıfın artık level de ve editör de görünmeyeceğini ifade eder.

Inherits(ClassName[,ClassName,...])
Çoklu kalıtım (multy inheritence) için kullanılır. Normalde extends ifadesi ile tek bir sınıfı devralırız; ama bu ifade ile başka BASE sınıfları da devralmak üzere ilave edebiliyorsunuz. (ne açgözlülük :)) Inherits komutuna parametre olarak aralarına virgül koyarak çok sayıda BASE class ismi belirtebilirsiniz. Yada komutu alt alta yazarak her satırsa bir base class ismi belirtebilirsiniz. Bu komut ile uygulanan çoklu kalıtımlar sadece native yani alt yapıdaki sınıflar için geçerlidir. UDK nın normal sınıflarında çöoklu kalıtım yapamazsınız. Zaten çoklu kalıtım dikkatli kullanılması gereken bir konudur. Aksi durumda başınıza ciddi sorunlar açabilir :)

NoExport
bu sınıftaki C++ ifadelerinin derleyici tarafından otomatik olarak üretilen bir C++ header dosyasında konmayacağını ifade eder. bu durumda buradaki C++ ifadesi manuel olarak ayrılmış bir header dosyasında belitrilmelidir. Bu komut elbette sadece native sınıflar için geçerlidir.

PerObjectConfig
Sınıfın configurasyon bilgisini her bir obje için ayrı ayrı kısımlarda depolar. Her bir kısım için ismi atanan .ini dosyasında bir başlık oluşur : [ObjectName ClassName] şeklinde. Bu özellik kalıtımla türetilen sınıflara da aktarılır.

PerObjectLocalized
Sınıfın bilgilerini her bir objenin kendi kısmına sahip olduğu bir ayar dosyasında tanımlanmasına neden olur. Bu özellik kalıtımla türetilen sınıflara da aktarılır.

HideDropDown
UnrealEd de sınıfın UnrealEd property winbdow da bir combo box içinde görünmesini engeller.

HideCategories(Category[,Category,...])
UnrealEd in property window daki bazı kategorileri gizlemek için kullanılır. İstenilen kategori adları eğer bir den fazla ise aralarına virgül konarak parametre olarak verilir. Eğer hiçbir kategorisi olmayan bir değişkeni gizlemek istersek, sadece sınıf adını veririz.

ShowCategories(Category[,Category,...])
HideCategories in tam tersidir. HideCategories in etkisini yokeder. Türetilen sınıflarda uygulanır. Base class a etki eder.

AutoExpandCategories(Category[,Category,...])
Bir veya daha çok kategorinin unreal editör (UnrealEd) içindeki property window unda otomatik genişler menü şeklinde yer almasını sağlayan belirteçtir. Kategorisi olmayan değişkenleri auto expand yapmak için değişkenin dahil olduğu sınıfın ismi kullanılır.

Collapsecategories
UnrealEd in property window u içinde söz konusu sınıfın özellikleri (değişkenleri) nin gruplar halinde kategorilenmesini sağlar. Bu özellik devralınan çocuk sınıflar da da kendisini gösterir. Türetilen sınıflar bu özelliği DontCollapseCategories keyword ü ile bertaraf edebilir.

DontCollapseCategories
az önce de söylendiği gibi Collapsecategories in tam tersidir, çocuk sınıflarda kullanılır. devralınan base sınıfın Collapsecategories özelliğini devre dışı bırakır.

ForceScriptOrder(true/false)
UnrealEd in property window unun bu sınıfın objelerinin özellik ve categorilerini script olarak tanımlanmış komutları işlemek için göstermeye zorlar.

Deprecated
Bütün objelerin kaydedilmeden yüklenmesine neden olur. Yinede Actorler editore yüklediklerinde uyarı verir.

Transient
bu sınıfa ait olan nesnelerin asla dike kaydedilmemesi gerektiğini ifade eder. Sadece belli bir tür kalıcı olmayan doğal (native) sınıflar için faydalıdır. Örneğin : Oyuncular ya da pencereler gibi.

ClassGroup(Groupname[,Groupname,...])
editörün actor browser inin belirtilen grup adı içerisinde bu sınıfı ve bu sınıfın herhangi bir alt sınıfını içermesi gerektiğini söyler. grup görünürlüğü actor browser da devrede olduğunda elbette.


Evet sınıf niteleyicilerin çoğunu da listeledik. Detaylı kod örneklerini ilerleyen yazılarda yapacağız. Bu yazımızda UDK katman yapısını ve UnrealScript in çalışma mantığını incelemeye çalıştık. Şimdilik de bu kadar olsun diyelim :) Bir sonraki yazıda görüşmek üzere hepinize sevgiler..

Hiç yorum yok: