Threading Issue Causing Crash when Calling Function from Another Class

Hello everyone,

I’ve been struggling to get a basic weapon system going in c++. I’m very new to working with the language and have been following various tutorials and documentation to learn the process. I’m currently having an issue with the editor crashing when I call the FireWeapon() function in the PrimaryCharacter.cpp. Apologies for the long post with the code and all but I really don’t know what it could be as I’m not familiar with threading all too well.

EDIT: I managed to get to the point where I know that any “If” statement found in weapon.cpp’s Fire() function will cause the engine to crash. I just don’t understand what the solution is.

The Error:

Dump Summary
------------
Dump File:	UE4Minidump.dmp : D:\Libraries\Game Development\Unreal Projects\HaloHorror\Saved\Logs\UE4CC-Windows-013F007F4EE5F1612F476C9AC88BC926_0001\UE4Minidump.dmp
Last Write Time:	5/8/2017 4:14:08 PM
Process Name:	UE4Editor.exe : D:\Programs(x86)\Epic Games\UE_4.15\Engine\Binaries\Win64\UE4Editor.exe
Process Architecture:	x64
Exception Code:	0xC0000005
Exception Information:	The thread tried to read from or write to a virtual address for which it does not have the appropriate access.
Heap Information:	Not Present

PrimaryCharacter.cpp code:
// Fill out your copyright notice in the Description page of Project Settings.

#include "HaloHorror.h"
#include "PrimaryCharacter.h"


// Sets default values
APrimaryCharacter::APrimaryCharacter()
{

	//USED TO SPAWN DEFAULT WEAPON IN PLAYER'S HANDS (TYPICALLY WILL BE THE PISTOL)//
	static ConstructorHelpers::FObjectFinder<UBlueprint> weaponBlueprint(TEXT("Blueprint'/Game/Weapons/TestWeapon.TestWeapon'"));
	weaponSpawn = NULL; //nullify weaponSpawn prior to attaching blueprint

	if (weaponBlueprint.Succeeded()) {
		weaponSpawn = (UClass*)weaponBlueprint.Object->GeneratedClass;
	}

	// Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

	//Object focusing and interacting
	Reach = 250;
	objectInFocus = "";
	//Default player state
	canJump = true;

	//movementSpeeds
	defaultMovementSpeed = 500;
	injuredMovementSpeed = 150;
	sprintMovementSpeed = 700;
	//sets movementSpeed to default
	GetCharacterMovement()->MaxWalkSpeed = defaultMovementSpeed;

	//Building character. Creating camera component
	defaultCollisionComp = CreateDefaultSubobject<UBoxComponent>(TEXT("defaultCollisionComp"));
	defaultCollisionComp->AttachToComponent(RootComponent, FAttachmentTransformRules::SnapToTargetNotIncludingScale);
	defaultPlayerMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("defaultMesh"));
	defaultPlayerMesh->AttachToComponent(RootComponent, FAttachmentTransformRules::SnapToTargetNotIncludingScale, NAME_None);
	FirstPersonCameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("FirstPersonCamera"));
	FirstPersonCameraComponent->AttachToComponent(RootComponent, FAttachmentTransformRules::SnapToTargetNotIncludingScale);
}


// Called when the game starts or when spawned
void APrimaryCharacter::BeginPlay()
{
	Super::BeginPlay();
	GetCharacterMovement()->MaxWalkSpeed = defaultMovementSpeed;

	//spawns default weapon and attaches to the player//
	FActorSpawnParameters spawnParams;
	spawnParams.Owner = this;
	spawnParams.Instigator = Instigator;
	AWeapon*Spawner = GetWorld()->SpawnActor<AWeapon>(weaponSpawn, spawnParams);

	if (Spawner) {
		Spawner->AttachToComponent(defaultPlayerMesh, FAttachmentTransformRules::SnapToTargetNotIncludingScale, "weaponSocket");
	}

}

// Called every frame
void APrimaryCharacter::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);
	CheckForInteractables();
}

// Called to bind functionality to input
void APrimaryCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);
	InputComponent->BindAxis("MoveForward", this, &APrimaryCharacter::MoveForward);
	InputComponent->BindAxis("MoveRight", this, &APrimaryCharacter::MoveRight);
	InputComponent->BindAxis("LookYaw", this, &APrimaryCharacter::LookYaw);
	InputComponent->BindAxis("LookPitch", this, &APrimaryCharacter::LookPitch);
	InputComponent->BindAction("Use", IE_Pressed, this, &APrimaryCharacter::Use);
	InputComponent->BindAction("StartJump", IE_Pressed, this, &APrimaryCharacter::StartJump);
	InputComponent->BindAction("FireWeapon", IE_Pressed, this, &APrimaryCharacter::FireWeapon);
}


// INPUT FUNCTIONALITY

void APrimaryCharacter::FireWeapon() {
	GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Black, "OnFire Event Triggered");
	currentWeapon->Fire();
}

//Checks environement for interactables
void APrimaryCharacter::CheckForInteractables() {
	//Creating FHitResult variable and setting up trace vectors.
	FHitResult LinetraceHit;
	StartTrace = FirstPersonCameraComponent->GetComponentLocation();
	EndTrace = (FirstPersonCameraComponent->GetForwardVector() * Reach) + StartTrace;
	//Used to ignore potential collision with playercharacter
	FCollisionQueryParams CQP;
	CQP.AddIgnoredActor(this);
	//begin the trace
	GetWorld()->LineTraceSingleByChannel(LinetraceHit, StartTrace, EndTrace, ECollisionChannel::ECC_WorldDynamic, CQP);
	//store hit result as potential object to be worked with
	AInteractableObject* PotentialObject = Cast<AInteractableObject>(LinetraceHit.GetActor());
	//checks to see if there was a hit. If not, sets currentobject to nullptr.
	if (PotentialObject == NULL) {
		CurrentObject = nullptr;
		objectInFocus = "";
		return;
	}
	//if hit successful, store object as currentobject, get object's name, print name to screen and store name in string objectInFocus.
	else {
		CurrentObject = PotentialObject;
		CurrentObject->GetName();
		GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, *CurrentObject->GetName());
		objectInFocus = *CurrentObject->GetName();
	}


}

and the class that it’s calling when it crashes is from weapon.cpp:

// Fill out your copyright notice in the Description page of Project Settings.

#include "HaloHorror.h"
#include "Weapon.h"

// Sets default values
AWeapon::AWeapon()
{

	collisionComp = CreateDefaultSubobject<UBoxComponent>(TEXT("collisionComp"));
	RootComponent = collisionComp;

	weaponMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("weaponMesh"));
	weaponMesh->AttachToComponent(RootComponent, FAttachmentTransformRules::SnapToTargetNotIncludingScale);

}


void AWeapon::Fire() {
	if (projectileType = EWeaponProjectile::EBullet) {
		GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Black, "Bullet");
		InstantFire();
	}
	if (projectileType = EWeaponProjectile::ESpread) {
		GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Black, "Spread");
		for (int32 i = 0; i <= weaponConfig.weaponSpread; i++)
		{
			InstantFire();
		}
	}
	if (projectileType = EWeaponProjectile::EProjectile) {
		GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Black, "Projectile");

		 InstantFire();
	}
}

void AWeapon::InstantFire() {
	GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Black, "WEAPON FIRED");

	//Creates a weapon fire that intentionally isn't 100% precise
	//The randomness introduced allows for things like weapon spreads
	const int32 randomSeed = FMath::Rand();
	FRandomStream weaponRandomStream(randomSeed);
	const float currentSpread = weaponConfig.weaponSpread;
	const float spreadCone = FMath::DegreesToRadians(weaponConfig.weaponSpread * .5);
	const FVector aimDir = weaponMesh->GetSocketRotation("MF").Vector();
	const FVector startTrace = weaponMesh->GetSocketLocation("MF");
	const FVector shootDir = weaponRandomStream.VRandCone(aimDir, spreadCone, spreadCone);
	const FVector endTrace = startTrace + shootDir * weaponConfig.weaponRange;
	const FHitResult impact = WeaponTrace(startTrace, endTrace);

	//Calls function to process any hits from trace
	ProcessInstantHit(impact, startTrace, shootDir, randomSeed, currentSpread);
}

FHitResult AWeapon::WeaponTrace(const FVector aTraceFrom, const FVector aTraceTo) const {

	static FName weaponFireTag = FName(TEXT("weaponTrace"));
	FCollisionQueryParams traceParams(weaponFireTag, true, Instigator);
	traceParams.bTraceAsyncScene = true;
	traceParams.bReturnPhysicalMaterial = true;
	traceParams.AddIgnoredActor(this);

	FHitResult hit(ForceInit);
	GetWorld()->LineTraceSingleByChannel(hit, aTraceFrom, aTraceTo, TRACE_WEAPON, traceParams);

	return hit;


}

void AWeapon::ProcessInstantHit(const FHitResult &impact, const FVector &origin, const FVector &shootDir, int32 randomSeed, float reticleSpread) {

	const FVector endTrace = origin + shootDir * weaponConfig.weaponRange;
	const FVector endPoint = impact.GetActor() ? impact.ImpactPoint : endTrace;
	DrawDebugLine(this->GetWorld(), origin, impact.TraceEnd, FColor::Black, true, 10000, 10.f);

}

Hey, the operator= in if statement is misspelled?

if (projectileType = EWeaponProjectile::EBullet)

if (projectileType == EWeaponProjectile::EBullet)

Thanks for pointing that out! Unfortunately that doesn’t resolve the issue.

Edit: I removed the if statements and just called InstantFire() and it also crashed, so I’m thinking something in there is causing it but I can’t be sure.

Well your problem is already answered… Thread tried to blah blah… Are you new to comp science altogether?
With such a precise errkr im surprised it doesn’t tdll you which line

Sounds like you’re hot on the trail. Try disabling code blocks in InstantFire() until you narrow it down to a line causing the fault.

In PrimaryCharacter.cpp:
Where do you set currentweapon, I think you forgot to and that might be your error. You get this code: 0xC0000005. This code stands for access violoation which usually means you are refering to a pointer with no data in it.

Also try to change line 86 to:

if(CurrentWeapon) {CurrentWeapon->Fire();}

or with debug to:

if(CurrentWeapon)
{
  UE_LOG(LogTemp, Warning, TEXT("Firing, AWeapon::Fire()");
  CurrentWeapon->Fire();
} else {
  UE_LOG(LogTemp, Warning, TEXT("CurrentWeapon is not set properly");
}

In weapon.cpp:

AWeapon::Fire(): your if statements use = and that has to be ==

I would suggest making it elseif statements or a switch to let the function run faster (not much)

That was it, I never assigned currentWeapon, thanks! I’ll also look into your other suggestions. Thanks for the help!

I am glad it helped, it was a nice wat of relaxing before my exams XD