I’m following a tutorial for setting up mass in my project, and I’m getting this error for a couple of my .cpp files:
0>EnemyDeathProcessor.cpp.obj: Error LNK2019 : unresolved external symbol “__declspec(dllimport) class UScriptStruct * __cdecl Z_Construct_UScriptStruct_FMassElement(enum ETypeConstructPhase)” (_imp?Z_Construct_UScriptStruct_FMassElement@@YAPEAVUScriptStruct@@W4ETypeConstructPhase@@ @Z) referenced in function “auto * __cdecl UE::StructUtils::GetAsUStruct(void)” (??$GetAsUStruct@UFMassElement@@@StructUtils@UE@@YAPEA_PXZ)
The code for the death processor, for reference:
#include "EnemyDeathProcessor.h"
#include "EnemyMassFragments.h"
#include "MassRepresentationFragments.h"
UEnemyDeathProcessor::UEnemyDeathProcessor() : EntityQuery(*this)
{
ExecutionFlags = static_cast<uint8>(EProcessorExecutionFlags::All & ~EProcessorExecutionFlags::Client);
}
void UEnemyDeathProcessor::ConfigureQueries(const TSharedRef<FMassEntityManager>& EntityManager)
{
// death processor should be able to read and write the death fragment
EntityQuery.AddRequirement<FEnemyDeathFragment>(EMassFragmentAccess::ReadWrite);
// as long as the entity is alive, its visualization will keep spawning an actor when close to the player
EntityQuery.AddRequirement<FMassRepresentationFragment>(EMassFragmentAccess::ReadOnly);
}
void UEnemyDeathProcessor::Execute(FMassEntityManager& EntityManager, FMassExecutionContext& Context)
{
EntityQuery.ForEachEntityChunk(Context,[](FMassExecutionContext& Ctx)
{
// access the fragments used
const auto Deaths = Ctx.GetMutableFragmentView<FEnemyDeathFragment>();
// because read only fragments cant be modified, have to access it as an array instead of a mutable
const auto Reps = Ctx.GetFragmentView<FMassRepresentationFragment>();
// iterate through fragment
for (int i = 0; i < Ctx.GetNumEntities(); ++i)
{
// check if entity is currently representing an actor
const bool bAlreadySpawnedActor =
Reps[i].CurrentRepresentation == EMassRepresentationType::HighResSpawnedActor ||
Reps[i].CurrentRepresentation == EMassRepresentationType::LowResSpawnedActor;
// if an already dead entity no longer represents an actor, request it be destroyed
if (!bAlreadySpawnedActor)
{
Ctx.Defer().DestroyEntity(Ctx.GetEntity(i));
continue;
}
// make entity destroy itself when specified ttl expires
Deaths[i].TTL -= Ctx.GetDeltaTimeSeconds();
if (Deaths[i].TTL <= 0.f)
{
Ctx.Defer().DestroyEntity(Ctx.GetEntity(i));
}
}
});
}
I know this error can happen when you’re missing a dependency in your build.cs file, but I’m not sure what dependency I’m missing.
These are the dependencies I currently have:
PrivateDependencyModuleNames.AddRange(new string[]
{
"GameplayAbilities", "GameplayTasks", "GameplayTags",
"DeveloperSettings", "AIModule", "NavigationSystem", "MassEntity", "MassCommon", "MassActors", "MassSpawner",
"MassLOD", "MassRepresentation"
});
The possibilities I can think of:
The first is that I think you may need Core and CoreUObject in the dependencies. I think this probably isn’t it though.
The second would be when you’re working with multiple modules in one project. Then you might be able to see this when referencing something in module A from module B. In that case you’d have to add module A to the dependencies in module B. (In your case, module A would be whatever contains FMassElement, and module B would be whatever contains EnemyDeathProcessor.cpp.)
Or perhaps FMassElement isn’t exported (you don’t have appropriate the XXX_API macro in the declaration, and it’s not annotated as MinimalAPI in the USTRUCT()). This could happen if you’re using it from another module.
Your dependency list looks fine for the code you posted, MassEntity and MassRepresentation cover both fragments in that query, and nothing else in there needs adding. So I don’t think a missing module is what’s biting you.
The dllimport part of the error is something to look into. That gets determined at compile time based on the _API macro used in the struct declaration. In your own module, the macro expands to dllexport; in every other module, it expands to dllimport.
So in this case, the compiler has been told that FMassElement exists in another DLL and should be imported from there. The problem is that nothing is actually exporting it.
Because of that, this is probably not a missing module dependency issue. If the struct wasn’t declared at all, you’d be getting a compile error instead. Likewise, if the struct was declared in your own module using your project’s _API macro, it would have been exported correctly.
I’d check EnemyMassFragments.h and look at the struct declaration. A common cause is accidentally using an API macro from another module, so something like struct MASSENTITY_API FMassElement when it should be your project’s own macro, or no macro at all. Easy to end up with if the tutorial kept its fragments in a plugin.
As a quick sanity check, search for Z_Construct_UScriptStruct_FMassElement in:
Intermediate/Build/Win64/x64/[YourTarget]/Development/[YourModule]/UHT/EnemyMassFragments.gen.cpp
If the definition is there, the struct itself is generating fine and the API macro is your problem.
Here’s EnemyMassFragments.h:
#pragma once
#include "CoreMinimal.h"
#include "MassEntityElementTypes.h"
#include "EnemyMassFragments.generated.h"
// fragment for enemy to search its surroundings
USTRUCT()
struct FEnemyWanderFragment : public FMassFragment
{
GENERATED_BODY()
UPROPERTY(EditAnywhere)
// origin is the center of the radius that the entity will search
FVector Origin = FVector::ZeroVector;
// target point
UPROPERTY(EditAnywhere)
FVector TargetLocation = FVector::ZeroVector;
// time until selecting a new target point
UPROPERTY(EditAnywhere)
float TimeUntilNewTarget = 0.f;
// radius and speed might have to be moved outside of the fragment for performance
UPROPERTY(EditAnywhere)
float Radius = 1000.f;
UPROPERTY(EditAnywhere)
float Speed = 300.f;
};
// state data for enemy
USTRUCT()
struct FEnemyStatusFragment : public FMassFragment
{
GENERATED_BODY()
UPROPERTY(EditAnywhere)
float HealthPercent = 1.f;
};
USTRUCT()
struct FEnemyDeathFragment : public FMassFragment
{
GENERATED_BODY()
UPROPERTY(EditAnywhere)
float TTL = 5.f;
};
I’m new to c++, so I might be missing something, but I don’t think I used a wrong API macro? I get an error when removing public FMassFragment.
Z_Construct_UScriptStruct_FMassElement was not in EnemyMassFragments.gen.cpp, so I assume the struct isn’t generating?
Oh, I misunderstood what an API macro is. It looks like I’m not using one at all.
Any ideas why the struct wouldn’t be generating?
I do have Core and CoreUObject in the dependencies, they’re just on a different line and I forgot to copy them.
What would the syntax be for including the API macro? I’ve been looking at the documentation, but I can’t seem to find how to include it.
Something like this:
struct YOURMODULENAME_API FYourStructName {
};
You’ve probably used it on other classes – a good place to check would be your player controller subclass, since that’s usually part of the template and would definitely have the API macro applied to it. Any class you create with the class wizard would also have it, unless you manually removed it.
As mentioned above, this kind of error could happen if the API macro is missing, but it could also happen if the macro refers to a different module than the one the class is defined in.
I figured it out! MassEntityElementTypes.h has been moved to Mass/EntityElementTypes.h, so I had to change the includes and include MassCore in my build.cs!