Pull to refresh

Создание своего типа ассета в Unreal Engine 4 и кастомизация панели свойств

Reading time4 min
Views23K


Здравствуйте меня зовут Дмитрий. Я занимаюсь созданием компьютерных игр на Unreal Engine в качестве хобби. Сегодня я хотел бы рассказать как в Unreal Engine создать свой тип ассета и как добавить дополнительный элемент на панель свойств ассета. Итак начнем.

Начнем с создания ассета. Во первых надо создать класс для своего ассета.

UCLASS()
class UICUSTOM_API UMyObject : public UObject
{
	GENERATED_BODY()
public:
	UPROPERTY(EditAnywhere, Category = "My Object Properties")
		FString Name;

};

После этого нужно чтобы наш ассет отображался в контент браузере для этого создаем потомка для класса UFactory:

UCLASS()
class UICUSTOM_API UMyObjectFactory : public UFactory
{
	GENERATED_UCLASS_BODY()

	// UFactory interface
	virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override;
	// End of UFactory interface
	virtual bool CanCreateNew() const override;
};

Здесь для нас наиболее важным методом является FactoryCreateNew который создает экземпляр нашего класса.

UObject* UMyObjectFactory::FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn)
{
	UMyObject* NewObjectAsset = NewObject<UMyObject>(InParent,Class, Name, Flags | RF_Transactional);
	
	return NewObjectAsset;
}

Итак наш ассет уже можно создать в контент браузере но нельзя выбрать не название, не цвет иконки, не категорию в конце концов. Для всего этого необходимо создать ещё один класс, потомок FAssetTypeActions_Base.

class UICUSTOM_API FMyObjectAssetAction : public FAssetTypeActions_Base
{
public:
	virtual FText GetName() const override;
	virtual FColor GetTypeColor() const override;
	virtual UClass* GetSupportedClass() const override;
	virtual bool HasActions(const TArray<UObject*>& InObjects) const override { return false; }
	virtual uint32 GetCategories() override;
	static void RegistrateCustomPartAssetType();
};

В принцепе все понятно по названию методов кроме метода RegistrateCustomPartAssetType(). Этот метод нужен для регистрации данного класса. Итак откуда же его вызывать? Этот метод должен быть вызван один раз при загрузке редактора, поэтому наиболее подходящее место для его вызова это конструктор GameMode. Вот собственно и он:

AUICustomGameMode::AUICustomGameMode()
{
#if WITH_EDITORONLY_DATA
	FMyClassDetails::RegestrateCostumization();
	FMyObjectAssetAction::RegistrateCustomPartAssetType();
#endif //WITH_EDITORONLY_DATA
}

После этого можно компилировать проект и наслаждаться результатом:



Вы наверно спросите. А какой в этом смысл, я ведь могу точно также создать блюпринт который будет наследовать MyObject, получится тоже самое но без геморроя. Здесь надо уточнить что блюпринт является классом наследником MyObject а не им самим.

Например если разместить ссылку на MyObject в каком нибудь другом ассете то созданный таким образом ассет вы выбрать сможете, а блюпринт для которого этот ассет является базовым нет.



Как видете ассет есть, а блюпринта нет.

Теперь перейдем к добавлению элементов на панель свойств ассета. Для этого создадим подопытный класс:

UCLASS()
class UICUSTOM_API ATestAct : public AActor
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	ATestAct();

	// Called when the game starts or when spawned
	virtual void BeginPlay() override;
	
	// Called every frame
	virtual void Tick( float DeltaSeconds ) override;

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
		UMyObject* MyObject;
	
};

Что бы кастомизировать его панель свойств нужно создать класс наследник IDetailCustomization:

class FMyClassDetails : public IDetailCustomization
{
public:
	static FReply MClick(IDetailLayoutBuilder* DetailBuilder);

	/** Makes a new instance of this detail layout class for a specific detail view requesting it */
	static TSharedRef<IDetailCustomization> MakeInstance();

	static void RegestrateCostumization();

	/** IDetailCustomization interface */
	virtual void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override;

	static void ShowNotification(FText Text, SNotificationItem::ECompletionState State = SNotificationItem::CS_None);

	static ATestAct* GetObject(IDetailLayoutBuilder* DetailBuilder);
};

Итак разъясняю: MClick метод который сработает когда мы нажмем на добавленную кнопку (в качестве примера я выбрал кнопку, но это может быть любой элемент интерфейса).

CustomizeDetails метод в котором происходит добавление нового элемента на панель деталей:

void FMyClassDetails::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
{
	// Create a category so this is displayed early in the properties
	ATestAct* TestAct = GetObject(&DetailBuilder);
	IDetailCategoryBuilder& MyCategory = DetailBuilder.EditCategory("Button", FText::GetEmpty(), ECategoryPriority::Important);
	
	//You can get properties using the detailbuilder
	//MyProperty= DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(MyClass, MyClassPropertyName));

	FText TestHUDText = FText::FromString("Your Text");
	MyCategory.AddCustomRow(TestHUDText)
		.ValueContent() //NameContent()
		[
			SNew(SButton)
			.Text(FText::FromString("ShowMessage"))
			.OnClicked(FOnClicked::CreateStatic(&FMyClassDetails::MClick, &DetailBuilder))
		];
	
}

RegestrateCostumization Метод необходимый для регистрации данной кастомизации. (Вы наверно заметили его вызов из конструктора GameMode) хочется отметить что TestAct нужно вводить без префикса A:

void FMyClassDetails::RegestrateCostumization()
{
	FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");

	//Custom detail views
	PropertyModule.RegisterCustomClassLayout("TestAct", FOnGetDetailCustomizationInstance::CreateStatic(&FMyClassDetails::MakeInstance));
}

ShowNotification Просто выводит сообщение на экран. GetObject Позволяет получить ссылку на кастомизируемый объект (ATestAct в данном случае).

Итак, вот что мы имеем в результате:



Спасибо за внимание, надеюсь, этот урок позволит вам создавать ещё более лучшие и интересные игры.

Проект с исходными кодами здесь.

P.S. Небольшое уточнение. Если вы захотите вставить несколько кнопок в одну строку, надо писать так:
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
[
	SNew(SButton)
	.Text(FText::FromString("OK"))
]
+ SHorizontalBox::Slot()
[
	SNew(SButton)
	.Text(FText::FromString("OK"))
]

А если вы заранее не знаете сколько кнопок нужно вам, то можно написать так:
TSharedPtr<SHorizontalBox> Container = SNew(SHorizontalBox);
for (int32 idx = 0; idx < 2; idx++)
{	
	Container->AddSlot()
		[
			SNew(SButton)
			.Text(FText::FromString("OK"))
		];
}
MyCategory.AddCustomRow(TestHUDText)
	.ValueContent()
	[
		Container.ToSharedRef()
	];
Tags:
Hubs:
Total votes 16: ↑15 and ↓1+14
Comments5

Articles