Subclassing UPhysicsAsset causes linker errors

When subclassing UPhysicsAsset I get linker errors where it can’t export the virtual functions which UPhysicsAsset overrides from UObject and IPreviewMeshProviderInterface.

Please help me understand how to subclass this class. I also need to subclass other engine classes like UBodySetup also and hopefully the answer to this applies to the others.

These are the functions from UPhysicsAsset:

//~ Begin UObject Interface
virtual void Serialize(FArchive& Ar) override;
virtual void PostLoad() override;
static void DeclareConstructClasses(TArray<FTopLevelAssetPath>& OutConstructClasses, const UClass* SpecificSubclass);
virtual FString GetDesc() override;
virtual void GetAssetRegistryTags(TArray<FAssetRegistryTag>& OutTags) const override;
virtual void GetResourceSizeEx(FResourceSizeEx& CumulativeResourceSize) override;
virtual void PostEditUndo() override;
virtual void PreEditChange(FProperty* PropertyThatWillChange) override;
virtual void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) override;
virtual EDataValidationResult IsDataValid(TArray<FText>& ValidationErrors) override;

/** IPreviewMeshProviderInterface interface */
virtual void SetPreviewMesh(USkeletalMesh* PreviewMesh, bool bMarkAsDirty = true);
virtual USkeletalMesh* GetPreviewMesh() const;

These are the link error messages:

1>Module.MjPhysicsAsset.cpp.obj : error LNK2001: unresolved external symbol “public: virtual void __cdecl UPhysicsAsset::Serialize(class FArchive &)” (?Serialize@UPhysicsAsset@@UEAAXAEAVFArchive@@@Z)
1>Module.MjPhysicsAsset.cpp.obj : error LNK2001: unresolved external symbol “public: virtual void __cdecl UPhysicsAsset::PostLoad(void)” (?PostLoad@UPhysicsAsset@@UEAAXXZ)
1>Module.MjPhysicsAsset.cpp.obj : error LNK2019: unresolved external symbol “public: static void __cdecl UPhysicsAsset::DeclareConstructClasses(class TArray<struct FTopLevelAssetPath,class TSizedDefaultAllocator<32> > &,class UClass const *)” (?DeclareConstructClasses@UPhysicsAsset@@SAXAEAV?$TArray@UFTopLevelAssetPath@@V?$TSizedDefaultAllocator@$0CA@@@@@PEBVUClass@@@Z) referenced in function “private: static class UClass * __cdecl UMyPhysicsAsset::GetPrivateStaticClass(void)” (?GetPrivateStaticClass@UMyPhysicsAsset@@CAPEAVUClass@@XZ)
1>Module.MjPhysicsAsset.cpp.obj : error LNK2001: unresolved external symbol “public: virtual class FString __cdecl UPhysicsAsset::GetDesc(void)” (?GetDesc@UPhysicsAsset@@UEAA?AVFString@@XZ)
1>Module.MjPhysicsAsset.cpp.obj : error LNK2001: unresolved external symbol "public: virtual void __cdecl UPhysicsAsset::GetAssetRegistryTags(class TArray<struct UObject::FAssetRegistryTag,class TSizedDefaultAllocator<32> > &)const " (?GetAssetRegistryTags@UPhysicsAsset@@UEBAXAEAV?$TArray@UFAssetRegistryTag@UObject@@V?$TSizedDefaultAllocator@$0CA@@@@@@Z)
1>Module.MjPhysicsAsset.cpp.obj : error LNK2001: unresolved external symbol “public: virtual void __cdecl UPhysicsAsset::GetResourceSizeEx(struct FResourceSizeEx &)” (?GetResourceSizeEx@UPhysicsAsset@@UEAAXAEAUFResourceSizeEx@@@Z)
1>Module.MjPhysicsAsset.cpp.obj : error LNK2001: unresolved external symbol “public: virtual void __cdecl UPhysicsAsset::PostEditUndo(void)” (?PostEditUndo@UPhysicsAsset@@UEAAXXZ)
1>Module.MjPhysicsAsset.cpp.obj : error LNK2001: unresolved external symbol “public: virtual void __cdecl UPhysicsAsset::PreEditChange(class FProperty *)” (?PreEditChange@UPhysicsAsset@@UEAAXPEAVFProperty@@@Z)
1>Module.MjPhysicsAsset.cpp.obj : error LNK2001: unresolved external symbol “public: virtual void __cdecl UPhysicsAsset::PostEditChangeProperty(struct FPropertyChangedEvent &)” (?PostEditChangeProperty@UPhysicsAsset@@UEAAXAEAUFPropertyChangedEvent@@@Z)
1>Module.MjPhysicsAsset.cpp.obj : error LNK2001: unresolved external symbol “public: virtual enum EDataValidationResult __cdecl UPhysicsAsset::IsDataValid(class TArray<class FText,class TSizedDefaultAllocator<32> > &)” (?IsDataValid@UPhysicsAsset@@UEAA?AW4EDataValidationResult@@AEAV?$TArray@VFText@@V?$TSizedDefaultAllocator@$0CA@@@@@@Z)
1>Module.MjPhysicsAsset.cpp.obj : error LNK2001: unresolved external symbol “public: virtual void __cdecl UPhysicsAsset::SetPreviewMesh(class USkeletalMesh *,bool)” (?SetPreviewMesh@UPhysicsAsset@@UEAAXPEAVUSkeletalMesh@@_N@Z)
1>Module.MjPhysicsAsset.cpp.obj : error LNK2001: unresolved external symbol "public: virtual class USkeletalMesh * __cdecl UPhysicsAsset::GetPreviewMesh(void)const " (?GetPreviewMesh@UPhysicsAsset@@UEBAPEAVUSkeletalMesh@@XZ)

header

#pragma once

#include "CoreMinimal.h"
#include "PhysicsEngine/PhysicsAsset.h"
#include "MyPhysicsAsset.generated.h"

/**
 * 
 */
UCLASS()
class PA_API UMyPhysicsAsset : public UPhysicsAsset
{
	GENERATED_BODY()

public:
		UMyPhysicsAsset(const FObjectInitializer& ObjectInitializer);
	
	virtual void Serialize(FArchive& Ar) override;
	virtual void PostLoad() override;
	virtual FString GetDesc() override;
	virtual void GetAssetRegistryTags(TArray<FAssetRegistryTag>& OutTags) const override;
	virtual void GetResourceSizeEx(FResourceSizeEx& CumulativeResourceSize) override;
		
#if WITH_EDITOR
	virtual void PostEditUndo() override;
	virtual void PreEditChange(FProperty* PropertyThatWillChange) override;
	virtual void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) override;
	virtual EDataValidationResult IsDataValid(class FDataValidationContext& Context) const override;
#endif

#if WITH_EDITORONLY_DATA
	static void DeclareConstructClasses(TArray<FTopLevelAssetPath>& OutConstructClasses, const UClass* SpecificSubclass);
#endif
	virtual void SetPreviewMesh(USkeletalMesh* PreviewMesh, bool bMarkAsDirty = true);
	virtual USkeletalMesh* GetPreviewMesh() const;
};

.cpp

#include "MyPhysicsAsset.h"

#include "Animation/MirrorDataTable.h"
#include "Engine/SkinnedAsset.h"
#include "UObject/FrameworkObjectVersion.h"
#include "Serialization/ObjectWriter.h"
#include "Serialization/ObjectReader.h"
#include "Components/SkeletalMeshComponent.h"
#include "PhysicsEngine/PhysicsConstraintTemplate.h"
#include "UObject/ReleaseObjectVersion.h"
#include "UObject/UObjectIterator.h"
#include "UObject/FortniteSeasonBranchObjectVersion.h"

//#include UE_INLINE_GENERATED_CPP_BY_NAME(PhysicsAsset)

#if WITH_EDITOR
#include "Misc/MessageDialog.h"
#endif

#define LOCTEXT_NAMESPACE "PhysicsAsset"


UMyPhysicsAsset::UMyPhysicsAsset(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	
}

void UMyPhysicsAsset::Serialize(FArchive& Ar)
{
	//Super::Serialize(Ar);
}

void UMyPhysicsAsset::PostLoad()
{
	//Super::PostLoad();
}

FString UMyPhysicsAsset::GetDesc()
{	
	return FString(); // Super::GetDesc();
}

void UMyPhysicsAsset::GetAssetRegistryTags(TArray<FAssetRegistryTag>& OutTags) const
{
	//return Super::GetAssetRegistryTags(OutTags);
}

void UMyPhysicsAsset::GetResourceSizeEx(FResourceSizeEx& CumulativeResourceSize)
{
	//Super::GetResourceSizeEx(CumulativeResourceSize);
}

void UMyPhysicsAsset::PreEditChange(FProperty* PropertyThatWillChange)
{
//	Super::PreEditChange(PropertyThatWillChange);
}

void UMyPhysicsAsset::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
//	Super::PostEditChangeProperty(PropertyChangedEvent);
}

/**/
EDataValidationResult UMyPhysicsAsset::IsDataValid(FDataValidationContext& Context) const
{
	return EDataValidationResult(); //  Super::IsDataValid(Context);
}

void UMyPhysicsAsset::PostEditUndo()
{
//	Super::PostEditUndo();
}

void UMyPhysicsAsset::DeclareConstructClasses(TArray<FTopLevelAssetPath>& OutConstructClasses, const UClass* SpecificSubclass)
{
//	Super::DeclareConstructClasses(OutConstructClasses, SpecificSubclass);
}

void UMyPhysicsAsset::SetPreviewMesh(USkeletalMesh* PreviewMesh, bool bMarkAsDirty)
{
	//Super::SetPreviewMesh(PreviewMesh, bMarkAsDirty);
}

USkeletalMesh* UMyPhysicsAsset::GetPreviewMesh() const
{
	return nullptr; // Super::GetPreviewMesh();
}

Unfortunately calling any super function gives problems, as if a module is missing. (relies on “Engine” that is in my build file)

If may be due to
#include UE_INLINE_GENERATED_CPP_BY_NAME(PhysicsAsset)
not working?

I was able to create a blueprint adding blueprintable but there seems to be a validation error on compile (not much information thrown there)

Output log

AssetCheck: /Game/BP_UMyPhysicsAsset contains invalid data.
AssetCheck: Data validation FAILED.
AssetCheck: Files Checked: 1, Passed: 0, Failed: 1, Skipped: 0, Unable to validate: 0

cpp definitions from PhysicsAsset.cpp can be copied with omitting super calls but that seems like a bad idea.

It’s strange that PhysicsAsset.h does not have an exposed initializer (only in the cpp)

Hmm… that’s odd. How would I expose the initializer? Would a small modification and a pull request suffice for all further classes I want to subclass to be supported in future releases? I read somewhere that when a user was trying to subclass the capsule component, the issue was that the class itself was missing the ENGINE_API from the necessary member functions. Could that be the same case for these particular functions?

Inside of UPhysicsAsset there are numerous uses of the namespace ENGINE_API. I’m guessing it might be missing some form of initialization besides the

		UPhysicsAsset (const FObjectInitializer& ObjectInitializer);

as that should be called during

UMyPhysicsAsset::UMyPhysicsAsset(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer){}

Its a pretty strange, and the types of error messages are vague at best.

Also stepping through the code adding breakpoints to both classes contructors I can confirm that UMyPhysicsAsset calls UPhysicsAsset in it’s super part so it should be constructed!

there is a message that stands out
my local path to project\MyPhysicsAsset.cpp’ is looking for a generated cpp with named ‘PhysicsAsset.gen.cpp’

Perhaps PhysicsAsset needs it’s generated version for this to work?

I have it in my source compiled version of the engine
ue_5_3_2_Source\Engine\Intermediate\Build\Win64\UnrealServer\Inc\Engine\UHT\PhysicsAsset.gen.cpp
though the compile should resolve this on it’s own during the build process :confused:

Hmm Subclassing UAnimationAsset I see that it works flawlessly without having to manually make overrides and that the Super call also works. The difference between the UAnimationAsset and the UPhysicsAsset is that the same functions used for the UObject Interface are set with the ENGINE_API macro.

UAnimationAsset Header:

ENGINE_API virtual void Serialize(FArchive& Ar) override;

/** Get available Metadata within the animation asset
 */
const TArray<UAnimMetaData*>& GetMetaData() const { return MetaData; }

/** Returns the first metadata of the specified class */
ENGINE_API UAnimMetaData* FindMetaDataByClass(const TSubclassOf<UAnimMetaData> MetaDataClass) const;

/** Templatized version of FindMetaDataByClass that handles casting for you */
template<class T>
T* FindMetaDataByClass() const
{
	static_assert(TPointerIsConvertibleFromTo<T, const UAnimMetaData>::Value, "'T' template parameter to FindMetaDataByClass must be derived from UAnimMetaData");

	return (T*)FindMetaDataByClass(T::StaticClass());
}

ENGINE_API void AddMetaData(UAnimMetaData* MetaDataInstance); 
void EmptyMetaData() { MetaData.Empty(); }	
ENGINE_API void RemoveMetaData(UAnimMetaData* MetaDataInstance);
ENGINE_API void RemoveMetaData(TArrayView<UAnimMetaData*> MetaDataInstances);

/** IInterface_PreviewMeshProvider interface */
ENGINE_API virtual void SetPreviewMesh(USkeletalMesh* PreviewMesh, bool bMarkAsDirty = true) override;
ENGINE_API virtual USkeletalMesh* GetPreviewMesh(bool bFindIfNotSet = false) override;
ENGINE_API virtual USkeletalMesh* GetPreviewMesh() const override;

Then the UAnimationAsset also has the same include line so I am sure it must be the lack of ENGINE_API macros on those UObject interface functions.
UAnimationAsset Source:

include UE_INLINE_GENERATED_CPP_BY_NAME(AnimationAsset)

I’ll try to build Unreal Engine from source to try testing it. Otherwise if you could validate my findings it would be helpful. There may be some other differences too. Thanks!

Well the UPhysicsAsset is only ever created from with in the editor from within a skeletal mesh panel. Even if you create an instance “by hand” there are very little you can change about it. You can’t for instance choose the parent class as you can with other blueprint classes.

So this might be an engine limitation (maybe some form of low level optimization), as it is a physics object that may run on different logic (a separate physics thread decoupled from the main game thread).

I’ve tried building the project based on a source version of 5.3.2 and it spits out the same errors :frowning:

Ah, so… I am overhauling the entire physics system so that I can use a research based physics engine to perform AI training of accurately simulated characters. I am trying to get the base UPhysicsAsset to incorporate with the asset I am already making. I also have the asset editor semi built and the component which goes along with it etc. So there’s more going on behind the scenes. I would like to be able to use the classes in the Physics engine line that inherits from UObject which is the main motivation for solving why I can’t inherit these classes. Though since you modified and build UE5.3.2 already it looks like there’s more to the issue. I’m gonna try looking into it more, gonna grab me a source UE5 now. Thanks.

I found this information on the forum.

and unfortunately the UPhysicsAsset is declared MinimalAPI. So you’d need to change it in source to be able to inherit from it :frowning:

That’s funny. The fact that I can click on the new class wizard and then make a new class derived from UPhysicsAsset even though MinimalAPI is specified is very confusing. Oh wells, I have UE 5 source available now and so I guess its gonna be a branch for the physics engine I’ll be using. Well lets see if it works when I get back to it after work.

Oh yay! Thank you so much! I figured it out. Looking at the UAnimationAsset I noticed it also had the MinimalAPI flag but was still subclassing. So I added the ENGINE_API macro to the functions that had linker issues in UPhysicsAsset and that solved the export problem! Requires modification of Unreal Engine Source though. But hey hopefully a game with fully simulated ai characters comes in the near future!

//~ Begin UObject Interface
ENGINE_API virtual void Serialize(FArchive& Ar) override;
ENGINE_API virtual void PostLoad() override;

#if WITH_EDITORONLY_DATA
ENGINE_API static void DeclareConstructClasses(TArray& OutConstructClasses, const UClass* SpecificSubclass);
#endif
ENGINE_API virtual FString GetDesc() override;
ENGINE_API virtual void GetAssetRegistryTags(TArray& OutTags) const override;
ENGINE_API virtual void GetResourceSizeEx(FResourceSizeEx& CumulativeResourceSize) override;
#if WITH_EDITOR
ENGINE_API virtual void PostEditUndo() override;

const TArray<FName>& GetPhysicalAnimationProfileNames() const
{
	return PhysicalAnimationProfiles;
}

const TArray<FName>& GetConstraintProfileNames() const
{
	return ConstraintProfiles;
}

ENGINE_API virtual void PreEditChange(FProperty* PropertyThatWillChange) override;
ENGINE_API virtual void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) override;
ENGINE_API virtual EDataValidationResult IsDataValid(class FDataValidationContext& Context) const override;

#endif
/** IPreviewMeshProviderInterface interface /
ENGINE_API virtual void SetPreviewMesh(USkeletalMesh
PreviewMesh, bool bMarkAsDirty = true);
ENGINE_API virtual USkeletalMesh* GetPreviewMesh() const;

2 Likes

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.