434 lines
19 KiB
C++
434 lines
19 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#pragma once
|
|
|
|
#include "CoreMinimal.h"
|
|
#include "Delegates/Delegate.h"
|
|
#include "Subsystems/EngineSubsystem.h"
|
|
#include "SubobjectData.h"
|
|
#include "ModuleDescriptor.h"
|
|
#include "SubobjectDataSubsystem.generated.h"
|
|
|
|
class UActorComponent;
|
|
class FScopedTransaction;
|
|
|
|
DECLARE_MULTICAST_DELEGATE_OneParam(FOnNewSubobjectAdded, const FSubobjectData&);
|
|
|
|
/** Options when adding a new subobject */
|
|
USTRUCT(BlueprintType, Category = "SubobjectDataSubsystem")
|
|
struct FAddNewSubobjectParams
|
|
{
|
|
GENERATED_USTRUCT_BODY()
|
|
|
|
FAddNewSubobjectParams()
|
|
: ParentHandle(FSubobjectDataHandle::InvalidHandle)
|
|
, NewClass(nullptr)
|
|
, AssetOverride(nullptr)
|
|
, BlueprintContext(nullptr)
|
|
, bSkipMarkBlueprintModified(false)
|
|
, bConformTransformToParent(true)
|
|
{
|
|
}
|
|
|
|
UPROPERTY(BlueprintReadWrite, Category = "SubobjectDataSubsystem")
|
|
FSubobjectDataHandle ParentHandle;
|
|
|
|
/** The class of the new subobject that will be added */
|
|
UPROPERTY(BlueprintReadWrite, Category = "SubobjectDataSubsystem")
|
|
TObjectPtr<UClass> NewClass;
|
|
|
|
/** Specific asset to use instead of the selected asset in the content browser */
|
|
UObject* AssetOverride;
|
|
|
|
/**
|
|
* Pointer to the blueprint context that this subobject is in. If this is null, it is assumed that
|
|
* this subobject is being added to an instance.
|
|
*/
|
|
UPROPERTY(BlueprintReadWrite, Category = "SubobjectDataSubsystem")
|
|
TObjectPtr<UBlueprint> BlueprintContext;
|
|
|
|
/** Optionally skip marking this blueprint as modified (e.g. if we're handling that externally */
|
|
UPROPERTY(BlueprintReadWrite, Category = "SubobjectDataSubsystem")
|
|
uint8 bSkipMarkBlueprintModified : 1;
|
|
|
|
/** Whether the newly created component should keep its transform, or conform it to its parent */
|
|
UPROPERTY(BlueprintReadWrite, Category = "SubobjectDataSubsystem")
|
|
uint8 bConformTransformToParent : 1;
|
|
};
|
|
|
|
/** Options for reparenting subobjects */
|
|
USTRUCT(BlueprintType, Category = "SubobjectDataSubsystem")
|
|
struct FReparentSubobjectParams
|
|
{
|
|
GENERATED_USTRUCT_BODY()
|
|
|
|
FReparentSubobjectParams() = default;
|
|
|
|
/** The handle of the subobject to reparent to. */
|
|
UPROPERTY(BlueprintReadWrite, Category = "SubobjectDataSubsystem")
|
|
FSubobjectDataHandle NewParentHandle;
|
|
|
|
/**
|
|
* Pointer to the blueprint context that this subobject is in. If this is null, it is assumed that
|
|
* this subobject is being added to an instance.
|
|
*/
|
|
UPROPERTY(BlueprintReadWrite, Category = "SubobjectDataSubsystem")
|
|
TObjectPtr<UBlueprint> BlueprintContext = nullptr;
|
|
|
|
/**
|
|
* The preview actor context to be used if in a blueprint context.
|
|
* This must have a value if BlueprintContext is needed.
|
|
*/
|
|
UPROPERTY(BlueprintReadWrite, Category = "SubobjectDataSubsystem")
|
|
TObjectPtr<AActor> ActorPreviewContext = nullptr;
|
|
};
|
|
|
|
/**
|
|
* The Subobject Data Subsystem will produce the reflected subobject data
|
|
* based on a given root object. A root object can be anything, an actor
|
|
* instance clicked on via the level editor, a UBlueprint* by opening an asset,
|
|
* or something piped in from python or other scripting languages.
|
|
*/
|
|
UCLASS(Category = "SubobjectDataSubsystem")
|
|
class SUBOBJECTDATAINTERFACE_API USubobjectDataSubsystem : public UEngineSubsystem
|
|
{
|
|
GENERATED_BODY()
|
|
public:
|
|
|
|
/** Implement this for initialization of instances of the system */
|
|
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
|
|
|
|
/** Implement this for deinitialization of instances of the system */
|
|
virtual void Deinitialize() override;
|
|
|
|
/**
|
|
* Static wrapper for getting this engine subsystem. Will return nullptr
|
|
* if the SubobjectDataInterface module has not been loaded yet.
|
|
*/
|
|
static USubobjectDataSubsystem* Get();
|
|
|
|
/**
|
|
* Gather all subobjects that the given UObject context has. Populates an array of
|
|
* handles that will have the given context and all it's subobjects.
|
|
*
|
|
* @param Context Object to gather subobjects for
|
|
* @param OutArray Array to populate (will be emptied first)
|
|
*/
|
|
void GatherSubobjectData(UObject* Context, TArray<FSubobjectDataHandle>& OutArray);
|
|
|
|
////////////////////////////////////////////
|
|
// System events
|
|
|
|
/** Delegate invoked when a new subobject is successfully added */
|
|
FOnNewSubobjectAdded& OnNewSubobjectAdded() { return OnNewSubobjectAdded_Delegate; };
|
|
|
|
////////////////////////////////////////////
|
|
|
|
/**
|
|
* Gather all subobjects that the given Blueprint context has. Populates an array of
|
|
* handles that will have the given context and all it's subobjects.
|
|
*
|
|
* @param Context Object to gather subobjects for
|
|
* @param OutArray Array to populate (will be emptied first)
|
|
*/
|
|
UFUNCTION(BlueprintCallable, Category = "SubobjectDataSubsystem", meta = (DisplayName = "Gather Subobject Data For Blueprint"))
|
|
void K2_GatherSubobjectDataForBlueprint(UBlueprint* Context, TArray<FSubobjectDataHandle>& OutArray);
|
|
|
|
/**
|
|
* Gather all subobjects that the given actor instance has. Populates an array of
|
|
* handles that will have the given context and all it's subobjects.
|
|
*
|
|
* @param Context Object to gather subobjects for
|
|
* @param OutArray Array to populate (will be emptied first)
|
|
*/
|
|
UFUNCTION(BlueprintCallable, Category = "SubobjectDataSubsystem", meta = (DisplayName = "Gather Subobject Data For Instance"))
|
|
void K2_GatherSubobjectDataForInstance(AActor* Context, TArray<FSubobjectDataHandle>& OutArray);
|
|
|
|
/**
|
|
* Attempt to find the subobject data for a given handle. OutData will only
|
|
* be valid if the function returns true.
|
|
*
|
|
* @param Handle Handle of the subobject data you want to acquire
|
|
* @param OutData Reference to the subobject data to populate
|
|
*
|
|
* @return bool true if the data was found
|
|
*/
|
|
UFUNCTION(BlueprintCallable, Category = "SubobjectDataSubsystem", meta = (DisplayName = "FindSubobjectDataFromHandle"))
|
|
bool K2_FindSubobjectDataFromHandle(const FSubobjectDataHandle& Handle, FSubobjectData& OutData) const;
|
|
|
|
/**
|
|
* Attempt to find an existing handle for the given object.
|
|
*
|
|
* @param Context The context that the object to find is within
|
|
* @param ObjectToFind The object that you want to find the handle for within the context
|
|
*
|
|
* @return FSubobjectDataHandle The subobject handle for the object, Invalid handle if not found.
|
|
*/
|
|
UFUNCTION(BlueprintCallable, Category = "SubobjectDataSubsystem")
|
|
FSubobjectDataHandle FindHandleForObject(const FSubobjectDataHandle& Context, const UObject* ObjectToFind, UBlueprint* BPContext = nullptr) const;
|
|
|
|
////////////////////////////////////////////
|
|
// Modifying subobjects
|
|
|
|
/**
|
|
* Creates a new C++ component from the specified class type
|
|
* The user will be prompted to pick a new subclass name and code will be recompiled
|
|
*
|
|
* @return The new class that was created
|
|
*/
|
|
UFUNCTION(BlueprintCallable, Category = "SubobjectDataSubsystem", meta = (DisplayName = "Create New C++ Component"))
|
|
static UClass* CreateNewCPPComponent(TSubclassOf<UActorComponent> ComponentClass, const FString& NewClassPath, const FString& NewClassName);
|
|
|
|
/**
|
|
* Creates a new Blueprint component from the specified class type
|
|
* The user will be prompted to pick a new subclass name and a blueprint asset will be created
|
|
*
|
|
* @return The new class that was created
|
|
*/
|
|
UFUNCTION(BlueprintCallable, Category = "SubobjectDataSubsystem", meta = (DisplayName = "Create New Blueprint Component"))
|
|
static UClass* CreateNewBPComponent(TSubclassOf<UActorComponent> ComponentClass, const FString& NewClassPath, const FString& NewClassName);
|
|
|
|
/**
|
|
* Add a new subobject as a child to the given parent object
|
|
*
|
|
* @param Params Options to consider when adding this subobject
|
|
*
|
|
* @return FSubobjectDataHandle Handle to the newly created subobject, Invalid handle if creation failed
|
|
*/
|
|
UFUNCTION(BlueprintCallable, Category = "SubobjectDataSubsystem")
|
|
FSubobjectDataHandle AddNewSubobject(const FAddNewSubobjectParams& Params, FText& FailReason);
|
|
|
|
/**
|
|
* Attempts to delete the given array of subobjects from their context
|
|
*
|
|
* @param ContextHandle The owning context of the subobjects that should be removed
|
|
* @param SubobjectsToDelete Array of subobject handles that should be deleted
|
|
* @param BPContext The blueprint context for the given
|
|
*
|
|
* @return The number of subobjects successfully deleted
|
|
*/
|
|
UFUNCTION(BlueprintCallable, Category = "SubobjectDataSubsystem", meta = (DisplayName = "Delete Subobjects from Blueprint"))
|
|
int32 DeleteSubobjects(const FSubobjectDataHandle& ContextHandle, const TArray<FSubobjectDataHandle>& SubobjectsToDelete, UBlueprint* BPContext = nullptr);
|
|
|
|
/**
|
|
* Attempts to delete the given array of subobjects from their context
|
|
*
|
|
* @param ContextHandle The owning context of the subobjects that should be removed
|
|
* @param SubobjectsToDelete Array of subobject handles that should be deleted
|
|
*
|
|
* @return The number of subobjects successfully deleted
|
|
*/
|
|
UFUNCTION(BlueprintCallable, Category = "SubobjectDataSubsystem", meta = (DisplayName = "Delete Subobjects from Instance"))
|
|
int32 K2_DeleteSubobjectsFromInstance(const FSubobjectDataHandle& ContextHandle, const TArray<FSubobjectDataHandle>& SubobjectsToDelete);
|
|
|
|
/**
|
|
* Attempts to delete the given array of subobjects from their context
|
|
*
|
|
* @param ContextHandle The owning context of the subobjects that should be removed
|
|
* @param SubobjectsToDelete Array of subobject handles that should be deleted
|
|
* @param OutComponentToSelect Populates this handle with a valid selection in the component hierarchy if desired
|
|
* @param bForce If true then this will attempt to delete a subobject even if the CanDelete function flags otherwise.
|
|
* @return The number of subobjects successfully deleted
|
|
*/
|
|
int32 DeleteSubobjects(const FSubobjectDataHandle& ContextHandle, const TArray<FSubobjectDataHandle>& SubobjectsToDelete, FSubobjectDataHandle& OutComponentToSelect, UBlueprint* BPContext = nullptr, bool bForce = false);
|
|
|
|
/**
|
|
* Attempts to delete the given subobject from its blueprint context
|
|
*
|
|
* @param ContextHandle The owning context of the subobjects that should be removed
|
|
* @param SubobjectToDelete The subobject handles that should be deleted
|
|
* @param BPContext The blueprint context for the given
|
|
*
|
|
* @return The number of subobjects successfully deleted
|
|
*/
|
|
UFUNCTION(BlueprintCallable, Category = "SubobjectDataSubsystem", meta = (DisplayName = "Delete Subobject from Blueprint"))
|
|
int32 DeleteSubobject(const FSubobjectDataHandle& ContextHandle, const FSubobjectDataHandle& SubobjectToDelete, UBlueprint* BPContext = nullptr);
|
|
|
|
/**
|
|
* Attempts to delete the given subobject from its context
|
|
*
|
|
* @param ContextHandle The owning context of the subobjects that should be removed
|
|
* @param SubobjectToDelete The subobject handles that should be deleted
|
|
*
|
|
* @return The number of subobjects successfully deleted
|
|
*/
|
|
UFUNCTION(BlueprintCallable, Category = "SubobjectDataSubsystem", meta = (DisplayName = "Delete Subobject from Instance"))
|
|
int32 K2_DeleteSubobjectFromInstance(const FSubobjectDataHandle& ContextHandle, const FSubobjectDataHandle& SubobjectToDelete);
|
|
|
|
/**
|
|
* Attempts to rename the given subobject to the new name.
|
|
*
|
|
* @param Handle Handle to the subobject to rename
|
|
* @param InNewName The new name that is desired for the given subobject
|
|
*
|
|
* @return True if the rename was successful, false otherwise.
|
|
*/
|
|
UFUNCTION(BlueprintCallable, Category = "SubobjectDataSubsystem")
|
|
bool RenameSubobject(const FSubobjectDataHandle& Handle, const FText& InNewName);
|
|
|
|
/**
|
|
* Attempts to change the subclass of a native component
|
|
*
|
|
* @param Handle Handle to the subobject to change class of
|
|
* @param NewClass The new class that is desired for the given subobject
|
|
*
|
|
* @return True if the class change was successful, false otherwise.
|
|
*/
|
|
UFUNCTION(BlueprintCallable, Category = "SubobjectDataSubsystem")
|
|
bool ChangeSubobjectClass(const FSubobjectDataHandle& Handle, const UClass* NewClass);
|
|
|
|
/**
|
|
* Attempts to reparent the given subobject to the new parent
|
|
*
|
|
* @param NewParentHandle Handle of the new parent
|
|
* @param ToReparentHandle The handle of the subobject that will get moved
|
|
*
|
|
* @return True if the reparent was successful, false otherwise.
|
|
*/
|
|
UFUNCTION(BlueprintCallable, Category = "SubobjectDataSubsystem")
|
|
bool ReparentSubobject(const FReparentSubobjectParams& Params, const FSubobjectDataHandle& ToReparentHandle);
|
|
|
|
UFUNCTION(BlueprintCallable, Category = "SubobjectDataSubsystem")
|
|
bool MakeNewSceneRoot(const FSubobjectDataHandle& Context, const FSubobjectDataHandle& NewSceneRoot, UBlueprint* BPContext);
|
|
|
|
/**
|
|
* Attempts to reparent all subobjects in the HandlesToMove array to the new parent handle.
|
|
*
|
|
* @param NewParentHandle Handle of the new parent
|
|
*/
|
|
UFUNCTION(BlueprintCallable, Category = "SubobjectDataSubsystem")
|
|
bool ReparentSubobjects(const FReparentSubobjectParams& Params, const TArray<FSubobjectDataHandle>& HandlesToMove);
|
|
|
|
/**
|
|
* Remove the child subobject from the owner
|
|
*
|
|
* @return True if the child was successfully removed.
|
|
*/
|
|
UFUNCTION(BlueprintCallable, Category = "SubobjectDataSubsystem")
|
|
bool DetachSubobject(const FSubobjectDataHandle& OwnerHandle, const FSubobjectDataHandle& ChildToRemove);
|
|
|
|
/**
|
|
* Add the given subobject to a new owner. This will remove the subobject from its previous
|
|
* owner if necessary.
|
|
*
|
|
* @param OwnerHandle The new owner to attach to
|
|
* @param ChildToAddHandle Handle to the subobject that will become a child of the owner
|
|
*
|
|
* @return true if the child was added successfully
|
|
*/
|
|
UFUNCTION(BlueprintCallable, Category = "SubobjectDataSubsystem")
|
|
bool AttachSubobject(const FSubobjectDataHandle& OwnerHandle, const FSubobjectDataHandle& ChildToAddHandle);
|
|
|
|
/**
|
|
* Returns true if the given new text is a valid option to rename the
|
|
* subobject with the given handle. Populates the OutErrorMessage if
|
|
* it is not valid.
|
|
*
|
|
* @param Handle Handle to the subobject that is being checked
|
|
* @param InNewText The new name that is desired
|
|
* @param OutErrorMessage The reasoning for an invalid name
|
|
*
|
|
* @return True if the rename is valid
|
|
*/
|
|
UFUNCTION(BlueprintCallable, Category = "SubobjectDataSubsystem")
|
|
bool IsValidRename(const FSubobjectDataHandle& Handle, const FText& InNewText, FText& OutErrorMessage) const;
|
|
|
|
/**
|
|
* Returns true if the given array of handles represents subobjects that
|
|
* can be copied.
|
|
*/
|
|
UFUNCTION(BlueprintCallable, Category = "SubobjectDataSubsystem")
|
|
bool CanCopySubobjects(const TArray<FSubobjectDataHandle>& Handles) const;
|
|
|
|
UFUNCTION(BlueprintCallable, Category = "SubobjectDataSubsystem")
|
|
void CopySubobjects(const TArray<FSubobjectDataHandle>& Handles, UBlueprint* BpContext);
|
|
|
|
UFUNCTION(BlueprintCallable, Category = "SubobjectDataSubsystem")
|
|
bool CanPasteSubobjects(const FSubobjectDataHandle& RootHandle, UBlueprint* BPContext = nullptr) const;
|
|
|
|
/**
|
|
* Pastes the given subobjects to the PasteToContext.
|
|
*
|
|
* @param PasteToContext The subobject to paste things onto
|
|
* @param NewParentHandles Array of Subobject Handles that you would like to paste
|
|
* @param BpContext Blueprint to use, if you are pasting to a blueprint. Null if pasting to an instanced object
|
|
* @param OutPastedHandles Array populated with the handles to the newly pasted subobjects
|
|
*/
|
|
UFUNCTION(BlueprintCallable, Category = "SubobjectDataSubsystem")
|
|
void PasteSubobjects(const FSubobjectDataHandle& PasteToContext, const TArray<FSubobjectDataHandle>& NewParentHandles, UBlueprint* BpContext, TArray<FSubobjectDataHandle>& OutPastedHandles);
|
|
|
|
/**
|
|
* Duplicate the given array of subobjects on the context.
|
|
*
|
|
* @param Context The owning context that the subobjects to dup come from
|
|
* @param SubobjectsToDup Array of handles of existing subobjects you would like to have duplicated
|
|
* @param BpContext Pointer to the current blueprint context if necessary. Use nullptr if dealing with instances
|
|
* @param OutNewSubobjects Array that will be populated with any newly created subobjects
|
|
*/
|
|
UFUNCTION(BlueprintCallable, Category = "SubobjectDataSubsystem")
|
|
void DuplicateSubobjects(const FSubobjectDataHandle& Context, const TArray<FSubobjectDataHandle>& SubobjectsToDup, UBlueprint* BpContext, TArray<FSubobjectDataHandle>& OutNewSubobjects);
|
|
|
|
FScopedTransaction* BeginTransaction(const TArray<FSubobjectDataHandle>& Handles, const FText& Description, UBlueprint* Blueprint);
|
|
|
|
/**
|
|
* Find the scene root for a given subobject handle.
|
|
* If this is an actor, then it will return the handle pointing to its DefaultSceneRoot.
|
|
* If this is a component, it will walk the parent chain until it finds the DefaultSceneRoot.
|
|
*/
|
|
FSubobjectDataHandle FindSceneRootForSubobject(const FSubobjectDataHandle& InHandle) const;
|
|
|
|
UFUNCTION(BlueprintCallable, Category = "SubobjectDataSubsystem")
|
|
static void RenameSubobjectMemberVariable(UBlueprint* BPContext, const FSubobjectDataHandle& InHandle, const FName NewName);
|
|
|
|
/**
|
|
* Walk the hierarchy up to find the AActor root for the given handle
|
|
*
|
|
* @param StartingHandle The handle to start walking the hierarchy at
|
|
*
|
|
* @return The handle of the root actor if one exists, FSubobjectDataHandle::InvalidHandle otherwise.
|
|
*/
|
|
static FSubobjectDataHandle GetActorRootHandle(const FSubobjectDataHandle& StartingHandle);
|
|
|
|
private:
|
|
|
|
/**
|
|
* Recursively add the attachment children of the given actor component
|
|
*/
|
|
FSubobjectDataHandle FindOrCreateAttachParentForComponent(UActorComponent* InActorComponent, const FSubobjectDataHandle& ActorRootHandle, TArray<FSubobjectDataHandle>& ExistingHandles);
|
|
|
|
/**
|
|
* Recursively search for an attachment parent that matches the given InActorComponent on the blueprint
|
|
*/
|
|
FSubobjectDataHandle FindAttachParentForInheritedComponent(UActorComponent* InActorComponent, const FSubobjectDataHandle& ActorRootHandle, UBlueprint* BP);
|
|
|
|
/**
|
|
* Find all child subobject data related to the given root context.
|
|
* Populates the OutVisited set with all data.
|
|
*/
|
|
void FindAllSubobjectData(FSubobjectData* RootContext, TSet<FSubobjectData*>& OutVisited) const;
|
|
|
|
/**
|
|
* Gets a ref to subobject data that has been initialized with the given context.
|
|
* It will have a unique ID, but may be reused from the FreeData array.
|
|
*/
|
|
FSubobjectDataHandle CreateSubobjectData(UObject* Context, const FSubobjectDataHandle& ParentHandle = FSubobjectDataHandle::InvalidHandle, bool bIsInheritedSCS = false);
|
|
|
|
/**
|
|
* Get a handle to the subobject with the given context with a specific parent.
|
|
* If the context is already a known child of the parent, then it will return an existing subobject handle.
|
|
*/
|
|
FSubobjectDataHandle FactoryCreateSubobjectDataWithParent(UObject* Context, const FSubobjectDataHandle& ParentHandle, bool bIsInheritedSCS = false);
|
|
|
|
/**
|
|
* Create a subobject data and handle for an inherited blueprint context.
|
|
* This will populate the given array of handles recursively with all possible children
|
|
* (which are USCS_Node* in this case)
|
|
*/
|
|
FSubobjectDataHandle FactoryCreateInheritedBpSubobject(UObject* Context, const FSubobjectDataHandle& ParentHandle, bool bIsInherited, TArray<FSubobjectDataHandle>& OutArray);
|
|
|
|
/** Find the parent that this new subobject should attach to */
|
|
FSubobjectDataHandle FindParentForNewSubobject(const UObject* NewSubobject, const FSubobjectDataHandle& SelectedParent);
|
|
|
|
FOnNewSubobjectAdded OnNewSubobjectAdded_Delegate;
|
|
|
|
}; |