Files
UnrealEngine/Engine/Source/Editor/SubobjectDataInterface/Public/SubobjectData.h
2025-05-18 13:04:45 +08:00

312 lines
12 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Templates/SharedPointer.h"
#include "SubobjectDataHandle.h"
#include "Components/ActorComponent.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "Engine/SCS_Node.h" // #TODO_BH We need to remove this when the actual subobject refactor happens
#include "SubobjectData.generated.h"
class UBlueprint;
/**
* A struct that represents a single subobject. This can be anything, but are
* most commonly components attached to an actor instance or blueprint. Keeps track
* of the handles to its parent object and any child that it has.
*
* If you wish to modify a subobject, use the SubobjectDataSubsystem.
*/
USTRUCT(BlueprintType)
struct SUBOBJECTDATAINTERFACE_API FSubobjectData final
{
friend class USubobjectDataSubsystem;
public:
GENERATED_BODY()
FSubobjectData();
FSubobjectData(UObject* ContextObject, const FSubobjectDataHandle& ParentHandle, const bool bIsInheritedSCS);
/** True if this subobject's handle is valid */
bool IsValid() const { return Handle.IsValid(); }
/**
* @return Whether or not we can edit properties for this subobject
*/
bool CanEdit() const;
/**
* @return Whether or not this object represents a subobject that can be deleted
*/
bool CanDelete() const;
/**
* @return Whether or not this object represents a subobject that can be duplicated
*/
bool CanDuplicate() const;
/**
* @return Whether or not this object represents a subobject that can be copied
*/
bool CanCopy() const;
/**
* @return Whether or not this object represents a subobject that can
* be reparented to other subobjects based on its context.
*/
bool CanReparent() const;
/**
* @return Whether or not this object represents a subobject that can
* be renamed.
*/
bool CanRename() const;
/**
* @return A read-only reference to the object represented by this node.
*/
inline const UObject* GetObject(bool bEvenIfPendingKill = false) const { return WeakObjectPtr.Get(bEvenIfPendingKill); }
/**
* @param InBlueprint The Blueprint in which the object will be edited.
*
* @note May not be the same as the value returned by GetObject().
* @return A reference to the object represented by this node that can be modified within the given Blueprint.
*/
const UObject* GetObjectForBlueprint(UBlueprint* Blueprint) const;
template<class T>
inline const T* GetObjectForBlueprint(UBlueprint* Blueprint) const
{
return Cast<T>(GetObjectForBlueprint(Blueprint));
}
/**
* @param bEvenIfPendingKill If false, nullptr will be returned if the cached object instance is pending kill.
* If true, it will be returned regardless (this is used for recaching the object if the objects
* have been reinstanced following construction script execution).
*
* @note Deliberately non-virtual, for performance reasons.
* @return A read-only reference to the object represented by this node.
*/
template<class T>
inline const T* GetObject(bool bEvenIfPendingKill = false) const
{
return Cast<T>(WeakObjectPtr.Get(bEvenIfPendingKill));
}
/**
* @param bEvenIfPendingKill If false, nullptr will be returned if the cached component template is pending kill.
* If true, it will be returned regardless (this is used for recaching the component template if the objects
* have been reinstanced following construction script execution).
*
* @note Deliberately non-virtual, for performance reasons.
* @warning This will not return the right component for components overridden by the inherited component handler, you need to call GetOrCreateEditableComponentTemplate instead
* @return The component template or instance represented by this node, if it's a component node.
*/
inline const UActorComponent* GetComponentTemplate(bool bEvenIfPendingKill = false) const
{
return Cast<UActorComponent>(WeakObjectPtr.Get(bEvenIfPendingKill));
}
inline const UActorComponent* FindComponentInstanceInActor(const AActor* InActor) const { return FindMutableComponentInstanceInActor(InActor); }
UBlueprint* GetBlueprint() const;
UBlueprint* GetBlueprintBeingEdited() const;
bool IsInstancedComponent() const;
bool IsInstancedActor() const;
bool IsNativeComponent() const;
bool IsBlueprintInheritedComponent() const;
bool IsInheritedComponent() const;
bool IsSceneComponent() const;
bool IsRootComponent() const;
bool IsDefaultSceneRoot() const;
/** Returns true if the component template's FName starts with USceneComponent::GetDefaultSceneRootVariableName */
bool SceneRootHasDefaultName() const;
/* Returns true if this subobject is a component. */
bool IsComponent() const;
bool IsChildActor() const;
/** Returns true if this subobject is a part of a child actor's hierarchy and not the root actor. */
bool IsChildActorSubtreeObject() const;
bool IsRootActor() const;
bool IsActor() const;
/** True if this subobject is an instance of an inherited component (its owner is an instanced actor) */
bool IsInstancedInheritedComponent() const;
bool IsAttachedTo(const FSubobjectDataHandle& InHandle) const;
FString GetDisplayString(bool bShowNativeComponentNames = true) const;
/** Get the display name of this subobject that should be used during drag/drop operations */
FText GetDragDropDisplayText() const;
/**
* Returns a string that gives details about this subobject. For Inherited
* components it will return "(Inherited)". For native components
* this function will return "(NativeComponentName) (Inherited)"
*/
FText GetDisplayNameContextModifiers(bool bShowNativeComponentNames = true) const;
FText GetDisplayName() const;
FName GetVariableName() const;
// Sockets for attaching in the viewport
FText GetSocketName() const;
FName GetSocketFName() const;
bool HasValidSocket() const;
void SetSocketName(FName InNewName);
void SetupAttachment(FName SocketName, const FSubobjectDataHandle& AttachParentHandle = FSubobjectDataHandle::InvalidHandle);
FSubobjectDataHandle GetHandle() const { return Handle; }
FSubobjectDataHandle GetParentHandle() const { return ParentObjectHandle; }
FSubobjectData* GetParentData() const { return ParentObjectHandle.GetData(); }
bool HasParent() const { return ParentObjectHandle.IsValid(); }
const TArray<FSubobjectDataHandle>& GetChildrenHandles() const { return ChildrenHandles; }
/** Walks the parent heirarchy and returns the root subobject in this case */
FSubobjectDataHandle GetRootSubobject() const;
// Return true if the given handle is a child of this subobject
bool HasChild(const FSubobjectDataHandle& ChildHandle) const;
// Attempt to find the given handle in out child array and return a pointer to it
FSubobjectDataHandle FindChild(const FSubobjectDataHandle& ChildHandle) const;
FSubobjectDataHandle FindChildByObject(UObject* ContextObject) const;
/** Get the asset name of this subobject from the asset brokerage */
FText GetAssetName() const;
/** Get the asset path of this subobject from the asset brokerage */
FText GetAssetPath() const;
/** Returns true if the asset this object represents is visible */
bool IsAssetVisible() const;
/**
* Retrieves tooltip text describing the specified component's mobility.
*
* @returns An FText object containing a description of the component's mobility
*/
FText GetMobilityToolTipText() const;
/**
* Retrieves a tooltip text describing if the component is marked Editor only or not
*
* @returns An FText object containing a description of if the component is marked Editor only or not
*/
FText GetComponentEditorOnlyTooltipText() const;
/**
* Retrieves tooltip text describing where the component was first introduced (for inherited components).
*
* @returns An FText object containing a description of when the component was first introduced
*/
FText GetIntroducedInToolTipText() const;
FText GetActorDisplayText() const;
private:
/**
* Add the given subobject handle as a child of this.
* NOTE: This does NOT do any actual manipulation of the subobject structure.
* This only updates this subobject to have the correct handles in it's child structure.
* Actual manipulation of subobjects should be done via the SubobjectSubsystem.
*/
bool AddChildHandleOnly(const FSubobjectDataHandle& InHandle);
/**
* Add the given subobject handle as a child of this.
* * NOTE: This does NOT do any actual manipulation of the subobject structure.
* This only updates this subobject to have the correct handles in it's child structure.
* Actual manipulation of subobjects should be done via the SubobjectSubsystem.
*/
bool RemoveChildHandleOnly(const FSubobjectDataHandle& InHandle);
void SetParentHandle(const FSubobjectDataHandle& NewParentHandle) { ParentObjectHandle = NewParentHandle; }
void ClearParentHandle() { ParentObjectHandle = FSubobjectDataHandle::InvalidHandle; }
// Weak ptr to the object instance represented by this node (e.g. component template)
UPROPERTY()
TWeakObjectPtr<UObject> WeakObjectPtr;
// Handle that will have a globally unique ID when it is constructed (i.e. when this is constructed)
UPROPERTY()
FSubobjectDataHandle Handle;
// Handle to this object's parent. If this is invalid, then it is the root subobject.
UPROPERTY()
FSubobjectDataHandle ParentObjectHandle;
// Set of child subobjects that use this subobject as a parent
UPROPERTY()
TArray<FSubobjectDataHandle> ChildrenHandles;
////////////////////////////////////////////
// Muteable accessors for this subobject to be used by the subsystem
inline UObject* GetMutableObject(bool bEvenIfPendingKill = false) const { return WeakObjectPtr.Get(bEvenIfPendingKill); }
// If this subobject is an actor, then return a pointer to that. Otherwise, search for
// the owning actor on this component and return it.
AActor* GetMutableActorContext();
template<class T>
inline T* GetMutableObject(bool bEvenIfPendingKill = false) const
{
return Cast<T>(WeakObjectPtr.Get(bEvenIfPendingKill));
}
template<class T>
inline T* GetMutableObjectForBlueprint(UBlueprint* Blueprint) const
{
return const_cast<T*>(GetObjectForBlueprint<T>(Blueprint));
}
inline UActorComponent* GetMutableComponentTemplate(bool bEvenIfPendingKill = false) const
{
return Cast<UActorComponent>(WeakObjectPtr.Get(bEvenIfPendingKill));
}
UActorComponent* FindMutableComponentInstanceInActor(const AActor* InActor) const;
////////////////////////////////////////////////////
// Anything related to an SCS node will be changed with an upcoming refactor and should remain private
private:
// Tries to find a SCS node that was likely responsible for creating the specified instance component. Note: This is not always possible to do!
static USCS_Node* FindSCSNodeForInstance(const UActorComponent* InstanceComponent, UClass* ClassToSearch);
USCS_Node* GetSCSNode(bool bEvenIfPendingKill = false) const;
/**
* Attempts to set the SCS node member variable based on the WeakObjectPtr.
* If the weak obj ptr _is_ an USCS_Node type, then we have to change the component
* template.
*
* If this is an instanced component, then we can find the scs node via FindSCSNodeForInstance.
*
* @return True if an SCS node has been set
*/
bool AttemptToSetSCSNode();
// For now, we need a pointer to the SCS node in order to have the correct component template
// and to be able to get the UBlueprint* from components. This will only be set upon construction
// if the given Object Context is an USCS_Node. The context object will be set to its template.
TWeakObjectPtr<USCS_Node> SCSNodePtr;
/**
* True if this SCS node's blueprint is a child of another Blueprint-generated class,
* which means that we must use the InheritableComponentHandler
*/
bool IsInheritedSCSNode() const;
const UChildActorComponent* GetChildActorComponent(bool bEvenIfPendingKill = false) const;
/** True if this SCS node is inherited from another blueprint generated class. */
bool bIsInheritedSubobject = false;
bool bIsInheritedSCS = false;
bool bIsChildActor = false;
};