cannot resolve error LNK2019

Hi guys, I have this lingering problem that i cannot figure out, can you please help me?
I have programmed some code for spatial hashing, but when i use the functions in the code it thows an error LNK2019 when building
I’m currently programming the player’s death through the use of an health component
I also tried to include way too many libraries, but i don’t know which ones to delete right now, so if you figure it out please tell me
Here is all the code you may need

TFSpatialHash.h

#pragma once

#include <CoreMinimal.h>
#include <Math/UnrealMathUtility.h>
#include <Math/Vector2D.h>
#include <Containers/Map.h>
#include <Containers/Array.h>
#include <GameFramework/Actor.h>

namespace Utils
{
	template<typename T, typename CellSizeType = int, typename Hash = TMap<FVector2D, T>>
	class ATTRITION_API TFSpatialHash
	{
	public:
		virtual ~TFSpatialHash() {};

		//You have to call this function
		void SetCellSize(CellSizeType CellSize);

		void Insert(const FVector2D& Point, const T Object);

		//Works only if Object is or is derived from AActor class
		void Insert(const T Actor);

		void Remove(const FVector2D& Point);

		TArray<T> Search(const FVector2D& Point, const double Range) const;
		T SearchNearest(const FVector2D& Point) const;

		void Update(const FVector2D& OldPoint, const FVector2D& NewPoint, const T Object) const;

		void Clear();

	private:
		CellSizeType CellSize_;
		Hash HashTable_;

		static FVector2D HashPoint(const FVector2D& Point);

		virtual void BeginPlay() {};
	};
}

TFSpatialHash.cpp

#include <TFSpatialHash.h>
#include <Math/Vector2D.h>
#include <Containers/Map.h>
#include <Misc/AssertionMacros.h>
#include <Math/UnrealMathUtility.h>
#include <GameFramework/Actor.h>

using namespace Utils;

template<typename T, typename CellSizeType, typename Hash>
void TFSpatialHash<T, CellSizeType, Hash>::SetCellSize(CellSizeType CellSize)
{
	CellSize_ = CellSize;
}

template <typename T, typename CellSizeType, typename Hash>
void TFSpatialHash<T, CellSizeType, Hash>::Insert(const FVector2D& Point, const T Object)
{
	const FVector2D Key = HashPoint(Point);
	HashTable_.Add(Key, Object);
}

template <typename T, typename CellSizeType, typename Hash>
void TFSpatialHash<T, CellSizeType, Hash>::Insert(const T Actor)
{
	const FVector2D Point(Actor->GetActorLocation().X, Actor->GetActorLocation().Y);

	const FVector2D Key = HashPoint(Point);
	HashTable_.Add(Key, Actor);
}

template <typename T, typename CellSizeType, typename Hash>
void TFSpatialHash<T, CellSizeType, Hash>::Remove(const FVector2D& Point)
{
	FVector2D Key = HashPoint(Point);
	HashTable_.Remove(Key);
}

template <typename T, typename CellSizeType, typename Hash>
TArray<T> TFSpatialHash<T, CellSizeType, Hash>::Search(const FVector2D& Point, const double Range) const
{
	TArray<T> Results;
	const FVector2D MinKey = HashPoint({Point.X - Range, Point.Y - Range});
	const FVector2D MaxKey = HashPoint({Point.X + Range, Point.Y + Range});

	for(double I = MinKey.X; I <= MaxKey.X; ++I)
	{
		for(double J = MinKey.Y; J <= MaxKey.Y; ++J)
		{
			FVector2D Key = FVector2D(I, J);
			const T* Object = HashTable_.Find(Key);

			if(Object && FVector2D::DistSquared(Point, Key) <= Range * Range)
			{
				Results.Add(*Object);
			}
		}
	}

	return Results;
}

template <typename T, typename CellSizeType, typename Hash>
T TFSpatialHash<T, CellSizeType, Hash>::SearchNearest(const FVector2D& Point) const
{
	T NearestObject = nullptr;
	double NearestDistance = DBL_MAX;

	for(auto& [Key, Object] : HashTable_)
	{
		if(const double Distance = FVector2D::DistSquared(Point, Key); Distance < NearestDistance)
		{
			NearestObject = &Object;
			NearestDistance = Distance;
		}
	}

	return NearestObject;
}

template <typename T, typename CellSizeType, typename Hash>
void TFSpatialHash<T, CellSizeType, Hash>::Update(const FVector2D& OldPoint, const FVector2D& NewPoint,
	const T Object) const
{
	if(OldPoint == NewPoint)
		return;

	if(HashTable_.Find(OldPoint) == &Object)
	{
		Remove(OldPoint);
		Insert(NewPoint);
	}
}

template <typename T, typename CellSizeType, typename Hash>
void TFSpatialHash<T, CellSizeType, Hash>::Clear()
{
	HashTable_.Reset();
}

template <typename T, typename CellSizeType, typename Hash>
FVector2D TFSpatialHash<T, CellSizeType, Hash>::HashPoint(const FVector2D& Point)
{
	const double X = FMath::FloorToInt(Point.X / CellSize_);
	const double Y = FMath::FloorToInt(Point.Y / CellSize_);

	return FVector2D(X, Y);
}

HealthComponent.h

#pragma once

#include <CoreMinimal.h>
#include <TFSpatialHash.h>
#include <Components/ActorComponent.h>
#include <Math/Vector2D.h>
#include <Containers/Map.h>
#include <GameFramework/Actor.h>

#include "HealthComponent.generated.h"

//using namespace UUtility;

//TODO: Add health for NPCs(mainly enemies)
//Do we need this in BP? If not, we could delete the dynamic part for some faster code
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnPlayerDiedSignature, ACharacter*, Player);

UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
class ATTRITION_API UHealthComponent : public UActorComponent
{
	GENERATED_BODY()

public:	
	//Sets default values for this component's properties
	UHealthComponent();

	UPROPERTY(BlueprintReadOnly, Category="Health|General")
	float Health;

	//Create and assign signature functions
	const FOnPlayerDiedSignature& GetOnPlayerDied() const {return OnPlayerDied;}

	UPROPERTY(BlueprintAssignable, Category="Health|Respawn|Signatures")
	FOnPlayerDiedSignature OnPlayerDied;

	//Addition to OnTakeAnyDamage Signature
	UFUNCTION()
	void OnDamage(AActor* DamagedActor, float Damage, const UDamageType* DamageType, AController* InstigatedBy, AActor* DamageCauser);

	//Handle the death events
	UFUNCTION()
	void PlayerDied(ACharacter* Player);

protected:
	//Called when the game starts
	virtual void BeginPlay() override;

	UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="Health|General")
	float MaxHealth;

	//The owner of this instance of the component
	UPROPERTY(BlueprintReadOnly, Category="Health|General")
	ACharacter* PlayerOwner;

	UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="Health|Respawn")
	float RespawnTimer;

	UPROPERTY()
	const UWorld* WorldPtr;

	UPROPERTY()
	AGameModeBase* GameMode;

	Utils::TFSpatialHash<AActor*> PlayerStartHash;
};

HealthComponent.cpp

#pragma region Includes

#include <HealthComponent.h>
#include <TFSpatialHash.h>
#include <Engine/World.h>
#include <GameFramework/Character.h>
#include <GameFramework/GameModeBase.h>
#include <GameFramework/PlayerStart.h>
#include <Kismet/GameplayStatics.h>
#include <Math/Vector2D.h>
#include <Containers/Map.h>
#include <Math/Vector.h>
#include <Math/UnrealMathUtility.h>
#include <GameFramework/Actor.h>
#include <Containers/Array.h>
#include <Misc/AssertionMacros.h>

#pragma endregion

// Sets default values for this component's properties
UHealthComponent::UHealthComponent()
{
	//Does the object need to tick?
	PrimaryComponentTick.bCanEverTick = false;

	//Populating variables
	MaxHealth = 100.0f;
	Health = MaxHealth;
	RespawnTimer = 5.0f;
}


// Called when the game starts
void UHealthComponent::BeginPlay()
{
	Super::BeginPlay();

	//Get owner of component
	WorldPtr = GetWorld();
	PlayerOwner = Cast<ACharacter>(GetOwner());
	GameMode = Cast<AGameModeBase>(WorldPtr->GetAuthGameMode());
	PlayerStartHash.SetCellSize(1000);

	//add our damage function to give the hit signature more functionality
	if(PlayerOwner)
	{
		PlayerOwner->OnTakeAnyDamage.AddDynamic(this, &UHealthComponent::OnDamage);
		UE_LOG(LogTemp, Log, TEXT("Bound 1"))
	}

	//Bind functions to the delegates
	if(!OnPlayerDied.IsBound())
	{
		OnPlayerDied.AddDynamic(this, &UHealthComponent::PlayerDied);
		UE_LOG(LogTemp, Log, TEXT("Bound 2"))
	}

	TArray<AActor*>  PlayerStartsArr;
	UGameplayStatics::GetAllActorsOfClass(WorldPtr, APlayerStart::StaticClass(), PlayerStartsArr);

	for(AActor* SpawnPoint : PlayerStartsArr)
	{
		PlayerStartHash.Insert(SpawnPoint);
	}
}

/**
 * @brief Added function to the damage event, all the parameters are needed by the signature
 */
void UHealthComponent::OnDamage(AActor* DamagedActor, float Damage, const UDamageType* DamageType, AController* InstigatedBy, AActor* DamageCauser)
{
	UE_LOG(LogTemp, Log, TEXT("Damage func called"))
	//Having negative damage doesn't make sense
	if(Damage <= 0.0f)
		return;

	//Clamp so we don't get negative health
	Health = FMath::Clamp(Health - Damage, 0.0f, MaxHealth);

	if(Health <= 0.0f && PlayerOwner)
	{
		UE_LOG(LogTemp, Log, TEXT("Death function called"))
		GetOnPlayerDied().Broadcast(PlayerOwner);
	}
}

/**
 * @brief Function to be called when player dies, connected to player died signature
 * @param Player Player to kill
 */
void UHealthComponent::PlayerDied(ACharacter* Player)
{
	UE_LOG(LogTemp, Log, TEXT("Dying"))
	//Get Player's controller
	AController* PlayerController = Player->GetController();
	const FVector2D PlayerPos = FVector2D(Player->GetActorLocation().X, Player->GetActorLocation().Z);

	GameMode->RestartPlayerAtPlayerStart(PlayerController, PlayerStartHash.SearchNearest(PlayerPos));
}

Thanks to everyone

Can you also provide the full LNK2019 error you are getting?

sorry i did not include it, here it is
i substituted the @ symbol with this | symbol bcs ue forum thought they were mentions
(p.s: some parts are in italian, if you need them translated just ask):

HealthComponent.cpp.obj : error LNK2019: riferimento al simbolo esterno “public: void __cdecl Utils::TFSpatialHash<class AActor *,int,class TMap<struct UE::Math::TVector2,class AActor *,class FDefaultSetAllocator,struct TDefaultMapHashableKeyFuncs<struct UE::Math::TVector2,class AActor *,0> > >::SetCellSize(int)” (?SetCellSize|?$TFSpatialHash|PEAVAActor||HV?$TMap|U?$TVector2|N|Math|UE||PEAVAActor||VFDefaultSetAllocator||U?$TDefaultMapHashableKeyFuncs|U?$TVector2|N|Math|UE||PEAVAActor||$0A||||||Utils||QEAAXH|Z) non risolto nella funzione “protected: virtual void __cdecl UHealthComponent::BeginPlay(void)” (?BeginPlay|UHealthComponent||MEAAXXZ)

HealthComponent.cpp.obj : error LNK2019: riferimento al simbolo esterno “public: void __cdecl Utils::TFSpatialHash<class AActor *,int,class TMap<struct UE::Math::TVector2,class AActor *,class FDefaultSetAllocator,struct TDefaultMapHashableKeyFuncs<struct UE::Math::TVector2,class AActor *,0> > >::Insert(class AActor * const)” (?Insert|?$TFSpatialHash|PEAVAActor||HV?$TMap|U?$TVector2|N|Math|UE||PEAVAActor||VFDefaultSetAllocator||U?$TDefaultMapHashableKeyFuncs|U?$TVector2|N|Math|UE||PEAVAActor||$0A||||||Utils||QEAAXQEAVAActor|||Z) non risolto nella funzione “protected: virtual void __cdecl UHealthComponent::BeginPlay(void)” (?BeginPlay|UHealthComponent||MEAAXXZ)

HealthComponent.cpp.obj : error LNK2019: riferimento al simbolo esterno "public: class AActor * __cdecl Utils::TFSpatialHash<class AActor *,int,class TMap<struct UE::Math::TVector2,class AActor *,class FDefaultSetAllocator,struct TDefaultMapHashableKeyFuncs<struct UE::Math::TVector2,class AActor *,0> > >::SearchNearest(struct UE::Math::TVector2 const &)const " (?SearchNearest|?$TFSpatialHash|PEAVAActor||HV?$TMap|U?$TVector2|N|Math|UE||PEAVAActor||VFDefaultSetAllocator||U?$TDefaultMapHashableKeyFuncs|U?$TVector2|N|Math|UE||PEAVAActor||$0A||||||Utils||QEBAPEAVAActor||AEBU?$TVector2|N|Math|UE|||Z) non risolto nella funzione “public: void __cdecl UHealthComponent::PlayerDied(class ACharacter *)” (?PlayerDied|UHealthComponent||QEAAXPEAVACharacter|||Z)

C:\Users\giugi\OneDrive\Documents\Unreal Projects\attrition\Binaries\Win64\UnrealEditor-attrition.patch_0.exe : fatal error LNK1120: 3 esterni non risolti

This other thread might be useful since it has a similar LNK2019 error in regards to TVector: Linker error when attempting to serialize FVector_NetQuantize10 in source builded ue5.0.3

Their solution was:

Someone called Laura on the UnrealSlackers Discord helped me, you need to add “NetCore” to your projects Build.cs

Does this fix your issue?

Nope (i wrote “Using NetCore” on top of the projectname.Build.Cs file, project’s name being attrition):

C:\Users\giugi\OneDrive\Documents\Unreal Projects\attrition\Source\attrition\attrition.Build.cs(4,7): error CS0246: The type or namespace name ‘NetCore’ could not be found (are you missing a using directive or an assembly reference?)
Expecting to find a type to be declared in a target rules named ‘attritionEditorTarget’. This type must derive from the ‘TargetRules’ type defined by Unreal Build Tool.

I feel very dumb but i don’t know anything about code compiling in ue :cry:

Can you paste your build.cs file?

// Copyright Epic Games, Inc. All Rights Reserved.

using UnrealBuildTool;
using NetCore;

public class attrition : ModuleRules
{
	public attrition(ReadOnlyTargetRules Target) : base(Target)
	{
		PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;

		PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "HeadMountedDisplay" });
	}
}

Instead of writing using Netcore, can you add it the PublicDependencyModuleNames?

	PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "HeadMountedDisplay", "NetCore" });

the lnk2019 error appears again, the same one

Usually when these LNK errors appear it is because you need a specific module loaded in via this build.cs file. Perhaps someone else knows which one you need :confused:

EDIT: You might need to regenerate project files after updating your build.cs file and then trying to recompile.

2 Likes

You (generally) can’t separate template class into .h & .cpp.

Write all your code in .h file and everything goes fine.

I just learnt about 2 words of italian but I see you using ACharacter in the header file without a forward declaration or header include. if a forward doesn’t do the job then move the ACharacter include to the .h file and see if something changes.

Sorry for the late response;

This has worked to delete the lnk2019 error, but now i’m faced with a new error ( i coded something wrogn, but, since we’re here, i will ask you for your attention one last time)
The error is this:

C:\Users\giugi\OneDrive\Documents\Unreal Projects\attrition\Source\attrition\Public\TFSpatialHash.h(76): error C2440: ‘=’: unable to convert from ‘const T *’ to ‘T’
with
[
T=AActor *
]
C:\Users\giugi\OneDrive\Documents\Unreal Projects\attrition\Source\attrition\Public\TFSpatialHash.h(76): note: Types shown are not related. Conversion requires reinterpret_cast, C-type cast, or function-type cast in parenthesis
C:\Users\giugi\OneDrive\Documents\Unreal Projects\attrition\Source\attrition\Public\TFSpatialHash.h(68): note: while compiling the member function ‘T
Utils::TFSpatialHash<T,int,TMap<FVector2D,T,FDefaultSetAllocator,TDefaultMapHashableKeyFuncs<InKeyType,InValueType,false>>>::SearchNearest(const FVector2D &) const’ of model class
with
[
T=AActor *,
InKeyType=FVector2D,
InValueType=AActor *
]
C:\Users\giugi\OneDrive\Documents\Unreal Projects\attrition\Source\attrition\Private\HealthComponent.cpp(103): note: see instance reference ‘T
Utils::TFSpatialHash<T,int,TMap<FVector2D,T,FDefaultSetAllocator,TDefaultMapHashableKeyFuncs<InKeyType,InValueType,false>>>::SearchNearest(const FVector2D &) const’ of the template function being compiled
with
[
T=AActor *,
InKeyType=FVector2D,
InValueType=AActor *
]
C:\Users\giugi\OneDrive\Documents\Unreal Projects\attrition\Source\attrition\Public\HealthComponent.h(66): Notes: See the instance reference ‘Utils::TFSpatialHash<AActor *,int,TMap<FVector2D,T,FDefaultSetAllocator,TDefaultMapHashableKeyFuncs<InKeyType,InValueType,false>>>’
of model class that is being compiled
with
[
T=AActor *,
InKeyType=FVector2D,
InValueType=AActor *
]

Translation done by me at midnight so errors may appear
This is the TFSpatialHash.h file (The one that is having compiler errors)

// Copyright LinearGames. All rights reserved.
#pragma once

#include <CoreMinimal.h>
#include <Math/UnrealMathUtility.h>
#include <Math/Vector2D.h>
#include <Containers/Map.h>
#include <Containers/Array.h>
#include <GameFramework/Actor.h>

namespace Utils
{
	template<typename T, typename CellSizeType = int, typename Hash = TMap<FVector2D, T>>
	class ATTRITION_API TFSpatialHash final
	{
	public:
		//You have to call this function
		void SetCellSize(CellSizeType CellSize)
		{
			CellSize_ = CellSize;
		};

		void Insert(const FVector2D& Point, const T Object)
		{
			const FVector2D Key = HashPoint(Point);
			HashTable_.Add(Key, Object);
		}

		//Works only if Object is or is derived from AActor class
		void Insert(const T Actor)
		{
			const FVector2D Point(Actor->GetActorLocation().X, Actor->GetActorLocation().Y);

			const FVector2D Key = HashPoint(Point);
			HashTable_.Add(Key, Actor);
		}

		void Remove(const FVector2D& Point)
		{
			FVector2D Key = HashPoint(Point);
			HashTable_.Remove(Key);
		}

		TArray<T> Search(const FVector2D& Point, const double Range) const
		{
			TArray<T> Results;
			const FVector2D MinKey = HashPoint({Point.X - Range, Point.Y - Range});
			const FVector2D MaxKey = HashPoint({Point.X + Range, Point.Y + Range});

			for(double I = MinKey.X; I <= MaxKey.X; ++I)
			{
				for(double J = MinKey.Y; J <= MaxKey.Y; ++J)
				{
					FVector2D Key = FVector2D(I, J);
					const T* Object = HashTable_.Find(Key);

					if(Object && FVector2D::DistSquared(Point, Key) <= Range * Range)
					{
						Results.Add(*Object);
					}
				}
			}

			return Results;
		}

		T SearchNearest(const FVector2D& Point) const
		{
			T NearestObject = nullptr;
			double NearestDistance = DBL_MAX;

			for(auto& [Key, Object] : HashTable_)
			{
				if(const double Distance = FVector2D::DistSquared(Point, Key); Distance < NearestDistance)
				{
					NearestObject = Cast<AActor*>(&Object);
					NearestDistance = Distance;
				}
			}

			return NearestObject;
		}

		void Update(const FVector2D& OldPoint, const FVector2D& NewPoint, const T Object) const
		{
			if(OldPoint == NewPoint) return;

			if(HashTable_.Find(OldPoint) == &Object)
			{
				Remove(OldPoint);
				Insert(NewPoint);
			}
		}

		void Clear()
		{
			HashTable_.Reset();
		}

	private:
		CellSizeType CellSize_;
		Hash HashTable_;

		static FVector2D HashPoint(const FVector2D& Point)
		{
			const double X = FMath::FloorToInt(Point.X / CellSize_);
			const double Y = FMath::FloorToInt(Point.Y / CellSize_);

			return FVector2D(X, Y);
		}

		virtual void BeginPlay() {};
	};
}

Many thanks for your help again, i’m new in the coding community

T is little misleading, because it’s not clear at first sight is this represent a type or pointer-to-type.

In SearchNearest function you have:
T NearestObject = nullptr // implies: T = pointer
for (auto& [Key, Object] = Hash_Table_) // Object = T = pointer
NearestObject = Cast<AActor*>(&Object) // &Object = pointer-to-pointer

Best way is to use T = Type and if you want a pointer then use T*

So you think i should get rid of the templates?

That’s not really what I meant.

Although actually, I don’t see much benefits from using templates here. You only use one method from T (GetActorLocation) and in one place you explicitly cast pointer to the AActor*.

So your template will only work for types that inherit from the AActor class. Removing the template and keeping a simple TArray<AActor*> seems enough.

Apart from these last problems, the error LNK2019 has been solved, so if i’m going to need help you will hear from me again, but i’m “closing” this thread (just marking the answer in reality).
Thanks to everyone