Gökhan ZER - Unreal Engine C++

26 Mart 2018 Pazartesi

UE4 - SPAWN

tekrar merhabalar,

Bugün UE4 programlamanın en ciddi ve de bir o kadar da heyecan verici konularından biri olan SPAWN konusunu inceleyeceğiz !



Önce Spawn nedir bir anlayalım....

sıklıkla kendimizi oyun esnasında level içine dinamik item ler eklemek isterken bulacağız; bunun UE 4 deki teknik anlamı SPAWN dir, daha doğrusu bir Actor ü Spawn etmektir.

Spawn etmek istediğimiz şey: diğer oyuncular, düşmanlar, alınabilir eşyalar (pick-ups), sağlık ikonları, silahlar, ödüller vs.vs. her şey olabilir !

aynı şekilde Actor ler dinamik olarak DESTROY da edilebilir. Bu, Blueprint kodu, veya C++ kodu ile sağlanabilir.

bildiğiniz gibi C++ da AActor tüm aktörlerin base class ıdır;
Örneğin: StaticMeshActor, CameraActor, PlayerStartActor vs…

dolayısı ile AActor tipinde olan, veya AActor ü base class alarak türetilmiş her sınıfın nesnesi Spawn edilebilir demektir.

Spawn işlemi progrmatik anlamda aslında AActor sınıfından (veya onu miras alan bir sınıftan) yeni bir instance yaratmak olarak tanımlanırsa, her şey daha basite indirgenmiş olur; ancak bu instance in normal instance dan tek farklı görsel bir obje olarak world içinde belirecek olmasıdır...!

bu işlem World.h header i içinde bulunan SpawnActor() fonksyonu kullanılarak yapılır.
Bu fonksyonun bir de jenerik, yani template versiyonu vardır ve her ikisinin de çok sayıda farklı paramerik versiyonları vardır. Gerçi template yapıdaki fonksyon da en nihayetinde normal SpawnActor() fonksyonunu kullanmaktadır; ancak template yapıdaki fonksyon kod yazımında sadelik ve kolaylık sağlamaktadır.


Spawn ön koşulları:
yeni bir actor yaratılmadan önce native kod un yaptığı bazı kontroller vardır ve aşağıdaki ön koşulların (preconditions) karşılanması gerekir:

  • level garbage-collected (silinme) aşamasında olmamalıdır.

  • spawn olacak class (sınıf) mutlaka belirtilmeli, NONE olmamalı, ve bu sınıf mutlaka bir Actor ve ya Actor den türemiş başka bir sınıf olmalı; ve Abstract da olmamalı. 

  • eğer frame rate düşük ise, yüksek derecede detaylandırılmış (high-detail) actor ler düşük detaya ayarlı levellerde spawn edilemez.

  • spawn edilecek Actor bStatic veya bNoDelete olmamalıdır.

  • world geometri si ile çarpışabilen (collide) Actor ler için, world geometry içine gömülmesin diye Spawn lokasyonu doğrulanır ve kısmen de olsa ayarlamalar yapılır; aksi durumda spawning iptal (abort) olur.

  • önkoşullar (preconditions) karşılanmış ise Actor yaratılırken, eğer belirtilmişse template Actor ün property lerinden yaratılır; eğer template belirtilmemiş ise class default object (CDO) sinin property lerinden yaratılır; ve bu spawn için önemli bir bilgidir...


Spawn esnasında motorda neler olur ?
yeni bir actor hayata geçerken BeginPlay içinde hem kendi birincil (primary) tick fonksyonunu, hem de component lerinin tick fonksyonlarını motora kaydeder (register).

Actor, PrimaryActorTick struct üyesi sayesinde, tick fonksyonunu özel bir grupta çalışacak şekilde de ayarlayabilir; veya tamamen disable edilebilir. Bu genellikle yapıcı (constructor) fonksyon içinde yapılır ki, BeginPlay çağrılmadan önce doğru şekilde ayarlanmış olduğuna emin olunabilinsin.

aşağıda bazı genel kullanımlar verelim:
PrimaryActorTick.bCanEverTick = true;
PrimaryActorTick.bTickEvenWhenPaused = true;
PrimaryActorTick.TickGroup = TG_PrePhysics;
Not:
benim anladığım o ki Actor lerin tick lemeler ile ilgili pek ok ince ayarı içlerinde bulunan bu PrimaryActorTick Struct yapısı ile halloluyor. Ve bu ayarlamaları actor un yapıcı fonksyonu içinde yapmamız (BeginPlay de değil) gerekiyor. 

bu tick ayarlamaları konusu Spawn işin içine girdiğinde daha da önem kazanıyor; oldukça zaten detaylı bir konu olan bu olaya burada çok yüzeysel değindik.

Spawn konusunda kodlamaya bodoslama girmek bence pek mümkün değil; öncesinde bazı kavramları en azından kabaca ne olduklarını teorik bile olsa bilmek gerekiyor (zaten derinlemesine girsek şu aşamada ne ben işin içinden çıkabilirim ne de...   :)

dilerseniz önce bu kavramlardan kısaca bahsedelim:  



Class Default Object (CDO) nedir?

Spawn konusunu okurken pek çok yerde karşıma bu kavram çıktı: “CDO”.

bildiğiniz gibi bir objeyi "new" ile instance ederken onun yapıcı (constructor) fonksyonu çağrılır; yapıcı fonksyon un görevi genellikle sınıfın değişkenlerini ilk kullanıma hazırlamak ve onlara ilk değer atamaktır. Yapıcı fonksyon un yaptığı bu işleme başlatma yani initialize diyoruz; ve işin sonunda döndürdüğü object e de “Class Default Object” yani CDO diyoruz.

tekrar edelim; yapıcı fonksyonun işi o class için bir CDO yu hazırlayıp yollamaktadır. Bazı kaynaklarda buna (CDO) “master copy” de denmektedir; bu master copy, aksi belirtilmediği sürece class ın gelecekteki her örneğinin (instance) de kopyası olacaktır. CreateNewObject veya SpawnActor ile obje her yaratıldığında da yine bu olacaktır.

eğer yapıcı fonksyon bloğu içinde hiçbir kod yoksa, ya da bu ilk değer atamalara dair hiçbir işlem (implemantasyon) yok ise, motor otomatikman tüm bu property alanlarına sıfır veya NULL atayacaktır, veya varsa bu item lerin kendi yapıcı fonksyonları otomatik olarak onlar ilk değer atayacaktır.

bildiğiniz gibi normal programlamada bu yapıcı fonksyon new operatörü kullanıldığında devreye girmekte ve class CDO sunu yollamakta idi; ancak UE4 de bir çok yerde nesne üretmek için (new yerine) daha başka arabirimler kullanıldığı gibi, CDO nun sadece nesne türetiminde değil, türetmeden dahi söz konusu olduğu, elde edildiği, edilmek istendiği durumlar çokça olabiliyor; bu yüzden he class ın CDO su dedikleri bu kelime UE4 ile birlikte bir kavram olarak önem kazanmış durumda;

biraz daha açalım...
motor başlatılırken UE4 reflection system i içinde yer alan he rbir class için CDO üretiliyor (höynk?) ve bu CDO yu tutan özel bir tip olan UClass lara depolanıyor;
bu yönü ile bakıldıuğında CDO, UClass içinde depolanan her sınıf için ana nesne kopyasıdır da diyebiliriz. (UClass tipi aşağıda anlatılacak) CDO nun olduğu yerlede kaşımıza hep bu UClass kavramı da çıkacak; ki aralarında kopmaz bir ilişki de var,

tekrar ederek biraz daha açalım:
  • motor başlatılırken her bir class için UClass objesi üretilir; yani her bir class için CDO yaratılır;

  • bu olurken de doğal olarak her birinin yapıcı fonksyonları çağrılır;

  • motor başta ürettiği CDO ları hafızanın bir yerinde UClass lar olarak depolar, 

  • ve yapıcı fonksyonlar (constructor) bir daha asla tetiklenmez;

  • bu yüzden yeni bir object yaratma anı/zamanı ile ilgili bir kodlama gereksinimi var ise bunun için belli event blokları kullanmalıyız; yapıcı fonksyonu ve new operatörünü değil.

  • benzer şekilde constructor ların içinde object leri veya gameplay kodlarını da kullanmamalıyız, çünkü yapıcı (constructor) fonksyon motorun başlatılması aşamasında tetiklendiğinden çoğu object o esnada henüz mevcut değildir; ayrıca level de muhtemelen henüz yüklenmemiştir.

  • her class ın CDO ine UClasss sınıfı içindeki UClass::GetDefaultObject fonksyonu ile kolayca erişebilirsiniz; bu bize aynı zamanda hız da kazandıracaktır (motor başlatılırken zaten oluşturulmuş olduğu için).

buradan şunu da anlıyoruz ki, UE4 de yapıcı fonksyonlar sadece sınıfın içindeki üyelerin ilk değer atamalarını yapmak için kullanılmalıdır; aksi durumda kaçınılmaz hatalar oluşabilir.

bu konu da tick konusu gibi yine dipsiz bir kuyu; biz burada sadece temel terminolojiyi verdik ve azcık da kulağa bilgi kar suyunu kaçırdık;

devam edelim,


UClass nedir?
"Runtime/CoreUObject/Public/UObject/Class.h"  yolu altında bulunan bu class ın görevi, bir class tipini bir object olarak ifade etmektir (ne??). Genel olarak runtime anında kod tarafında Class lar üzerinde yapılan işlemler gibi de düşünebiliriz.

StaticClass
UClass* ReturnType = UClass::StaticClass();
StaticClass() fonksyonu Class.h içinde bir static fonksyondur, bunun anlamı çağrılması için object instance a gerek yoktur;  

StaticClass() bağlı olduğu sınıfın (class) UClass ını döndürür;
birkaç farklı tipi vardır; her durumda görevi bir object in class tipi için UClass* tipi yollamaktır.

örneğin eğer AActor::StaticClass() çağrırsanız AActor class ının UClass ını döndürecektir;

veya UType::StaticClass() bize UType için bir UClass return edecektir;

ancak instance edilmiş bir obje için şu kodu yazarsak : Obj->GetClass() bu durumda Obj hangi tipte yaratılmış ise o tip için bir UClass pointer i return olacaktır; yani o class tan yaratılmış tüm objeler için GetClass() metodu bize aynı UClass pointerini döndürecektir; şayet bir base pointer den bile çağırıyor olsak bu böyledir.

bir kaç örnek verelim :
AMyActor* ActorPtr = NewObject(...);
UObject* ObjPtr = Actor;
UClass* MyActorClass = AMyActor::StaticClass();     // AMyActor
UClass* ObjectClass = UObject::StaticClass();  // UObject
UClass* ActorPtrClass = ActorPtr->GetClass();  // AMyActor
UClass* ObjPtrClass = ObjPtr->GetClass(); // AMyActor
UObject* ObjPtr = Actor;
UClass* MyActorClass = AMyActor::StaticClass();     // AMyActor
UClass* ObjectClass = UObject::StaticClass();  // UObject
UClass* ActorPtrClass = ActorPtr->GetClass();  // AMyActor
UClass* ObjPtrClass = ObjPtr->GetClass();        // AMyActor

yani, UType::StaticClass() sabittir; Obj->GetClass() dinamik olabiliyor iken; çünkü pointer başka bir objeye her zaman atanabilir; bu polymorphism ve virtual fonksyonların çalışma şeklidir; 


GetDefaultObject
UClass::GetDefaultObject
UObject* GetDefaultObject (bool bCreateIfNeeded)
özetle doğrudan CDO elde etmek istediğimiz durumlarda bu fonksyonu kullanabiliriz;
yine Class.h tipi içinde tanımlı olan bu fonksyon UClass header in içindeki bir template metoddur; görevi belirtilen class ın default object yani CDO ini elde etmek ve onu UObject tipinde yollamaktır; template yapısı içinde kullanıldığında ise bu UObject i de (templatteki) belirtilen tipe dönüştürerek yollandığını görüyoruz; açık kodu aşağıda:


içindeki default olarak true ya atanmış olan dolayısı ile kullanımı şart olmayan bool parametresinin anlamı eğer true ise şayet CDO null bile olsa sıfırdan yaratılacaktır.


GetDefault
UClass::GetDefault
aşağıdaki açık koduna baktığımızda az önceki GetDefaultObject i kullandığını görebiliriz; bu fonksyon da yine class default objesini (CDO) yollamaktadır; tek farklı yolladığı CDO pointeri aşağıdaki koddada görebileceğiniz üzere CONST tipindedir, yani bu değeri alıp değiştiremezsiniz; çoğu durumda Class Default Object lerinin değiştirilmemesi gerekir; bu metod bu yüzden değişmeyen (immutable) pointer yollar; eğer değişebilen objec e ihtiyacmızı var ise bunun yerine GetMutableDefault kullanmalıyız.



Bu fonksyonun Class.h içindeki açık tanımına bakarsak zaten yine GetDefaultObject in kullanıldığını ancak bunun bir değişmeyen const a atanarak güvenlik sağlandığını görebiliriz.

bunun dışında Class.h içerisinde bir class üzerinde yapılabilecek harika işlemler mevcuttur; ama şu an sadece bunları, yani sadece konumuz ile ilgili olanlarını inceledik.



World.h hakkında...
Runtime/Engine/Classes/Engine/World.h  yolu altında bulunan bu sınıf, içinde oynadıüımız tüm ortamı, dünyayı ifade eden UE4 ün ana sınıflarından biridir; ve temel işlevlerinden biri de Spawn işlemleridir.

World objesi UE4 deki en üst seviyeli (top level) object tir.
World,, içinde Actor ve Component lerin varolabildiği , render edilebildikleri bir haritayı (map) veya bir sandbox ı temsil eder.

bir World, levellerin akışı içinde (Level Streaming) volume ler ve çeşitli fonksyonlar vasıtası ile yüklenebilen ve hafızadan kaldırılabilen (load-unload) tek kalıcı level olabilir,, veyahutta leveller koleksiyonundan organize edilmiş bir birleşimi (World Composition) de temsil edebilir.

Bağımsız (standalone) bir oyunda, genellikle tekbir World mevcuttur; (mevcut bir dünya ve varılacak hedef noktalarının olduğu kesintisiz alan geçişleri (seamless area transitions) haricinde).

Editörün içinde çok sayıda World mevcuttur; editlediğin level olsun, PIE de oynadığın level olsun, ve hatta viewportta etkileşimli (interactive) render edilen her bir editör tool u bir World dür; ve falzası..



C++ ile Spawn işlemi:
SpawnActor fonksyonu World.h içinde tanımlıdır;

bir actor ü spawn etme UWorld::SpawnActor() fonksyonu ile yapılır; bu fonksyon belirtilen tipte yeni bir Actor yaratır ve pointer adresini döndürür; bu pointer AActor tipindedir;

AActor* UWorld::SpawnActor();

UWorld::SpawnActor() fonksyonu sadece AActor sınıfı veya hiyerarşisi içinde AActor den miras alınmış olan sınıfların instance ini yaratmak için kullanılabilir.

sanırım bu aşamada hatırlatmak doğru olacaktır ki,

“Actor ler doğrudan kendi transform bilgilerini (Location, Rotation, Scale) tutmaz; Actor transform dataları için (eğer var ise) kendi Root Component ini kullanılır; daha doğrusu RootComponent ine atanmış olan SceneComponent türevindeki bir component i kullanır.“.

bunu ileride yine hatırlatacağım;

şimdi Actor yaratımında kullanılacak olan parametreleri tek tek inceleyelim:



şimdi bu parametrelerin kısa açıklamasını yapalım:

Class (UClass)
spawn edilecek Actor ün sınıfını belirtilen, daha doğrusu yukarıda anlatıldığı gibi sınıf CDO sunu tutan UClass tipindeki parametre.

InName (FName)
isteğe bağlı kullanım.
spawn edilecek Actor e atanacak isim olarak bir FName parametresi. eğer belirtilmez ise spawn edilen actor otomatik olarak [Class]_[Number] şeklinde isimlendirilir.

Location (FVector)
isteğe bağlı kullanım.
spawn edilen Actor e başlangıç konumu sağlayacak olan bir FVector.

Rotation (FRotator)
isteğe bağlı kullanım.
spawn edilecek olan Actor e sağlanacak olan başlangıç rotasyon bilgisi FRotator tipinde.

Template (AAcor)
isteğe bağlı kullanım.
yeni bir Actor yaratırken bir şablon (template) olarak kullanılmak için belirtilen AAcor.
Not: Spawn edilen Actor bu Template Actor ün property değerleri kullanılarak başlatılır  (initilalize); eğer template belirtilmezse, UClass tipinde belirtilmiş olan class default object (CDO) spawn edilecek actor ü başlatmak için kullanılır.

bNoCollisionFail (bool)
isteğe bağlı kullanım.
Actor spawn olduğunda collision testinin uygulanıp uygulanmayacağını belirleyen bir bool değişkendir; eğer true olursa, actor spawn olurken template actor ün root componentinin collision ayarlarından bağımsız olarak hiçbir collision testine maruz kalmaz.

bRemoteOwned (bool)
isteğe bağlı kullanım.

Owner (AAcor)
isteğe bağlı kullanım.  Actor ün Spawn edildiği Actor ün adresidir; (AActor tipinde pointer.)

Instigator (APawn)
isteğe bağlı kullanım.
Spawn olan actor tarafından yapılan hasardan (damage) sorumlu olan APawn.

bNoFail (bool)
isteğe bağlı kullanım.
Belirli koşullar karşılanmadığı takdirde spawn ın başarısız olup olmayacağını belirleyen bir bool.
Eğer true ise, spawn başarısız (fail) olmaz.

OverrideLevel (ULevel)
isteğe bağlı kullanım.
ULevel içinde Actor ün spawn edileceği Level dir; başka bir deyişle Actor ün dışı dır.
NULL bırakılırsa Owner  parametresi ile belirtilen Actor ün Level i (onun dışı) kullanılır.
Eğer bu da belirtilmez ise kalıcı (persistent) level kullanılır.

bDeferConstruction (bool)
isteğe bağlı kullanım.
yapıcı fonksyon scriptinin koşturup koşturulmayacağını belirleyen bir bool değişkenidir. eğer true olursa Actor spawn olduğunda yapıcı fonksyon bloğu (construction script) koşturulmaz; sadece Blueprint den spawn olmuş olan actore uygulanabilir.

Dönüş Değeri (Return Value):
yukarıdaki açık fonksyon ile spawn gerçekleştirildiğinde dönen değer, spawn olmuş olan Actor ait AActor tipinde bir pointer dir.
Eğer aşağıda anlatılacak olan template tipinde bir ifade ile spwan yapılırsa, dönüş değeri  "template class”  parametresi ile belirtilen UClass parametresi ile aynı tipe dönüştürülmek zorundadır. Bu tip template, yani jeneric bile olsa yine ya AAcotr dür ya da AActor den miras alınarak yaratılmış bir  sınıftır.



FActorSpawnParameters
bu SpawnActor fonksyonuna ait opsiyonel parametrelerinin bir struct idir. Bu Gruplama kodda sadelik sağlar. Yani bazı parametreleri tek bir struct de toplayıp Spawn fonksyonuna tek tek yan yana yazmak yerine tek ifade ile vermek açısından kullanışlıdır; bu enum yapısı,  "Runtime/Engine/Classes/Engine/EngineTypes.h"  altında tanımlıdır.

yukardıda zaten bu opsiyonel parametreler açıklanmıştı, FActorSpawnParameters için kısaca tekrar üzerlerinden geçelim:

FName Name
spawn edilecek actore atanacak (assign) isim; şayet belirtilmez ise actor e [Class]_[Number] şeklinde rakamı artan forma isimler verilir.

AActor* Template
spawn edilecek yeni actor için kullanılacak template actor dür ve onun property değerleri ile başlatılır (initialize); bu parametre eğer NULL geçilir ise başlangıç değerleri için UClass tipinde belirtilmiş olan Class Default Objesi (CDO) kullanılır.

AActor* Owner
Actor ü Spawn eden Actor. (NULL bırakılabilir)

APawn* Instigator
Spawn olan Actor tarafından yapılan zarardan sorumlu olan APawn. (NULL bırakılaiblir.)

ULevel* OverrideLevel
Aktörü spawn edecek olan ULevel.
Actor bu ULevel in içinde spawn olacak.
NULL bırakılırsa Owner  parametresi ile belirtilen Actor ün Level i (onun dışı) kullanılır.
Eğer Owner NULL ise bu sefer de kalıcı (persistent) level kullanılır.

ESpawnActorCollisionHandlingMethod SpawnCollisionHandlingOverride
enum tipinde olan bu parametremiz, Spawn noktasında çarpışmalar ile ilgili seçenekleri içerir. Actor ün spawn olduğu yerde collision u bloklama gibi sorunların çözümleri için geçerli stretejilerin tanımlandığı bir enum yapısıdır;

bu enum itemlarını kısaca inceleyelim :

- Undefined
override etme, actor ün ayarlarını kullan demektir. Daha doğrusu default ayarlara (default settings) geri dön demektir.

- AlwaysSpawn
collision a hiç aldırış etmeden Actor istenen lokasyonda spawn edilir.

- AdjustIfPossibleButAlwaysSpawn
Actor (shape component e bağlı olarak) en yakınındaki collision çakışması olmayan lokasyonu bulmaya çalışacaktır; ancak herhangi bir tane bulamazsa bile her zaman spawn edecektir.

- AdjustIfPossibleButDontSpawnIfColliding
Actor (shape component lere bağlı olarak) en yakınındaki collision çakışması olmayan lokasyonu bulmaya çalışacaktır; ancak bu defa uygun bir yer bulmadıkça spawn olmayacaktır.

- DontSpawnIfColliding
isminden de anlaşılacağı üzere şayet çakışma varsa Spawn mutlaka fail olacaktır.





Bazı örnek kullanımlar / C++ 
1-

- burada ilk parametre spawn edilecek olan Class tipidir. Bu tip UClass tipinde bir pointer olarak belirtilmeli.

- ikinci ve üçüncü parametre olan  Location ve Rotation parametreleri dikkat ederseniz  " const * " ibaresi ile tanımlanmış; ve ilk default değerleri de şimdiden NULL verilmiş; bu onları girmek zorunda olmadığımız anlamına gelir;

' * ' sembolü bu parametrenin bir pointer olduğu, const ibaresi ile kullanıldığında bunun bir const pointer olduğu, yani bir formal parametre olarak geçirdiğin fonksyon onun değerini değiştiremez, yani ona başka Location / Rotation değerleri atayamaz demektir; atanan değerler korunacaktır; yani Const ifadesi burada pointer in içeriğinin değişmeyeceğini bildirir. (Çünkü const olan pointer dir; pointer in işaret ettiği bellek alanı değil.)

- dördüncü parametre, spawn işleminin bir çok parametresini bünyesinde topluca barındıran FActorSpawnParameters object ini alıyor; bu bize biraz kod görünümünde sadelik sağlıyor;

ancak şu hemen önündeki  ' ' ne demek derseniz, bu referans parametresi anlamına gelir; bu sayede fonksiyona argüman yerine o argümanın doğrudan kendi adresini yolluyoruz; tek bir bellek adresi; özellikle büyük veri tiplerinde ile çalışırken adres aktarımları bize çok hız kazandırır.  FActorSpawnParameters  bir class değil bir struct olduğundan adresi aktarılacaksa bu şekilde kullanılıyor.

& operatörü ile adres aktarılmasındaki olay arada hiçbir aracının olmaması; yani doğrudan doğruya fonksyona kendi adresini yani bir anlamda doğrudan kendisini aktarması anlamına geliyor; bir başka kopyası da fonksyonda oluşmuyor; bu kullanımın tehlikesi artık aynı bellek bölgesinin (field) fonksyonun onu karşıladığı başka isimlerle de ifade edilmesi ve biri onu lokal bir blok içinde değiştirse dahi blok dışında kalan farklı isimlerdeki değişkenlerin de değerinin değişmesi; çünkü hepsinin adresi aynı; kullanırken dikkatli olmak gerek; güvenlik maksadı ile önüne  "const"  koyarsak adres aktarıp değişmeyeceğini de garanti altına almış oluruz; fonksyona dikkat ederseniz kullanımı da aynen bu şekilde olmuş; o artık fonksyon içinde readonly durumdadır.

Özetle tekrar: İlkinde pointer const, ikincisinde kendisi const durumdadır. Burada normal struct te erişimin olduğu gibi “ . ” operatörü ile üyelere erişilebilir.

bir de önemli olan şu nokta var:

const FActorSpawnParameters& SpawnParameters

yerine,

' & ' olmadan, doğrudan:

const FActorSpawnParameters SpawnParameters

ancak bu sefer parametre fonksiyona verilirken bu obje kopyalanacaktı; dolayısı ile memory ve işlemciyi gereksiz kullanmamak için başına & koyuyor. Her ne kadar bu kullanım OOP ilkeleri ve yapısal programlama ile çok da bağdaşmasa da dediğimiz gibi büyük objeler de performans artışı için oldukça hız kazandırıcı olabilir.

const u da kullanmayabiliriz; o sadece readonly güvenliği sağlamak içindi.



2-
aşağı yuları ilk fonksyon ile aynı olan bu spawn fonksyonunun ilkinden tek farkı Locaiton ve Rotation u ayrı ayrı vermek yerine tek bir FTransform nesnesinde toplu olarak vermesidir. Dikkat ederseniz FTransform da yine cosnt olarak verilmiş. Yani Spawn fonksyonu bu değerleri değiştiremeyecektir.


3 -


anladığım o ki bu fonksyon tam olarak istenen yerde spawn işlemini gerçekleştiriyor; öyleki root component in transfom bilgilerini ezerek (override ederek) parametrede verilen transfom bölgesinde Spawn gerçekleştiriyor.

içeriğine baktığımda:



sonuç olarak yukarıdaki diğer iki fonksyon gibi return ifadesinde aynı fonksyona yönlendirdiğini, ama poarametrelerini ayarlarken,

a- önce SpawnParameters deki Template Actor varmı diye bakıyor, varsa bir Actor e onu alıyor;

b- eğer Template yok ise bu defa ilk parametre olan Class parameresinden Class Default Object i CDO elde ediyor.

bir Actor ü kesin olarak elde ettikten sonra, onun RootComponentini elde etmeye çalışıyo,



template NULL değil ise RootComponentini TemplateRootComponent e at demiş yukarıda kendileri; ardından fonksyon parametresi ile gelen AbsoluteTransform değerini bir şekilde kullanmış.


Not: SpawnActor fonksyonunun ilk parametresi olan UClass da belitrilen tip ne olursa olsun dönüş tip isanırım AActor oluyor.




Template tipinde Spawn fonksyonları:

bunlar da aslında yine nihayetinde SpawnActor fonksyonunu kullanıyor, ama parametrelerin daha küçük bir alt kümesine ihityaç duyarak kodu sadeleştirmeleri ve dönüş Actor alt tipinin belirtilmesine ihtiyaç duyulan durumlar için oldukça kullanışlıdırlar.

Şimdi gelin Template Spawn fonksyonlarını inceleyelim:

1- Spawn T Instance, Return T Pointer
SpawnActor  (   AActor* Owner=NULL,
   APawn* Instigator=NULL,
   bool bNoCollisionFail=false )
operasyonunu gerçekleştiren Actor ün (Owner) RootComponenti ile aynı Location, ve Rotation da Spawn yaparak template (T) de belirtilen tip ile aynı tipte Instance oluşturarak onun pointer ini döndürür.

bu fonksyon üç parametre alır:

a- Owning Actor: az önce sözünü ettiğimiz operasyonu gerçekleştiren Actor dür.

b- instigating Pawn
c- sonraki parametre ise, bNoCollisionFail, yani, spawn olan Actor ün ortama zarar vermesi veya ortamda zaten varolan başka bir Actor ile çarpışıyor durumda olması durumunda spawn işleminin iptal edilip edilmemesi gerekliliğini belirten bir bool flag.
bu Template in açık tanımı ise şöyledir:
/** Spawns and returns class T, respects default rotation and translation of root component. */
template< class T >
T* SpawnActor
(
    AActor* Owner=NULL,
    APawn* Instigator=NULL,
    bool bNoCollisionFail=false
)
{
    return (T*)(GetWorld()->SpawnActor(T::StaticClass(), NAME_None, NULL, NULL, NULL, bNoCollisionFail, false, Owner, Instigator));
}

kullanımı:

MyHUD = SpawnActor<AHUD>(this, Instigator);
özetle bu kullanımda de belirtilen tip mutlaka dönen tip oluyor (UClass), ilk parametre ile belirtilen şey de Owner oluyor, yani onun sadece transform bilgileri kullanılıyor. 



2 - Spawn T Instance with Transform, Return T Pointer

SpawnActor  (
   FVector const&  Location,
   FRotator const&  Rotation,
   AActor* Owner=NULL,
   APawn* Instigator=NULL,
   bool bNoCollisionFail=false )

bu fonksyon template i, belirtilen Lokasyon ve belirtilen rotation da bir template class T instance i spawn eder, ve template class ile aynı tipte instance edilmiş bir pointer döndürür, yani T*.
parametrelerine baktığımızda :

a- Lokayon ve rotasyon,
b- Owning Actor, 
c- Instigating Pawn, 
d- sonraki parametre ise,bNoCollisionFail, yani, spawn olan Actor ün ortama zarar vermesi veya ortamda zaten varolan başka bir Actor ile çarpışıyor durumda olması durumunda spawn işleminin iptal edilip edilmemesi gerekliliğini belirten bir bool flag.
/** Spawns and returns class T, forcibly sets world position. */
template< class T >
T* SpawnActor
(
    FVector const& Location,
    FRotator const& Rotation,
    AActor* Owner=NULL,
    APawn* Instigator=NULL,
    bool bNoCollisionFail=false
)
{
    return (T*)(GetWorld()->SpawnActor(T::StaticClass(), NAME_None, &Location, &Rotation, NULL, bNoCollisionFail, false, Owner, Instigator));
}

bu fonksyon ilave olarak bir UCLASS parametresi almıyor, de belirtilen tip zaten içeride UClass parametresi olarak veriliyor, aynı tipte de döndürülüyor.

yine açık tanımına bakarsak, template i işin içine hiç sokmamış , doğrudan doğruya açık tanımında NULL atıp geçmiş;




ilaveten lokasyon ve rotsyon parametresi de alıyor; dolayısı ile burada akla şöyle bir önemli soru geliyor: transform bilgileri zaten veriliyorsa, transformun zaten kendisinden alındığı Owner ne olacak ? ona gerek yokki ?

sorunun cevabı kullanımında gizli:

bakarsanız kullanımına 3. parametreye NULL atamış. Bu tam da Owner parametresi. Kendisine NULL atamış kullanırken çünkü ona gerek yok.

kullanımı:

Controller = SpawnActor<AController>(GetLocation(), GetRotation(), NULL, Instigator, true);
peki bu NULL neden Owner a karşılık geliyor derseniz, yukarıdaki açık tanıma dikkat edin; lokasyon ve rotasyon un default değer ataması yok, yani bu değerler fonksyonun formal parametrelerinde mutlaka belirtilmek zorunda; sonrakilerde ise default değer atamnası var, yani bunları belirtmeseniz de olur; ama yinede NULL olarak da olsa belirtilmiş çünkü kullanımda Instigator u özellikle kendisi vermek istemiş, o yüzden de sıralamayı bozmamak için işe yaramayacak olsa da Owner a NULL verip geçmiş. Yani bu kullanım aslında:
Controller = SpawnActor(GetLocation(), GetRotation());
şeklinde de olabilirdi.

benzer şekilde template spawn ifadesini kısaltmak için ortalıklardan gizlenmiş olan bRemoteOwned değişkeni içinde otomaitkman false çakılmış; ancak sonrasında gelen Owner ve Instigator parametreleri aktif olarak kullanılmış. 


Not: anladığım o ki, location ve rotation, kısaca transfom bilgilerinin açık olarak verildiği durumlarda templateparametresine gerek kalmadığı; yani template parametresinin daha çok bu transfom bilgileri için kullanıldığını düşünmeye başladım. 



3 - Spawn Class Instance, Return T Pointer

SpawnActor(   UClass* Class,   AActor* Owner=NULL,   APawn* Instigator=NULL,   bool bNoCollisionFail=false)
Bu template fonksyon , belirtilen Sınıfın bir örneğini (instance), spawn işlemini gerçekleştiren Aktör ün Root Component i ile aynı location ve aynı rotation da spawn eder ve template class olarak belirtilen tipe dönüştürülmüş olarak instance a ait bir pointer döndürür.
/** Spawns given class and returns class T pointer, respects default rotation and translation of root component. */
template< class T >
T* SpawnActor
(
    UClass* Class,
    AActor* Owner=NULL,
    APawn* Instigator=NULL,
    bool bNoCollisionFail=false
)
{
    return (Class != NULL) ? Cast<T>(GetWorld()->SpawnActor(Class, NAME_None, NULL, NULL, NULL, bNoCollisionFail, false, Owner, Instigator)) : NULL;
}
bu durum da, 
  • bu fonksyonun ilk parametresi olan UClass ın tipinin, template class in child i olması gerekir. Yani aynı tipte veya ondan türemiş bir tip olması gerekir.
  • ikinci parametre ise yine Owner, yani işlemi gerçekleştiren Owning Actor,
  • üçüncğü parametre ise hasar işlerinden sorumlu olan Instigator, yani Instigating Pawn,
  • sonraki parametre ise,bNoCollisionFail, yani, spawn olan Actor ün ortama zarar vermesi veya ortamda zaten varolan başka bir Actor ile çarpışıyor durumda olması durumunda spawn işleminin iptal edilip edilmemesi gerekliliğini belirten bir bool flag. bu da zaten default olarak false a ayarlandığı için belitilmez ise otomatikman false giriyor fonksyona.

SpawnActor ün orjinal haline bakarak yine daha iyi bi yorum getirelim:

AActor* UWorld::SpawnActor
(
    UClass* Class, FName InName,
    FVector const* Location,
    FRotator const* Rotation,
    AActor* Template,
    bool bNoCollisionFail,
    bool bRemoteOwned,
    AActor* Owner,
    APawn* Instigator,
    bool bNoFail,
    ULevel* OverrideLevel,
    bool bDeferConstruction
)
templatein açık tanımına bakarak,


lokasyon , rotasyon ve template parametrelerini her halükarda NULL geçtiğini görebiliriz;

bNoCollisionFail parametresini de en sonda kullandığı için ve ona da default değer olarak false atadığı için onu da hiç belirtmesek oluyor, yani UClass dan sonra doğrudan Owner ve Instigator belirtilebiliyor, yani şu şekilde:
kullanımı:

MyHUD = SpawnActor<AHUD>(NewHUDClass, this, Instigator);
 Owner olarak this yazmak bence şık kullanımlarda nbir tanesi; bu o an üzeirnde olduğunuz söz konusu class ı berlitmenin çok kestirme ve şık bir yolu.



4- Spawn Class Instance with Transform, Return T Pointer

SpawnActor(   UClass* Class,   FVector const& Location,   FRotator const& Rotation,   AActor* Owner=NULL,   APawn* Instigator=NULL,   bool bNoCollisionFail=false)

bu template fonksyonu, belirtilen lokasyon ve belirtilen rotasyonda belirtilen UClass tipini instance spawn eder (yaratır), ve tipi ile yaratılan bu instance in pointer ini yollar.

bu durum da, bu şablonun ilk parametresi olan Class ın tipinin, template class nin child i olması gerekir. Yani aynı tipte veya ondan türemiş bir tip olması gerekir.

ikinci ve üçüncü parametre olan lokasyon ve rotation parametrelerine ek olarak,
dördüncü ve beşinci yine owning Actor, instigating Pawn parametreleridir;

sonraki parametre ise bNoCollisionFail, yani spawn olan Actor ün ortama zarar vermesi veya ortamda zaten varolan başka bir Actor ile çarpışıyor durumda olması durumunda spawn işleminin iptal edilip edilmemesi gerekliliğini belirten bir bool flag. bu da zaten default olarak false a ayarlandığı için belitilmez ise otomatikman false giriyor fonksyona.

açık tanımı şöyle :

/** Spawns given class and returns class T pointer, forcibly sets world position. */
template< class T >
T* SpawnActor
(
    UClass* Class,
    FVector const& Location,
    FRotator const& Rotation,
    AActor* Owner=NULL,
    APawn* Instigator=NULL,
    bool bNoCollisionFail=false
)
{
    return (Class != NULL) ? Cast<T>(GetWorld()->SpawnActor(Class, NAME_None, &Location, &Rotation, NULL, bNoCollisionFail, false, Owner, Instigator)) : NULL;
}
kullanımı:
APawn* ResultPawn = SpawnActor<APawn>(DefaultPawnClass, StartLocation, StartRotation, NULL, Instigator);
kullnımında NULL olarak atanan parametrenin yine açık tanımda Template e karşılık geldiği görülür. Yani template bu fonksyonda hiçbir zaman yer alamaz;



ancak lokasyon ve rotasyonun olmasına rağmen transform belirtilebilir. Yukarıdaki kullanımda Owner i de NULL ile geçerek özellikle Instigator u belirtmek istemiş.




Visual Studio da tanımlı template SpawnActor ler :

/** Templated version of SpawnActor that allows you to 
 specify a class type via the template type */
 
template< class T >
T* SpawnActor(
 const FActorSpawnParameters& SpawnParameters = FActorSpawnParameters()
)
{
 return CastChecked<T>
  (
   SpawnActor(T::StaticClass(), NULL,NULL,SpawnParameters),
   ECastCheckedType::NullAllowed
  );
}


/** Templated version of SpawnActor that allows you to specify 
 location and rotation in addition to class type via the template type */
 
template< class T >
T* SpawnActor( 
 FVector const& Location, 
 FRotator const& Rotation, const 
 FActorSpawnParameters& SpawnParameters = FActorSpawnParameters())
{
 return CastChecked<T>(
  SpawnActor(
   T::StaticClass(), 
   &Location, 
   &Rotation, 
   SpawnParameters),
   ECastCheckedType::NullAllowed
  );
}


/** Templated version of SpawnActor that allows you to 
 specify the class type via parameter while the return type 
 is a parent class of that type */
 
template< class T >
T* SpawnActor( 
 UClass* Class, 
 const FActorSpawnParameters& SpawnParameters = FActorSpawnParameters())
{
 return CastChecked<T>(
  SpawnActor(Class, NULL, NULL, SpawnParameters),
  ECastCheckedType::NullAllowed
  );
}


 /**
  *  Templated version of SpawnActor that allows you to 
  specify the rotation and location   in addition class type 
  via parameter while the return type is a parent class of that type
  */
 
 template< class T >
 T* SpawnActor( 
  UClass* Class, 
  FVector const& Location, 
  FRotator const& Rotation, 
  const FActorSpawnParameters& SpawnParameters = FActorSpawnParameters())
 {
  return CastChecked<T>(
   SpawnActor(Class, &Location, &Rotation, SpawnParameters),
   ECastCheckedType::NullAllowed
   );
 }


 /**
  *  Templated version of SpawnActor that allows you to specify 
  whole Transform class type via parameter while the return type 
  is a parent class of that type
  */
 
 template< class T >
 T* SpawnActor( 
  UClass* Class, 
  FTransform const& Transform,
  const FActorSpawnParameters& SpawnParameters = FActorSpawnParameters())
 {
  return CastChecked<T>(
   SpawnActor(Class, &Transform, SpawnParameters), 
   ECastCheckedType::NullAllowed
   );
 }


/** Templated version of SpawnActorAbsolute that allows you to
 specify absolute location and rotation in addition to class type 
 via the template type */
 
template< class T >
T* SpawnActorAbsolute( 
 FVector const& AbsoluteLocation, 
 FRotator const& AbsoluteRotation, 
 const FActorSpawnParameters& SpawnParameters = FActorSpawnParameters()
)
{
 return CastChecked<T>(
  SpawnActorAbsolute(T::StaticClass(), 
  FTransform(AbsoluteRotation, AbsoluteLocation), SpawnParameters), 
  ECastCheckedType::NullAllowed
  );
}


 /**
  *  Templated version of SpawnActorAbsolute that allows you to specify 
  whole absolute Transform class type via parameter while the return type 
  is a parent class of that type  */
 
 template< class T >
 T* SpawnActorAbsolute( 
  UClass* Class, 
  FTransform const& Transform,
  const FActorSpawnParameters& SpawnParameters = FActorSpawnParameters()
 )
 {
  return CastChecked<T>(
   SpawnActorAbsolute(Class, Transform, SpawnParameters), 
   ECastCheckedType::NullAllowed
   );
 }


 /**
  * Spawns given class and returns class T pointer, forcibly sets world
    transform (note this allows scale as well).  
    WILL NOT run Construction Script of Blueprints to give caller 
    an opportunity to set parameters beforehand.  Caller is responsible 
    for invoking construction manually by calling 
    UGameplayStatics::FinishSpawningActor (see AActor::OnConstruction).
  */
 
 template< class T >
 T* SpawnActorDeferred(
  UClass* Class,
  FTransform const& Transform,
  AActor* Owner = nullptr,
  APawn* Instigator = nullptr,
  ESpawnActorCollisionHandlingMethod CollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::Undefined
  )
 {
  if (Owner)
  {
   check(this == Owner->GetWorld());
  }
  FActorSpawnParameters SpawnInfo;
  SpawnInfo.SpawnCollisionHandlingOverride = CollisionHandlingOverride;
  SpawnInfo.Owner = Owner;
  SpawnInfo.Instigator = Instigator;
  SpawnInfo.bDeferConstruction = true;
  return (Class != nullptr) ? Cast<T>(SpawnActor(Class, &Transform, SpawnInfo)) : nullptr;
 }

bunların dışında bir de Absolute ve Deferred spawn konuları var ki bunları daha başka bir yazıda inceleyeceğiz. Yukarıda ön tanımlarını verdik.

Arkadaşlar bir başka uzun bir yazı oldu yine; pratiğe geçmek için ön adımlar diyelim. İşin görsel zevkli kısımları sonraya kalsın bir kez daha :)


bir sonraki yazıda görüşmek üzere hoşçakalın.