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

24 Mart 2011 Perşembe

Unreal Development Kit (UDK) - GameMode



Herkese selam;
Bu makalemizde ilk makaledeki  bilgileri biraz daha açıcaz ve biraz daha ileri giderek ilk küçük kod parçamızı yazıcaz. Bu kod görünürde fazla birşey yapmasada yeni bir oyun yaratırken yapılacak işlemlerin temellerini anlamamızı sağlayacak.

Önce eski bilgilerimizi biraz tazeliyelim :
Developer/Src altındaki her folder bir script paketidir. Ve her folder “Classes” adında bir alt foldera sahiptir. İşte .uc uzantılı tüm class tanımlamaları onların içindedir.

UnrealScript ileri OOP desteğine sahip bir dildir ve her bir .uc uzantılı dosya tek bir class tanımı içerir. Aslında incelediğinizde tüm sınıfların başka sınıflardan miras alınarak oluşturulduğunu görebilirsiniz. Yani UDK da sınıf devralma (inheritence) bol bol kullanılmaktadır. Tabi bir sınıf başka bir sınıfı miras alacağı zaman onun daha önceden tüm özellikleri ile tanımlanmış olması şarttır. Ve SRC altındaki her .uc uzantılı dosya bir kod dosyası (kod paketi) ise,, ve her kod dosyası bir üst sınıftan özellikleri devralan yeni bir sınıf tanımı ise,, biz de kendi sınıflarımızı geliştirirken yararlanmamız gereken kod paketlerinin (sınıfların) bulunacağı yeri folder yapısından kolayca kestirebiliriz.

Dolayısı ile biz bu folderlardaki sınıfları oyunumuzu yazarken bir framework gibi çağırarak kullanacağız. Mesela SRC altındaki dizinlere bir gözarar isek : Core, Engine, GameFramework... vs. gibi folderlar emrimize amade binlerce class içermektedir. Biz de SRC altında kendi  folderimizi yaratıcaz ve kodlarımızı yaratırken buraya ya doğrudan yazacak ya da diğer folderlardaki sınıfları genellikle miras alma (inheritence) yöntemi ile çağırıcaz. Genellikle de her ikisini birden yapıcaz.

Oyunumuzu ilk oluştururken karar vermemiz gereken en önemli şey Oyun Modu (GameMode) kavramıdır. GameMode nedir? Oyun modunu çok özetle oyunun oynanma şeklini belirleyen kurallar kümesi olarak tanımlanabilir. Elbette bu tanım size bugünedek oyunlardan bildiğiniz klasik oyun modlarını çağrıştırsa da UDK Script deki GameMode terimi, oyunun belkemiği diyebileceğimiz en temel sınıf olan GameInfo Class ının yapısının teknik ifadesidir. UDK da programlamaya ilk önce GameMode sınıfımızı yaratarak başlarız. Bu sınıf herşeyin temelinde çalışacak olan ve oyunumuzun alt yapısını belirleyecek olan en hayati sınıftır.

Bir UDK oyun modu, GameInfo sınıfının veya GameInfo sınıfından türemiş başka bir sınıfın devralınarak zenginleştirilmesi ile elde edilir. Sonuçta elde edilen / edilecek olan sınıf ne kadar üst seviye olursa olsun en altta (temelinde) UDK nın oyun modu için bize sunmuş olduğu en temel sınıf olan GameInfo class ı base class olarak bulunur. Dolayısı ile oyun modu olarak üretilen nesneye pratik olarak GameInfo class tipi diyebiliriz.

UDK nın bize sunmuş olduğu yapıda birçok oyun modu hazır bir şekilde emrimize amade sunulmuştur. Bunlar : Deathmatch, Capture The Flag , Vehicle Capture The Flag vs.. İçinizde Quake veya Unreal Tournament oynayan varsa bu oyun modlarını kolayca anımsayacaktır. Yanlış hatırlamıyor isem Quake in bir de “free for all” seçeneği vardı, bu modun teknik tabiri : takım arkadaşı yok, amaç yok, hareket eden herşeyi öldür idi :)  Tarz olarak birbirine oldukça yakın olan bu oyunlarda olay genellikle benzerdir. Silahtan kamera görünür, bazen tek, bazense takım olarak savaşırsınız.



(bu arada siz de benim gibi quakelive de uykusuz saatlerinizi harcayanlardansanız bu ekran size tanıdık gelecektir :)

Epic Games in UDK yı Unreal Tournament oyununu ürettiği yapı ile birlikte sunduğunu düşünürsek aynı oyun modlarını da kaynak kodları ile bize sunmuş olması gayet normaldir. Yani UDK ile birşeyler ürettiğinizde, siz aksini belirtmediğiniz sürece oyununuz yukarıdaki resim tarzında olacaktır. Mesela hehangi bir harita (map) üretip FrontToEnd de çalıştırır isek haritamız hemen UTDeathmatch modunda çalışacaktır. Çünkü bu UDK nın default olarak sunulan modudur. UDK ile birlikte hazır gelen tüm modlar da temelde benzerdir. Eğer bu tür bir oyun yapacak isek hiç uğraşmaya gerek kalmadan bu hazır modları çağırabilir ve kendi projemizde kolayca kullanabiliriz.

Harita dosyamızın da ismini CTF-  veya  VCTF- gibi dosya ön ekleri ile (prefix) başlayacak şekilde
yazarsak haritamız UDK nın diğer hazır tanımlı modlarında da kolayca çalışacaktır. Ama oyun malum gene klasik tarzda olacaktır. Buradan haritalardaki dosya ön ekinin oyun moduna karar verilirken etken rol oynayabildiğini anlıyoruz. Birazdan bu konuyu daha detaylı inceleyeceğiz.

Unreal Tournament veya Quake gibi bir oyun yapacak bile olsak pratikte neredeyse hiçbir zaman UTDeathmatch modunda oyun üretmek istemeyebiliriz, çünkü hiç olmazsa birazcık olsun bizim kurallarımız geçerli olsun istiyorsak mutlaka kendi oyun modu sınıfımızı üretmek durumundayız. Ve tabiki yaptığımız haritanın da bu yeni modda çalışmasını sağlamalıyız.


Yukarıda UDK nın oyun modlarına ilişkin class yapısı gözükmekte. Buada dikkatimizi çeken nokta, tüm Unreal Tournament game mod larının bir ağaç apısı içinde birbirini takip ettiği. Bu takip aslında programlamadaki miras (class inheritence) yapısıdır. Yani bir alt class üst sınıfı tamamen devralarak zenginleştirmiş ve ortaya eskisini temel alan ama daha farklı ve yeni bir class çıkmıştır. Dolayısı ile UTCTFGame oyun modunun base (temel) classs ı UTTeamGame, onun da base class ı UTDeathmatch olduğunu anlıyoruz. Ve HEPSİNİN de temelinde UTGame temel sınıfının olduğu anlaşılıyor. Yani UTGame tüm Unreal Tournament oyun modlarının temel sınıfıdır diyebiliriz.

UTGame sınıfı  Development/SRC altında bir script paketi olarak açık kaynak kodları ile bulunmaktadır. UTGame sınıfı Unreal Tournament oyunlarının alt yapısını oluşturacak tüm unsurları içinde barındırıyor. Dolayısı ile eğer arzu edersek biz de onu miras alarak onu kendi oyunumuzda kullanabiliriz. Nasıl ki bayrak çalma oyun modu ile takım ölüm maçı oyun modları farklı modlar olduğu halde UTGame temel alınarak, türetilmiştir; biz de bu sınıftan kendi oyun modumuzu üretebiliriz. Ancak şimdiden söyliyim ne yaparsak yapalım oyunumuz genel yapı itibari ile Unreal Tournament oyunlarının bir benzeri olacaktır. Yani bu işin türkçesi o ki : UTGame oyun modu ile comodore 64 ün yandan görünüşlü balon patlatmaca oyununu yapamazsınız. Kamerayı sağa sola çevirip benzetseniz bile UTGame ile üretilen herşeyin malum doğasına karşı gelmeye çalışmak bir yığın sorunu da beraberinde getirecektir. Bu onu gerçekte olmadığı bir oyuna benzetme çabası akıntıya karşı kürek çekmek gibi olacaktır.

UDK ile atıyorum bir FRP türü oyun yapmak istersek ya da gerçekten yandan görünüşlü balon patlatmaca veya kurtçuk oyunu yapmak istersek ne olacak? :) Bu durumda yukarıdaki şekildeki sınıf diyagramına tekrar gözatar isek,, UTGame in de aslında bir alt sınıf olduğunu ve onun da başka bir (!) temel sınıftan devralınarak  türetildiğini görürüz. İşte bu gerçekten temel (!) sınıfın ismi GameInfo sınıfıdır.

GameInfo sınıfından miras alınarak oluşturulan UTGame ve UTGameInfo sınıfları superclass tır ve Unreal Tournament içinde gördüğümüz bütün oyun modlarını onlardan üretilmiştir. GameInfo ise en temel seviyedeki UDK oyun alt yapısıdır. Temel seviyeden kastım henüz belli bir oyun şekline henüz dönüştürülmemiştir.

Bu nokta oyunumuz için hayati kararı vereceğimiz noktadır. GameInfo, UTGameInfo veya UTGame sınıflarından hangisini miras alarak oyun modumuzu üreteceğiz?  Sınıfımızı GameInfo dan miras alarak üretme bize hayal gücümüz ile sınırlı bir atmosferi geliştirme imkanı sunar. Hangi tarz veya hangi şekilde kurallarda isterseniz oyununuzu şekillendirebilirsiniz,, Tanrısı siz olduğunuz (haşa... :)) bir dünya....

UTGame ve UTGameInfo dan miras alarak oyun modunu üretmek ise daha hali hazırda bir sistematik sunar. Bu sistematik aşağı yukarı Quake / UnrealTournament a benzer bir dünyadır. Özellikle de bu tür bir oyun yapmak istiyor isek, varolan hazır yapı ile çabucak ilerlersiniz. Ancak bu çabuk ilerlemenin de bedeli oyun alt yapısının aşağı yukarı belli olmasıdır. GameInfo da ise çok daha özgürsünüz, ama çok daha fazla kod yazmanız gerekir.

GameInfo ve ondan miras alınarak üretilen tüm sınıflar bizim oyunumuz için bir temel sınıf olabilir. Bunlara genel olarak GameMod sınıfları da diyebiliriz. Kalıtım seviyesi arttıkça (GameInfo dan aşağı doğru inildikçe) daha fazla özellik ve fonksyonellik elde ederiz. Ama dünyayı daraltırız. Bu yönülye düşünürsek GameInfo en super class tır, çünkü hepsinin atasıdır :) Ama UT* ailesindeki diğer sınıflara oranla sizi çok daha fazla uğraştıracağına şüpe yoktur.

Hiyerarşi ağacındaki UTGame den bir sonraki class olan UTDeathmatch tır ve aslında basit bir oyun modudur. Pek fazla kural yoktur. Amaç malum çevrenizde gördüğünüz tüm hayat belirtilerini yoketmek... :D Bir alt düzeydeki UTTeamGame ise takım içeren tüm oyn modları için bir superclass tır. Aslında o da TeamDeathMatch gibi çalışır. Ve butun UTTeamGame lerin yaptıgı gibi takım calışması gösterir. Team Deathmatch oyun modunu normal Deathmatch moduna takım fonksyonelliği eklediğimizde elde ederiz. Hiyerarşideki bir sonraki class olan UTCTFGame sınıfı ise capture the flag oyun modunu destekler. UTCTFGame sınıfı UTTeamGame temel sınıfı devralınarak türetilmiş olan yine takım temelli bir başka oyun modudur. Fakat amaç biraz daha farklı olsada bayrak çalma modu da yine yukarıdaki UT ailesi modlara oldukça yakındır. Kavram biraz daha daraldılmış durumdadır.

Bu class hiyerarşisini temel olarak anlamak yapmak istediğimiz şeyleri yapabilmemizin anahtarıdır. 

Bugünkü örneğimizde biz de yeni bir oyun modu yaratacağız. Örneğimizde kolay olması açısından temel sınıf olarak UTGame i kullanacağız. Bu şekilde farklı bir adda yeni bir sınıf, yani yeni bir script paketi tanımlamış olacağız. Yeni sınıfımıza da UDKGame adını vereceğiz. Gerçi devraldığım yapıya pek yeni bir kod eklemeyeceğim şimdilik. Ama bizim verdiğimiz bir isimde yeni bir sınıf oluşacak. Ve olayın alt yapısınını anlamak açısından küçük basit bir örnek yapmış olacağız.

Karar vermemiz gereken bir diğer konu da  .udk uzantılı harita (map) dosyalarımızın ön ekinin (prefix) ne olacağıdır. Bildiğiniz gibi UDK da prefix kavramı var ve map dosyalarının prefix lerine göre bazı ayarlamalar otomatik olarak yapılır. Örneğin UT-haritaadi.udk şeklindeki bir map dosyasının UnrealTournament oyun modunda çalışır. Birazdan bu konuya da giricez ve haritaların bizim oyun modumuzu kullanmaları için bir prefix tanımlayacağız.


Pekala başlayalım;
Önce SRC dizini altında UDKGame adında bir folder yaratalım ve içine ekleyeceğimiz UDKGame.uc dosyasını yazmaya başlayalım :

class UDKGame extends UTGame;

her kod dosyasının en üstünde buna benzer bir satır göreceksiniz. Bu satır derleyiciye yeni bir sınıfın yaratıldığını ve bu sınıf ile ilgili tüm kodların yine bu dosyanın içinde olacağını söyler. Yani her .uc dosyası yeni bir sınıf tanımlama satısı ile başlar ve hemen ardından bu sınıfa ilişkin kodlar ile devam edilir.

UDK da miras (inheritence) yoğun bir şekilde uygulanıyor demiştik. Buradaki ilk satırda en sondaki UTGame devralınan base class ı, UDKGame ise yeni oluşturulan çocuk (türetilen) sınıfı temsil eder. Aradaki extends ifadesi ise UDK da miras alma olayını simgeleyen deyimidir. Bütün kalıtımlar bu extends ifadesi ile olur. Extend kelime anlamı ile zaten genişleme, büyüme anlamı taşır. Burada da biz halihazırda varolan UTGame i alıp genişleticez ve yeni yarattığımız sınıfın adına da UDKGame diyeceğiz.

Artık UDKGame adında yeni GameInfo (tabanlı) sınıfımız var. Hatırlayın UTGame de GameInfo yu genişletmişti. Yani onun çocuk sınıfı idi. Bu durumda artık UDKGame de bir  GameInfo tabanlı class yani GameModu sınıfı olduğunu söyleyebiliriz. Çünkü bu tek satır kod ile UTGame ile eş bir sınıf yaratmış olduk.

Şimdi,
udk nın bizim yarattığımız oyun modumuzu kullanması için yapmamız gereken iki şey vardır :
1- Engine nin bizim kod paketimizi de yüklemesini sağlamak;
2- Enginin bizim oyun modumuızu kullanmasını sağlamak;

bu işlemler ilk makaleden hatırlayın config dosyalarındsa yapılıyordu. Aksi takdirde UDK ya orada olduğunuzu söylemezseniz istediğiniz kadar kod yazın sizi kale almayacaktır :)

Hazır yeri gelmişken config dosyaları ile bir kaç pratik kuralı daha burada paylaşmak istiyorum. Daha iyi anlaşılmaı için madde madde verelim :

  • UnrealEngine konfigürasyon ayarlarını UTGame\Config dizini altından yakalar.
  • UDK da 3 tip konfig dosyası vardır bunlar : UT*.ini , Default*.ini ve Base*.ini dosyalarıdır.
  • Base.ini config dosyaları epic in motor için koyduğu en temel ayar dosyalarıdır. Onlara dokunmuyoruz. (şimdilik :))
  • İlk önce Engine folderinin altında bulunan Base*.ini ayar dosyaları yüklenir; Daha sonra Config dizini altındaki Default*.ini ayar dosyaları yüklenir; En son da aynı dizin altındaki UT*.ini ayar dosyaları yüklenir.
  • Default*.ini ayarları Base*.ini deki ayarlara baskın gelecek şekilde işleme konur.
  • UT*.ini dosyasındaki ayarlar da Default*.ini deki ayarlara baskın gelecek şekilde işleme konur.
  • Default*.ini dosyaları bizim standart ayar dosyalarımızdır. Bu ayarlar oyununuzu dağıtırken standart olurlar Base*.ini ayarlarına baskın gelirler. Eğer bu dosyalar yoksa onlar base*.ini deki temel ayarlar ile yeniden yaratılırlar.
  • UT*.ini dosyaları son kullanıcıya ulaşan yegane (en son) ayarlardır. Son kullanıcı bunları yanlışlıkla silse bile default.ini dosyalarındaki temel ayar sistemi ile tekrar oluşturulabilir. (Ama şu aşamada bu bir zaman kaybı olduğundan gerek kalmaması için bugünkü örneğimizde sadece Default*.ini leri kullanacağız.)
  • Artı tüm ayar editlemelerini UT*.ini ler içinde yaparsak ve son kullanıcı bunları değiştirirse, tekrar düzgün config dosyaları elde etmek için oyunlarını tekrar install etmek zorunda kalabilirler.
  • Her ne kadar bazı opsiyonları kullanıcıya bırakmak isteyebileceğimiz durumlar olabilecek olsa da şu aşamada olayı kökünden halledip tüm değişikliklerimizi Default*.ini dosyalarında yapıcaz.
  • UT*.ini config dosyalarını siliyoruz. Taki onlara özel olarak ihtiyaç duyana kadar.
  • Eğer yaptığınız değişiklikler bir işe yaramamış görünüyorsa UT*.ini dosyalarını silmiş olduğunuza emin olun. Ve tekrar yükleyin.
  • Herhangibirşey ters giderse o esnada da UT*.ini dosyalarını tekrar silin.


Şimdi DefaultEngine.ini dosyasını bir metin editöründe açalım. Bu config dosyası önemli kritik ayarlar taşır. Motora hangi modüllerün yükleneceği ve bunları sırası ile ilgili bilgiler verir. Yeni bir script paketini oyuna dahil etmek istediğimizde mutlaka burada ayarlama yapmak zorundayız.

[UnrealEd.EditorEngine]
;ModEditPackages=MyMod

bu satır motora bizim script paketimizi yüklemesini söylediğimiz yerdir. Satır başındaki noktalıvirgül satırı comment haline getiriyor. Eğer komutun işleme alınmasını istiyorsak noktalıvirgülü kaldırmalıyız.

ModEditPackages=MyMod” bu satır motora der ki : Oyunumuzun modu MyMod olacaktır ve kodların yeri de SRC altındaki MyMod dizinidir. Bu dizin UDK kurulurken default olarak oluşturulur ve içi boştur. Bir kodlamaya başlama örneği teşkil etmesi açısından yaratılır ve yine örnek teşkil etmesi açısından comment halinde config dosyalarına eklenir.

Buradaki satırı UDKGame olarak değiştirelim. Ve varsa başındaki noktalıvirgülü kaldıralım. Bu sayede motor UDKGamee içindeki kodlarımıza izin verecek ve UDKGame paketini yükleyecek.

ModEditPackages=UDKGame

Eğer motor paketimizi doğru bir şekilde yüklüyor ise Frontend in derleme işleminde bunu görebiliyor olmamız gerekir. Kodlarımızı ForntEnd de “Full Recompile“ komutu verdiğimizde tüm paketler ne olduklarına ya da en son ne zaman derlendiklerine bakılmaksızın tekrar derleyecektir. Derleme esnasında satır bildirimlerini takip ederek eklediğimiz paketleri de takip edebiliriz. Tabi bu durumda değişmemiş olmasına rağmen derlenmiş tüm paketler tekrar derleme işlemine tabi tutulurlar. Sonuç değişmese de bir miktar zaman kaybı yaşarız. Bunun yerine sadece değiştiğini bildiğimiz paketleri derleme işlemine tabi tutabiliriz. Doğrudan make buttonunun üstüne tıklayarak sadece değişmiş olan paketlerin derlenmesini sağlarız. Derleme logları arasında şayet UDKGame paket ismini görmüş isek ve derleme başarılı olmuş ise paketimiz yüklenmiş demektir. (!) Yani artık paketimiz doğrudan kullanılabilir durumdadır. Ama hala henüz devreye alınmamıştır. Bu bir sonraki aşamada olacak.

Buraya kadar ne yaptık?
Yeni bir oyun objesi yarattık ve .uc uzantılı dosyayı aynı adı taşıyan klasöre (SRC altında) ekledik. Sonrada bu paketin derleme işlemine katılması ve yüklenmesi için DefaultEngine.ini dosyasının [UnrealEd.EditorEngine] tabının altında ModEditPackages=UDKGame satırını ekledik. Ve FrontEnd den derleme işlemi yaptık. Böylece paketimiz derlendi ve UDK motorunca tanınır hale geldi. ama henüz göreve atanmış durumda değil :)

Devam edelim,
Şimdi bir sonraki aşamada derleyiciye default oyun modu olarak bizim miras alarak yaptığımız oyun modunu kullanmasını söyleyeceğiz. Bunu da yine ayar yaparak halledeceğiz. Şimdi DefaultGame.ini dosyasını açalım ve içindeki [Engine.GameInfo] tabına ulaşalım.

[Engine.GameInfo]
DefaultGame=UTGame.UTDeathmatch
DefaultServerGame=UTGame.UTDeathmatch
PlayerControllerClassName=UTGame.UTPlayerController
…...
..


DefaultGame özelliğini değiştirerek oyun modunu kendi yazdığımız mod olarak belirlemiş oluyoruz.
şimdi ilgili satırları aşağıdaki şekilde değiştirelim.


[Engine.GameInfo]
DefaultGame=UDKGame.UDKGame
DefaultServerGame=UTGame.UTDeathmatch
PlayerControllerClassName=UTGame.UTPlayerController
…...
..

Paketlerin içinde pek çok şeye referans gösterebiliriz : static mesh ler,, material ler, parçacık sistemleri, code class lar vs... Ama onları referans ederken tam isimlerini belirtmek durumundayız. Oyundaki bir varlığın tam ismi  PackageName.GroupName.Asset  şeklindedir. Her asset (valık), grup ismine sahip olmayabilir. Örneğin kod class ları (kod paketleri) tam adı içinde grup ismine sahip olmayan varlıklara örnektir. Ancak bir varlığı bildirirken yazdığımız isim ifadesi mümkün mertebede derleyicinin onu bulmasını kolaylaştıracak nittelikte olmalıdır. Biz burada UDKGame ismini hem paket ismi (folder adı) olarak hem de GameInfo temel sınıfının adı olarak kullandık. (Ama aynı olmak zorunda değiller.) Kod paketlerinin referans edilme şekli her zaman paket_ismi.varlık_ismi şeklinde ise bizim oyunumuzun temel classı nın tam referans adı : UDKGame.UDKGame dir.

Dolayısı ile DefaultGame.ini içindeki DefaultGame özelliğini de aşağıdaki şekilde değiştirdik :
DefaultGame=UDKGame.UDKGame


Şimdi oyun modumuzu test edelim:
DefaultGame.ini dosyamızı katdedip bütün UT*.ini dosyalarını silelim. Konfigürasyonlarımız tamamladıktan sonra oyun modumuzu test etmek için frontend ten hemen Launch buttonuna basıp yükleyemeyiz. Bunu yaparsak UTFrontEnd ilk aşamadaki geçerli olan ayarları yükler. (biraz evvel derlenen şekilde.) O yüzden şu aşamada tekrar derlemek yerinde olacaktır.

Derlemede bir sorun yoksa sıra geldi bir MAP, yani harita belirtmeye. *.UDK uzantılı haritamızı frontend e verirken her haritanın farklı GameModu na sahip olabileceği hayati bilgisini akılda tutmakta fayda var. Peki bir haritanın hangi modda çalışacağını nereden bileceğiz? İşte tam bu noktada tekrar karşımıza prefix (isim ön eki) kavramı tekrar ortaya çıkıyor. Makalenin başlarında da söylediğim gibi bazı önceden tanımlı prefixler gereği DM- ile başlıyan haritaların modu UTDeathmatch, CTF- ile başlıyanların UTCTFGame game, yani bayrak çalma modu olacaktır vs.. Ve bir haritanın başında prefix eklemek doğrudan o modda çalışabileceği anlamına da gelmez. Söz konusu oyun modunun kodları haritamızda olmayan bazı unsurlar içeriyorsa (örneğin oyuncuların başlangıç noktaları veya daha başka herhangi birşey) oyun yüklenirken sorun olacaktır. Ve biz hiç harita yapmadık; o yüzden şimdilik UDK nun deneme yapmamız için hizmetimize verdiği ve sorunsuz yüklenebilecek bir harita olan ExampleMap.udk yı kullanacağız. ExampleMap.udk özel ayar ve arayüz değişiklikleri gerektirmeden sorunsuz çalışacaktır. Bu udk ile birlikte hazır gelen örnek deneme haritasıdır. Bu haritayı seçip Launch a bastığımızda oyunumuz doğrudan bu sahada arayüz olmadan çalışacaktır.


Şimdi biraz daha detaylar üzerinde kafa yormaya başlayabiliriz;
Oyuncu kontrolü nasıl olacak? Ekrandaki bildirim grafikleri (HUD) nasıl olacak? Oyun modumuzu doğrudan GameInfo sınıfından dan miras alarak oluşturmuş olsa idik ne elimizde silah ne de ekranda herhangi bir HUD grafiği olurdu. Bütün bular ve fazlası UTGame sınıfının standarlarıdır. UTDeathmatch kodunu açıp incelerseniz, UTGame ve UTDeathmatch sınıflarının hemen hemen aynı olduklarını görürsünüz. Çünkü malum onlar da UTGame den miras alınarak üretilmiştir. Hele ki biz UTGame i miras aldık ve üzerine hiç kod eklemedik. Yani şu durumda ismi farklı bile olsa aslında ekranda gördüğümüz UTGame oyun modunun takendisidir. Ve UTDeathmatch moduna buna oldukça yakındır. Aralarındaki basit farklılık UTGame oyunculara fiziksel silah verir oysa UTDeathmatch vermez. Eğer her ikisinin de defaultproperties kod bloğunu incelerseniz sonlarda bazı farklılıklar göreceksiniz. (Daha sonra detaylı gireceğiz bu konuya da.) UTGame modu fiziksel silah verdiğine göre ve bizim modumuz da hemen hemen aynı olduğuna göre oyunda silahımız var demektir. Ve aynı şekilde oyuncunun oyun ile etkileşimini sağlayan control sistemi de (PlayerController) yine miras aldığımız UTGame inki ile aynıdır.

UDK daki başlı başına bir konu da “Actor Class” tır. Aktör sınıfları oyuncumuzun oyun atmosferi ile etkileşebileceği tüm unsurlardır : Alınabilir eşyalar, basılabilen buttonlar, açılan kilitler, mermiler vs. vs.. vs.... İlk bakışta onlar ile etkileşebileceğimizi bilemesek bile onlar oradadırlar. Tüm kontrol mantığı da zaten bu tür çeşitli fiziksel aktör sınıflarını PlayerController ile kodlayarak onlara etki etmek şeklindedir. Kalıtım ile elde ettiğimiz moddaki tüm aktör sınıfları da yine UTGame in aktörleri ile tıpatıp aynıdır. Yani onları da olduğu gibi miras almış durumdayız. Oyuncuya ait görsel her konudan sorumlu sınıf ise Pawn class ıdır. Yani oyuncu ve oyuncunun kullandığı görsel her unsur Pawn ın  programlanması ile ekranda elde edilir. Dolaıyısı ile Pawn,, Actor Class ların da görselliğinden sorumludur diyebiliriz. Bu süreçte oyuncu girdisi ilk önce PlayerController e yollanır, sonra o da karşılık gelen Pawn a etki eder. (şimdilik fazla detaya girmeden temel tanım olarak bilmemiz de yeterli.) Ve tabi aynı şekilde Pawn sınıfı da UTGame için programlanmış hali hazırdaki Pawn ın aynısı olarak karşımızdadır.

Özetle : Game Modunu UTGame den miras alarak, UTGame deki güç, sağlık, zırh, mermi durumu gibi her türlü durum bilgisini ekrana gösteren HUD sınıflarını, oyuncunun oyun dünyası ile etkileşimini / kontrolünü (Player Interaction) sağlayan PlayerController sınıflarını, ve bütün aktör sınıflarını ve onların görselliğini organize eden Pawn sınıflarını da tamamen aynı şekilde miras almış oluyoruz. Ve oyununuz çalıştığında aslında Epic in Pawn larını ve PlayerController lerini ve HUD larını kullanmaktayız. Oysa kendimizinkileri kullanıyor olmamız gerekirdi :) Epic in kodlarını kullanmaya devam edebilirsiniz ama bu şekilde sadece epic in UTDeathmatch ve ailesi oyun modları için tasarladığı sistematiği kullanabileceksiniz; yani epic in default sistemini. Şimdi oyun modumuza biraz daha farklılık ve fonksyonellik vermek için neler yapabileceğimizi göreceğiz; ve kendi Pawn clasımızı ve de PlayerController nesnemizi yazacağız.

Önceki makalemizden hatırlayın, temel sınıflardan kalıtım ile elde edilmiş GamePawn ve GamePlayerController sınıfları vardı. Normalde programlama yaparken “Game” ile başlayan hiçbir sınıfı miras alarak yeni bir sınıf türetmek istemeyebilirsiniz. Bunun sebebi bu durumda bir çok motor fonksyonalitelerini tekrar yazmak zorunda kalacağımızdır. Game* sınıfları yeni temiz bir sayfa baslatmanıza izin verir, fakat bunun da bedeli vardır : Zaman ve efor... Biz ilk örneğimizde fazla uğraşmadan unreale çok benzeyen (ne tesadüf :)) first person shooter tarzı bir ortamı çabucak ortaya çıkarmak istediğimiz için UT* den miras alarak oyun modumuzu türettik. Dolayısı ile Pawn ve Controller nesneleri için de GamePawn ve GamePlayerController sınıflarını değil,, UTGame in daha zenginleştirilmiş ve daha yine hali hazır sınıflarını çağırıp kullanacağız.

Pekala başlayalım,
Önce kendi controller sınıfımıza sahip olalım. UTGame in controller sınıfını çağırmak ve onu daha da geliştirmek için oyun modunu yaratırken yaptığımız gibi yapacağız; yani UTPlayerController sınıfını extend yöntemi ile miras alıcaz ve yeni bir sınıf adı vericez. Örneğimizde yeni sınıfın adına HTPlayerController ismini verdim. Siz başka isim de verebilirsiniz. "HT" ile başlaması burada aynı zamanda prefix yani dosya ön eki olacak. Yaratacağımız sınıfı Development\Src\UDKGame\Classes\HTPlayerController.uc yolunda tanımlayacağız. Bu arada önemli bir noktayı da belirtelim ki kodlama esnasında yeni sınıfımızı UDKGame.HTPlayerController  şeklinde ifade edeceğiz.


class HTPlayerController extends UTPlayerController
config(UDKGame);


bu şekilde yeni controller sınıfımızı miras alarak üretiyoruz. 2. satırdaki  "config(UDKGame);"  komutu dikkarinizi çekmiştir. Unral Script te, bir sınıf örneğine “config” komutu, engine in bizim için sınıfımızın bazı ayarlarını değiştirmemize olanak verecek bir config dosyası yaratmasını istiyorsak kullanılır. Bu, olayı manuel yaratmaktan çok daha pratiktir. Bu ayarlar genellikle son kullanıcının kendi ayarlarını seçebilmesine izin vermek içindir. Config komutunu kullanmak kod üzerinde bazı oynamalar (manipulations) yaparak default properties kısmında tanımlanan global değişkenleri config dosyaları üzerinden erişilebilir ve değiştirilebilir kılar.  Yani DefaultProperties kod bloğundaki tanımlamalara bir config dosyası üzerinden erişim yetkisi verirsiniz. Bu şekilde config dosyası üzerinden koda müdahale mümkün hale gelir. Config dosyaları ile derinlemesine çalışma konusunu başka makalelerde inceleyeceğiz. Şimdilik temel tanım olarak bilmemiz yeterli.

Config komutunu ilk satır yerine 2. satıra yazdık. Çok fazla farketmese de config komutunun sınıf tanımının (class dectaration) bir operatörü olduğunu düşünürsek onu ilk değilde ikinci satırda yazmak kodun okunaiblirliği açısından daha iyi olabilir.

class HTPlayerController extends UTPlayerController
config(UDKGame);


defaultproperties
{
}

Bu arada defaultproperties kod bloğunun ne olduğunu açıklamakta fayda var. Burası bir sınıf tanımının içindeki global değişkenlerin (variables) tanımlandığı kısımdır. Bildiğiniz gibi bir sınıf tanımı (class dectaration) iki temel kısımdan meydana gelir : değişkenlerin / nesnelerin tanımlandığı kısım, ve fonksyonlar. UDK da bu değişken / nesneler defaultproperties kod bloğu içinde yer alır. Yapılacak atamalar, kalıtım ile devralınan sınıfın özelliklerinin üzerine yazma veya yeni özellikler ekleme gibi sınıfın programlanabilir değişken / nesnelerinin tanımlandığı ve / veya değerlerinin değiştirildiği bloktur. C++ ın gelenekselleşmiş ve oturmuş syntax ı varken yeni yazım şekillerine ne gerek vardı diye düşünüyor insan. Ama oyun programlamanın apayrı bir dünya olduğunu düşünürsek varsın o kadarcık da farkı oluversin diyelim. Ve evet şimdilik defaultproperties bloğunu da boş bıraktık. Ve yeni fonksyonlar da eklemedik. Ama en azından controller sınıfını içinde devralma şablonunu çanlamış olduk. Yeni controller sınıfımızı HTPlayerController.uc olarak kaydedelim.




Evet geriye ne kaldı? Bir sürü şey, :) ama biz sadece UTGame den bir yeni mod yaratmayı temel yapı itibari ile inceliyoruz. O yüzden iki sınıftan (PlayerController ve Pawn) türetmeyi örnek olarak yapıyoruz. Yoksa örnekleri çok daha fazla artırabiliriz. O yüzden benzer bir işlemi arzu ederseniz Pawn sınıfı için de aynısını yapalım ve pekişsin. Bunun için UTGame in özelleşmiş kendi Pawn sınıfı olan UTPawn sınıfındantüretme yapacağız ve adına da HTPawn  diyeceğiz. Aşağıdaki kodları proje dizinimize HTPawn.uc adı ile ekleyeceğiz:

class HTPawn extends UTPawn
notplaceable;
defaultproperties
{
}

koda yakından bakarsanız yeni bir terim göze çarpıyor : “notplaceable”. Notplacable ifadesi unreal editörüne bu sınıfın ortamda (in world) konumlandırılmayacağını söyler. Bu satırı bu sınıf tanımına ekledik çünkü konumlandırma istemiyoruz. Pawn sınflarının sadece oyuncuyu temsil eden fiziksel özelliklerin saklaması ile ilgili olduğunu hatırlayın. Pawn lar ya kod ile yada factory sınıfı ile realtime oluşturulur. Bunların detaylarını da şimdilik başka makalelere ertelemek durumundayız. Ve şimdilik Pawn ların UTGame sınıfı tarafından otomatik olarak oluşturulduğunu ve detaylarına kod ile müdahale edebileceğimizi bilmemiz yeterli. Elbette yukarıda yine sadece sınıf devralma şablonunu verdim. İçine yeni hiçbir kod eklemedim. HTPawn.uc dosyamızı da kaydedelim.

Evet buraya kadar yaptıklarımız biri GameMod sınıfı (UTGame) olan toplam 3 sınıftan devralma yaptık. Ve Proje dizinimize kaydettik. Ama dediğim gibi içlerine yeni birşey eklemedik. Aslında bir nesne yaratılırken söylenecek çok şey var ama makaleyi fazla uzatmak istemiyorum. Ama kısaca şu ilaveyi  yapayım ki : Defaultproperties kod bloğu aslında sınıf ayarlarının verildiği bir config dosyası gibidir. Defaultproperties blogunda değişken / nesne atamaları yapılır dedik ama bu atamalar satır sonunda noktalıvirgül ile bitmez. Zaten her atama tek satırda yapıldığından gerek de yoktur. DefaultProperties kod bloğunun harikalığı, devralınan sınıfların da özelliklerin kolaylıkla değiştirilmesine izin vermesidir. Bir sınıfa ilk gözatmanızda özellikle başta defaultproperties bloğuna bakmanız önerilir. Zira burası o sınıfın ne yapabileceği üzerine size en iyi fikir verecek yerdir.


Devam edelim,
Şimdi geldik işin gerçekten zevkli bir noktasına. Kendi oyun modumuza bizim PlayerController ve Pawn sınıflarımızı kullanmasını söyleyelim (!). Olayı daha iyi anlamak için önce devraldığımız UTGame.UTGame sınıfının kodlarını incelersek, aşağıdaki bloğuna gözatar isek :

defaultproperties
{
HUDType=class'UTGame.UTHUD'
PlayerControllerClass=class'UTGame.UTPlayerController'
ConsolePlayerControllerClass=class'UTGame.UTConsolePlayerController'
DefaultPawnClass=class'UTPawn'
PlayerReplicationInfoClass=class'UTGame.UTPlayerReplicationInfo'
GameReplicationInfoClass=class'UTGame.UTGameReplicationInfo'
DeathMessageClass=class'UTDeathMessage'
BotClass=class'UTBot'
//And a lot more stuff in here....
}

şimdi şu satıra lütfen dikkat :
PlayerControllerClass=class'UTGame.UTPlayerController'

Bu satırda PlayerControllerClass adında bir nesne tanımı var. Devamında da ilgili PlayerController sınıfının tam yolu verilmiş. Class ibaresinin hemen ardından bir sınıf adının tam yolunun belirtilmesi, o sınıftan bir örnekleme (nesne) yaratılması anlamı taşıyor. İleride oyun içeriklerine istinaden kodlarla uğraştığımızda bu tanımlama satırları çok daha anlamlı hale gelecek.

PlayerController in kendi tanımladığımız sınıf olmasını istiyoruz; dolayısı ile yukarda tanımladığımız HTPlayerController sınıfının bu satırlarda olmasını sağlamalıyız. Sınıf tanımı yaparken class ibaresinden sonra tırnak açıp tam yolunu veriyorduk hatırlayın; dolayısı ile bizim yeni PlayerController nesnemizin tam yolu UDKGame.HTPlayerController şeklinde olacaktır.

Bazı kod tanımlama satırlarında DefaultPawnClass=class'UTPawn' gibi sadece sınıf adı belirtmek yeterli olasada, tam yol vermek her zaman daha iyidir. Bu hem kodun okunaklılıını artırır hem de ileride olası hataların önüne geçer. Ayrıca devraldığımız base (temel) sınıfın kodlarını okumak istersek bu şekilde yolunu asla şaşırmayız.

Şimdi hangi değişkenleri değiştirmemiz gerektiğini biliyoruz. Artık onları değiştirelim! Bu değişkenleri GameInfo sınıfımızın tanımının yapıldığı yerde belirtelim :


class UDKGame extends UTGame
config(UDKGame);


defaultproperties
{
DefaultPawnClass=class'UDKGame.HTPawn'
PlayerControllerClass=class'UDKGame.HTPlayerController'
}


artık GameInfo sınıfımız (UDKGame) bizim Pawn ve PlayerController nesnelerimizi kullanacak :)

şimdi kodlarımızı kaydedip derlersek ve çalıştırırsak; kendi oyun modumuz ve yeni nesnelerimiz devreye girecek ama görüntüde hiçbir değişiklik olmayacaktır. Çünkü yeni nesnelerimize farklı hiçbir fonksyonellik katılmamıştır. Sadece devralınarak farklı isimde yeni sınıflar elde edilmiştir ama teknik olarak önceki sınıflar ile tamamen aynıdırlar. (Bu makalede yeni kodlar yazmadan önce olayın alt yapısını anlamak adına game modu, ve epic classlarını devralma kavramını işlemek istedim. O yüzden varolan yapıya şimdilik yeni kodlar eklemedim.

Şimdi biraz da zurnanın son deliği olan ve ara ara bahsettiğim şu prefix kavramından biraz bahsedelim.

GameMode ve Map Prefix birlikteliği :
Oyun modumuzu yaptık, bazı sınıflarını da yeniden tanımladık. Peki .udk uzantılı harita (map) dosyalarını bu mod ile nasıl eşleştireceğiz? İşte bunu dosya ön eki (prefix) sağlamaktadır. Yani o haritanın hangi modda çalışacağını belirlemektedir. Yanlız burada olay dosya adını değişmekle bitmiyor. Belirttiğiniz prefix in kod tarafında da UDK da kayıtlı olması gerekiyor. Yani UDK nın o prefix i tanıması gerekiyor. Dolayısı ile eğer yükleyeceğimiz haritanın önünde UDK da tanımlı prefix lerden biri var ise default olarak oyun modu o olacaktır. örneğin : dosya adı DM- ile başlayan bir haritanın modu Deathmatch, CTF- Capture The Flag , VCTF- Vehicle Capture The Flag olacaktır. Bu modlar zaten hatırlayın UDK ile birlikte gelen hazır modlar idi, dolayısı ile bunların prefix leri de önceden tanımlanmıştır. Bu tanımlamalar GameMode temel sınıfının defaultproperties kısmında tanımlanır.  UTGame sınıfının defaultproperties kısmına bakarsak benzer tanımları görebiliriz (versiyona göre görmeyebiliriz de).

Prefix tanımlaması defaultproperties kısmında şu şekilde yapılır :

DefaultMapPrefixes(0)=(Prefix="DM",GameType="UTGame.UTDeathmatch")
DefaultMapPrefixes(1)=(Prefix="CTF",GameType="UTGameContent.UTCTFGame_Content")
DefaultMapPrefixes(2)=(Prefix="VCTF",GameType="UTGameContent.UTVehicleCTFGame_Content"

burada parantez içinde 0 - 1 - 2 - 3 diye artarak devam eden aslında bir tür dizi değişken (array) tanımlamasıdır. Örnekteki dizi toplam 3 elemanlı ve 3 farklı prefix tanımlanıyor. Ve deniyorki karşına bu isimlerde prefix e sahip bir harita çıktığında belirtilen GameModu nu kullan. Böylece her harita için farklı GameMod ları tanımlayabiliriz. Tabi burada belirttiğiniz GameMod larının kaynak kodlarının belirtilen yolda olması gerektiğini herhalde söylememe gerek yok :)

şimdi kendi GameInfo sınıfımızın (UDKGame) prefix ile ilgili tanımlarını overrite ederek bazı kodlar eklemeliyiz!

DefaultMapPrefixes(0)=(Prefix="HT",GameType="UDKGame.UDKGame")

bu satır ile kendi HT uzantılı prefix imizi tranımlıyoruz. Ve bu prefix in UDKGame GameMod unu kullanmasını söylüyoruz. Eğer istersek ve birden fazla game moduna sahipsek dizi indexini atırarak birden fazla prefix tanımlayabilirdik. kodumuzun son hali şöyle görünüyor olmalı :

class UDKGame extends UTGame
config(UDKGame);


defaultproperties
{
DefaultMapPrefixes(0)=(Prefix="HT",GameType="UDKGame.UDKGame")
DefaultPawnClass=class'UDKGame.HTPawn'
PlayerControllerClass=class'UDKGame.HTPlayerController'
}


Bir oyun moduna bir den fazla map bağlanabilir (prefix ile). Peki bir projede bir den fazla oyun modu (GameInfo tipinde sınıf tanımı) olamaz mı? Elbette olabilir. Aslına bakarsanız prefix kavramı UDK da biraz da bunun için vardır. Bizim burada geliştirdiğimiz projenin SRC altındaki folder ismi UDKGame olduğundan, bu folder altındaki UDKGame.uc dosyasında yapılan sınıf tanımı en üst düzey GameMod sınıfıdır. Ama bu dosya altına daha pek çok GameMod tanımı olabilir. Projedeki en üst düzey GameInfo sınıfı alt düzet GameInfo ları çağırabilir, ama bu diğer GameInfo lar diğer oyun modlarından doğrudan çağrılamazlar. İşte UDK da default olarak tanımlı olan UTGame folderinin içine bakarsak burada başka oyun mod (GameInfo) tanımlarının da olduğunu görebiliriz. Eee..? prefix ler karışırsa ne olacak?

Bunun için UTDeathmatch.uc dosyasının defaultproperties inin içinde prefix tanımının biraz farklı olarak belirtildiğini görürüz :

Acronym="DM"
MapPrefixes[0]="DM"

Bir prefix e bir den fazla oyun modu tanımı yapıldığı durumlar olabilir. Bu durumda UDK baskın olanı tercih edecektir. MapPrefixes” deyimi ile prefix eklenmesi oyun modunun ne olacağı konusunda artık tereddütsüz UTDeathmatch i belirtilen prefix haritasına geçişi sağlar. Bu satırlar ile birlikte UTDeathmatch oyun modunun prefix i DM olur. Ve malum bunun da anlamı adı DM- ile başlayan tüm haritalar aksi belirtilmediği sürece UTDeathmatch oyun modunda çalışacaktır.

Yukarıda oyun modumuz için zaten MapPrefixes ile tanımladık ama en azından UDKGame in bu acronym in HT olduğunu bilmesine de izin vermeliyiz.

class UDKGame extends UTGame
config(UDKGame);


defaultproperties
{
Acronym="HT"
DefaultMapPrefixes(0)=(Prefix="HT",GameType="UDKGame.UDKGame")
DefaultPawnClass=class'UDKGame.HTPawn'
PlayerControllerClass=class'UDKGame.HTPlayerController'
}

Acronym="HT" böylece bunu yapmış oluyoruz. Ardından gelen benzer kodla da işi baya bi sağlama alıp HT prefixine sahip tüm haritalarda her zaman bizim oyun modumuzun (UDKGame) çalışmasını garanti altına almış olduk.

Son olarak yaptığımız her bir sınıf tanımını doğru isimle .uc uzantısı ile proje dizinine kaydetmeyi lütfen unutmayın.

Evet biraz uzun bi makale oldu, sabrınız için teşekkür ederim :) Burada anlatılanların UDK nın yeni versiyonları ile birlikte (temelde değişmese bile) değişime uğrayabileceğini de lütfen unutmayın.

Yeni makalelerde tekrar görüşmek üzere, hoşçakalın...

Hiç yorum yok: