Sparse Class Data システムを使用すると、よく使用される アクタ 型の冗長なデータを取り除くことで、メモリが節約されます。ゲーム開発中に、
ゲーム内に同時に多数のインスタンスを持つ (冗長なコピーによって大量のメモリが消費される) アクタ クラスのメンバーである。
配置済みのアクタ インスタンスで変化しない。つまり、基本値をオーバーライド (変更) するアクタ インスタンスが存在しないため、プロパティに
EditInstanceOnly
またはEditAnywhere
UProperty 指定子が必要ない。C++ コードで変更されない。変数に直接アクセスする C++ コードは、アクセサ関数への呼び出しで置き換える必要がある。
Sparse Class Data 機能を実装するには、ネイティブ (C++) コードが必要です。ブループリントで宣言された変数は、このプロセスの対象とするために、C++ コードに移行する必要があります。
実装例
クラスの 1 つに Sparse Class Data を使用すると決めたら、候補となるプロパティを特定する必要があります。EditAnywhere
、EditInstanceOnly
、または BlueprintReadWrite
でタグ付けされたプロパティは、Sparse Class Data の候補にはなりません。同様に、ネイティブ C++ コードで変更されるプロパティも、Sparse Class Data システムで処理する対象とはなりません。というのも、そのようなプロパティはアクタ インスタンスごとに異なる値を持つ可能性があります。レベル エディタでインスタンスごとに編集されたり、ブループリント スクリプティングや、ネイティブ コードによってゲーム セッション中に 1 つのアクタ インスタンスの値が変更されたりするからです。次のサンプル クラスにはいくつかのプロパティが含まれており、一部は Sparse Class Data の候補です。
// このクラスのプロパティはすべてエディタで変更できるが、クラスごとの変更であり、インスタンスごとの変更はできない。
UCLASS(BlueprintType)
class AMyActor : public AActor
{
GENERATED_UCLASS_BODY()
public:
// このプロパティはエディタで変更できるが、クラスごとの変更しかできない。
// ブループリント グラフではアクセスや変更ができない。
// Sparse Class Data の有力な候補である。
UPROPERTY(EditDefaultsOnly)
float MyFloatProperty;
// このプロパティはエディタで変更できない。
// ブループリント グラフでアクセスできるが、変更はできない。
// Sparse Class Data の有力な候補である。
UPROPERTY(BlueprintReadOnly)
int32 MyIntProperty;
// このプロパティはエディタでクラスごとに編集できる。
// ブループリント グラフでアクセスできるが、変更はできない。
// Sparse Class Data の有力な候補である。
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
FName MyNameProperty;
// このプロパティは配置済みの MyActor インスタンスで編集できる。
// Sparse Class Data の有力な候補ではない。
UPROPERTY(EditAnywhere)
FVector MyVectorProperty;
// このプロパティはブループリント グラフで変更できる。
// Sparse Class Data の有力な候補ではない。
UPROPERTY(BlueprintReadWrite)
FVector2D MyVector2DProperty;
};
候補となる変数を特定したら、それらの変数を含めた構造体を作成し、BlueprintType
EditDefaultsOnly
指定子を含んでいる必要があります。
USTRUCT(BlueprintType)
struct FMySparseClassData
{
GENERATED_BODY()
FMySparseClassData()
: MyFloatProperty(0.f)
, MyIntProperty(0)
, MyNameProperty(NAME_None)
{ }
// エディタでこのプロパティのデフォルト値を設定できる。
// ブループリント グラフではアクセスできない。
UPROPERTY(EditDefaultsOnly)
float MyFloatProperty;
// このプロパティの値は C++ コードで設定される。
// ブループリント グラフでアクセスできる (が、変更できない)。
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
int32 MyIntProperty;
// エディタでこのプロパティのデフォルト値を設定できる。
// ブループリント グラフでアクセスできる (が、変更できない)。
//「GetByRef」は、ブループリント グラフがコピーではなく定数の参照にアクセスすることを意味する。
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta=(GetByRef))
FName MyNameProperty;
};
FMySparseClassData
この構造体を使用するには、元のクラスに何らかの変更が必要です。プロセスに必要な変更は、具体的には次の 3 つです。
この構造体を Sparse Class Data 型として使用するようにクラスに指示する。
新しい構造体に移動するプロパティに
#if WITH_EDITORONLY_DATA
プリコンパイラ ディレクティブ ブロックを追加し、元のクラスでは_DEPRECATED
サフィックスでマークする。さらに、UProperty 指定子をすべて削除し、プロパティをprivate
アクセスに設定します。_DEPRECATED
の名前を使用するうえでコードの他の部分は変更しないようにします。これらの行は、Sparse Class Data 構造体へのアクセサ呼び出しで置き換えられます。エディタ ビルドで (
#if WITH_EDITOR
)、MoveDataToSparseClassDataStruct
関数をオーバーライドします。この関数は、元のクラスから Sparse Class Data 構造体へのワンタイム コピーを実行することで既存のデータ値を保持します。
これらの変更を加えると、クラスは次のようになります。
UCLASS(BlueprintType, SparseClassDataTypes = MySparseClassData)
class AMyActor : public AActor
{
GENERATED_BODY()
#if WITH_EDITOR
public:
// ~ この関数は既存のデータを FMySparseClassData に移動する。
virtual void MoveDataToSparseClassDataStruct() const override;
#endif // WITH_EDITOR
#if WITH_EDITORONLY_DATA
//~ これらのプロパティは FMySparseClassData 構造体に移動する。
private:
// このプロパティはエディタで変更できるが、クラスごとの変更しかできない。
// ブループリント グラフではアクセスや変更ができない。
// Sparse Class Data の有力な候補である。
UPROPERTY()
float MyFloatProperty_DEPRECATED;
// このプロパティはエディタで変更できない。
// ブループリント グラフでアクセスできるが、変更はできない。
// Sparse Class Data の有力な候補である。
UPROPERTY()
int32 MyIntProperty_DEPRECATED;
// このプロパティはエディタでクラスごとに編集できる。
// ブループリント グラフでアクセスできるが、変更はできない。
// Sparse Class Data の有力な候補である。
UPROPERTY()
FName MyNameProperty_DEPRECATED;
#endif // WITH_EDITORONLY_DATA
//~ 残りのプロパティはインスタンスごとに変更できる。
//~ したがって、Sparse Class Data 実装には含まない。
public:
// このプロパティは配置済みの MyActor インスタンスで編集できる。
// Sparse Class Data の有力な候補ではない。
UPROPERTY(EditAnywhere)
FVector MyVectorProperty;
// このプロパティはブループリント グラフで変更できる。
// Sparse Class Data の有力な候補ではない。
UPROPERTY(BlueprintReadWrite)
FVector2D MyVector2DProperty;
};
次の関数は、共有プロパティすべての既存の値をコピーします。
#if WITH_EDITOR
void AMyActor::MoveDataToSparseClassDataStruct() const
{
// スパース データが保存済みの場合は上書きしないようにする。
UBlueprintGeneratedClass* BPClass = Cast<UBlueprintGeneratedClass>(GetClass());
if (BPClass == nullptr || BPClass->bIsSparseClassDataSerializable == true)
{
return;
}
Super::MoveDataToSparseClassDataStruct();
#if WITH_EDITORONLY_DATA
// Unreal Header Tool (UHT) によって GetMySparseClassData が自動的に作成される。
FMySparseClassData* SparseClassData = GetMySparseClassData();
// すべての Sparse Class Data プロパティを含めるにはこれらの行を変更する。
SparseClassData->MyFloatProperty = MyFloatProperty_DEPRECATED;
SparseClassData->MyIntProperty = MyIntProperty_DEPRECATED;
SparseClassData->MyNameProperty = MyNameProperty_DEPRECATED;
#endif // WITH_EDITORONLY_DATA
}
#endif // WITH_EDITOR
Engine/BlueprintGeneratedClass.h
この時点で、Sparse Class Data が実装できました。影響を受けるプロパティをユーザーがエディタで編集、アクセスしても、気がつくような動作の違いはありませんが、出荷ビルドのメモリ使用量は削減されます。プロパティを C++ コードで参照する場合は、その変数にアクセスする試行を getter 関数の呼び出しに置き換えます。例えば、MyFloatProperty
変数へのアクセスに使用していたコードでは、代わりに GetMyFloatProperty
を呼び出すようにします。この関数は、UHT によって自動的に生成されます。重要な getter 関数があり、その動作を維持する必要がある場合は、UHT によって getter 関数が生成されないよう NoGetter
UProperty メタデータ指定子で指示します。値ではなく定数の参照によってアクセスする必要のある変数には、GetByRef
UProperty メタデータ指定子を使用します。