Özet
Singleton, yazılım geliştirmede sıklıkla kullanılan bir tasarım yaklaşımıdır. Amaç, bir nesnenin yalnızca bir örneğinin oluşturulabilmesini zorlamaktır. Singleton yapısı ile tanımlanan bir nesnenin yalnızca bir örneği oluşturulabilir. Özellikle Factory denilen nesne üretim sınıfları, Application yada Program olarak isimlendirilen ve genellikle yazılımın kendisini temsil eden yapılar, bu tasarım yaklaşımı ile oluşturulurlar[1].
UE4, Game ve Editor uygulamarı için böyle bir tasarım içeriyor. Bu yazının ilgilendiği nokta ise, Epic Games'in bu yapıyı kullanıcılar tarafından kullanılabilir hale getirmesi. Bu konuyu, hazırladığı yazısıyla anlaşılması kolay hale getiren Rama'ya da teşekkür etmek gerekir[2].
Bu yazıda UE4 projesinde Game Singleton yapısının nasıl tanımlanacağı anlatılmıştır. Tanımlanan yapı sayesinde tasarım zamanında (design-time) kolaylıkla editör üzerinden tüm sınıf(class) ve içerikler(asset) bir veritabanı gibi Singleton üzerinde tutulabilir. Bu yapıya da çalışma zamanında(runtime) hem C++ tarafında hem de Blueprint tarafında rahatlıkla erişilebilir.
C++ Sınıfının Oluşturulması
Projeye Object temelli yeni bir C++ Class eklenir ve istenilen Propertyler oluşturulur. Bu örnekte class ismi Singleton seçilmiştir.
Singleton.h
Singleton.cpp
Blueprint Sınıfının Oluşturulması
Kod projesi derlendikten sonra projeye C++ tarafında oluşturulan "Singleton"dan kalıtım almış yeni bir Blueprint Class eklenir ve istenildiği gibi adlandırılır (şekil 1.a). Ardından Project Settings/Engine/General Settings altında Game Singleton Class değerine; oluşturulan bu Blueprint Class atanır (bu değer arama bölümüne yazmadan gözükmez). Böylece UE4 yeni oluşturulan Blueprint Classı Singleton olarak tanır (şekil 1.b).
Ekli dosyayı görüntüle 1
Ekli dosyayı görüntüle 2
Şekil 1
Singleton Blueprint Üzerinde Değerlerin Atanması
Singleton olarak oluşturulan Blueprint Class açılır ve Class Defaults bölümünde, C++ tarafında hazırlanmış Property değerleri girilir (şekil 2).
Ekli dosyayı görüntüle 3
Şekil 2
C++ Üzerinde Kullanım
Singleton'a erişim bu ana kadar sadece C++ ile mümkündür. Aşağıdaki örnekte SingletonBP'ye atanmış vektörler, PlayerController üzerinde, DrawDebugSphere kullanılarak çizilmiştir (şekil 3).
MyPlayerController.cpp (yalnızca bir bölümü)
Ekli dosyayı görüntüle 4
Şekil 3
Blueprint Üzerinde Kullanım
Malesef Singleton örneğine Blueprint üzerinden erişmenin doğrudan bir yolu yoktur. Bu yüzden UBlueprintFunctionLibrary yardımı ile Blueprint üzerinden erişim için bir fonksiyon daha gerekmektedir. Kısaca yeni bir BlueprintFunctionLibrary tabanlı C++ sınıfı projeye eklenmeli ve bu sınıfa aşağıdaki fonksiyon eklenmelidir.
MyBlueprintFunctionLibrary.h
MyBlueprintFunctionLibrary.cpp
Bu aşamadan sonra Singleton örneği, artık herhangi bir Blueprint üzerinden de çağrılabilecektir.
Dikkat Edilmesi Gereken Noktalar
Singleton üzerinde değer yada referans kullanıldığında değerlerini runtime değiştirmemek gerekir. Bu durum, doğru tasarım yapmaya aykırıdır. Belirtmekte yarar vardır ki değişken ve referans kullanıldığında; çalışma sırasında değiştirilirlerse, öngörülemeyecek sorunlara yol açabilir, tasarımı kolaylaştırmak yerine zorlaştırır ve yazılım geliştirme yaklaşımlarına da aykırıdır. Değişkenler değişmez(constant) olması durumunda rahatlıkla kullanılabilir ama ne zaman ilkleneceği(ilk değerini alacağı) doğru seçilmelidir. Örneğin Blueprint üzerinde oluşturuldukları sırada verilen varsayılan(default) değerlerinin korunması tavsiye edilir.
Referanslar
[1] Wikipedia, Singleton pattern
[2] Rama, Global Data Access, Data Storage Class Accessible From Any CPP or BP Class During Runtime
Singleton, yazılım geliştirmede sıklıkla kullanılan bir tasarım yaklaşımıdır. Amaç, bir nesnenin yalnızca bir örneğinin oluşturulabilmesini zorlamaktır. Singleton yapısı ile tanımlanan bir nesnenin yalnızca bir örneği oluşturulabilir. Özellikle Factory denilen nesne üretim sınıfları, Application yada Program olarak isimlendirilen ve genellikle yazılımın kendisini temsil eden yapılar, bu tasarım yaklaşımı ile oluşturulurlar[1].
UE4, Game ve Editor uygulamarı için böyle bir tasarım içeriyor. Bu yazının ilgilendiği nokta ise, Epic Games'in bu yapıyı kullanıcılar tarafından kullanılabilir hale getirmesi. Bu konuyu, hazırladığı yazısıyla anlaşılması kolay hale getiren Rama'ya da teşekkür etmek gerekir[2].
Bu yazıda UE4 projesinde Game Singleton yapısının nasıl tanımlanacağı anlatılmıştır. Tanımlanan yapı sayesinde tasarım zamanında (design-time) kolaylıkla editör üzerinden tüm sınıf(class) ve içerikler(asset) bir veritabanı gibi Singleton üzerinde tutulabilir. Bu yapıya da çalışma zamanında(runtime) hem C++ tarafında hem de Blueprint tarafında rahatlıkla erişilebilir.
C++ Sınıfının Oluşturulması
Projeye Object temelli yeni bir C++ Class eklenir ve istenilen Propertyler oluşturulur. Bu örnekte class ismi Singleton seçilmiştir.
Singleton.h
Kod:
#pragma once
#include "Object.h"
#include "Singleton.generated.h"
/**
*
*/
UCLASS(BlueprintType, Blueprintable)
class UET_API USingleton : public UObject
{
GENERATED_BODY()
public:
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = Singleton) TArray<TSubclassOf<AActor>> ActorClasses;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = Singleton) TArray<FVector> Locations;
};
Singleton.cpp
Kod:
#include "UET.h"
#include "Singleton.h"
Blueprint Sınıfının Oluşturulması
Kod projesi derlendikten sonra projeye C++ tarafında oluşturulan "Singleton"dan kalıtım almış yeni bir Blueprint Class eklenir ve istenildiği gibi adlandırılır (şekil 1.a). Ardından Project Settings/Engine/General Settings altında Game Singleton Class değerine; oluşturulan bu Blueprint Class atanır (bu değer arama bölümüne yazmadan gözükmez). Böylece UE4 yeni oluşturulan Blueprint Classı Singleton olarak tanır (şekil 1.b).
Ekli dosyayı görüntüle 1
Ekli dosyayı görüntüle 2
Şekil 1
Singleton Blueprint Üzerinde Değerlerin Atanması
Singleton olarak oluşturulan Blueprint Class açılır ve Class Defaults bölümünde, C++ tarafında hazırlanmış Property değerleri girilir (şekil 2).
Ekli dosyayı görüntüle 3
Şekil 2
C++ Üzerinde Kullanım
Singleton'a erişim bu ana kadar sadece C++ ile mümkündür. Aşağıdaki örnekte SingletonBP'ye atanmış vektörler, PlayerController üzerinde, DrawDebugSphere kullanılarak çizilmiştir (şekil 3).
MyPlayerController.cpp (yalnızca bir bölümü)
Kod:
#include "Singleton.h"
void AMyPlayerController::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
USingleton* Singleton = Cast<USingleton>(GEngine->GameSingleton);
if (Singleton != nullptr)
{
for (int32 i = 0; i < Singleton->Locations.Num(); i++)
{
DrawDebugSphere(GetWorld(), Singleton->Locations[i], 50, 32, FColor::Yellow);
GEngine->AddOnScreenDebugMessage(i, 1, FColor::Green, FString::FromInt(i));
}
}
else
{
GEngine->AddOnScreenDebugMessage(9, 1, FColor::Red, "Singleton gecerli degil!");
}
}
Ekli dosyayı görüntüle 4
Şekil 3
Blueprint Üzerinde Kullanım
Malesef Singleton örneğine Blueprint üzerinden erişmenin doğrudan bir yolu yoktur. Bu yüzden UBlueprintFunctionLibrary yardımı ile Blueprint üzerinden erişim için bir fonksiyon daha gerekmektedir. Kısaca yeni bir BlueprintFunctionLibrary tabanlı C++ sınıfı projeye eklenmeli ve bu sınıfa aşağıdaki fonksiyon eklenmelidir.
MyBlueprintFunctionLibrary.h
Kod:
#pragma once
#include "Kismet/BlueprintFunctionLibrary.h"
#include "MyBlueprintFunctionLibrary.generated.h"
/**
*
*/
UCLASS()
class UET_API UMyBlueprintFunctionLibrary : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintPure, Category = Singleton) static class USingleton* GetGameSingleton();
};
MyBlueprintFunctionLibrary.cpp
Kod:
#include "UET.h"
#include "MyBlueprintFunctionLibrary.h"
#include "Singleton.h"
USingleton* UMyBlueprintFunctionLibrary::GetGameSingleton()
{
return Cast<USingleton>(GEngine->GameSingleton);
}
Bu aşamadan sonra Singleton örneği, artık herhangi bir Blueprint üzerinden de çağrılabilecektir.
Dikkat Edilmesi Gereken Noktalar
Singleton üzerinde değer yada referans kullanıldığında değerlerini runtime değiştirmemek gerekir. Bu durum, doğru tasarım yapmaya aykırıdır. Belirtmekte yarar vardır ki değişken ve referans kullanıldığında; çalışma sırasında değiştirilirlerse, öngörülemeyecek sorunlara yol açabilir, tasarımı kolaylaştırmak yerine zorlaştırır ve yazılım geliştirme yaklaşımlarına da aykırıdır. Değişkenler değişmez(constant) olması durumunda rahatlıkla kullanılabilir ama ne zaman ilkleneceği(ilk değerini alacağı) doğru seçilmelidir. Örneğin Blueprint üzerinde oluşturuldukları sırada verilen varsayılan(default) değerlerinin korunması tavsiye edilir.
Referanslar
[1] Wikipedia, Singleton pattern
[2] Rama, Global Data Access, Data Storage Class Accessible From Any CPP or BP Class During Runtime