Engine crashes - Weapon class with enum

I am trying to implement a weapon class to a project of mine. So I created a class, derived from AActor, called it Weapon. In there I created an enum EBulletTypes so I can keep track of what kind of bullets my weapons will have (PistolBullet, RifleBullet, RocketLauncherBullet, …). I also created a struct so I can store different attributes in there like MaxAmmo, Damage, and so on. In my Weapon class I created two functions StartFiringWeapon and StopFiringWeapon which atm should just AddOnScreenDebugMessages to see if everything runs how it should. The StartFiringWeapon and StopFiringWeapon functions are being executed within my character class when the left mouse button is pressed. I want to print to the screen the type of bullet that is currently been selected. I set the BulletType in the AWeapon constructor to be equal to the first BulletType of my enum. But if I perform if statements and print something dependent on what BulletType is set in StartFiringWeapon the engine crashes when I press the left mouse button. If I don’t check for the enums in StartFiringWeapon the engine does not crash and the message is being added.

Weapon.h:



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

#pragma once

#include "GameFramework/Actor.h"
#include "Weapon.generated.h"

UENUM(BlueprintType)
enum class EBulletType : uint8
{
		EKnifeBullet,
		EPistolBullet,
		EShotgunBullet,
		ERifleBullet,
		ESniperRifleBullet,
		EGrenadeLauncherBullet,
		ERocketLauncherBullet
};


USTRUCT()
struct FWeaponAttributes 
{
	GENERATED_USTRUCT_BODY()

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Weapon Attributes")
		int32 n32MaxAmmo;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Weapon Attributes")
		int32 n32CurrentAmmo;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Weapon Attributes")
		int32 n32MaxClip;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Weapon Attributes")
		int32 n32CurrentClip;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Weapon Attributes")
		float fDamage;

};

UCLASS()
class MYPROJECT_API AWeapon : public AActor
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	AWeapon();

	// Called when the game starts or when spawned
	virtual void BeginPlay() override;
	
	// Called every frame
	virtual void Tick( float DeltaSeconds ) override;

	// Create WeaponAttributes
	UPROPERTY(EditDefaultsOnly, Category = "Weapon Attributes")
		FWeaponAttributes WeaponsAttributes;

	// Create bullet types
	UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Weapon Attributes")
		EBulletType BulletType;

	// Fire weapon function declaration
	UFUNCTION()
		void StartFiringWeapon();

	UFUNCTION()
		void StopFiringWeapon();
	
};



Weapon.cpp



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

#include "MyProject.h"
#include "Weapon.h"


// Sets default values
AWeapon::AWeapon()
{
 	// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

	AWeapon::WeaponsAttributes.fDamage = 0.0f;
	AWeapon::WeaponsAttributes.n32MaxAmmo = 0;
	AWeapon::WeaponsAttributes.n32CurrentAmmo = AWeapon::WeaponsAttributes.n32MaxAmmo;
	AWeapon::WeaponsAttributes.n32MaxClip = 0;
	AWeapon::WeaponsAttributes.n32CurrentClip = AWeapon::WeaponsAttributes.n32MaxClip;
	BulletType = EBulletType::EKnifeBullet;

}

// Called when the game starts or when spawned
void AWeapon::BeginPlay()
{
	Super::BeginPlay();
	
}

// Called every frame
void AWeapon::Tick( float DeltaTime )
{
	Super::Tick( DeltaTime );

}

void AWeapon::StartFiringWeapon()
{
	// This is where it crashes
	// If I delete the if-statements 
	// And if I just call GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Cyan, TEXT("Function ran"));
	// Than the engine will not crash
		if (BulletType == EBulletType::EKnifeBullet)
		{
			if (GEngine)
			{
				GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Cyan, TEXT("Knife"));
			}
		}
		else if (BulletType == EBulletType::EPistolBullet)
		{
			if (GEngine)
			{
				GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Cyan, TEXT("Pistol"));
			}
		}
		else if (BulletType == EBulletType::EShotgunBullet)
		{
			if (GEngine)
			{
				GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Cyan, TEXT("Shotgun"));
			}
		}
}

void AWeapon::StopFiringWeapon()
{
	if (GEngine)
	{
		GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Cyan, TEXT("StopFiringWeapon()"));
	}
}


Character.h:



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

#pragma once

#include "Weapon.h"
#include "GameFramework/Character.h"
#include "Character_Simpl0S.generated.h"

UCLASS()
class MYPROJECT_API ACharacter_Simpl0S : public ACharacter
{
	GENERATED_BODY()

public:
	// Sets default values for this character's properties
	ACharacter_Simpl0S();

	// Called when the game starts or when spawned
	virtual void BeginPlay() override;
	
	// Called every frame
	virtual void Tick( float DeltaSeconds ) override;

	// Called to bind functionality to input
	virtual void SetupPlayerInputComponent(class UInputComponent* InputComponent) override;

	// Some code here

	AWeapon *pWeapon;

// Fire Weapon
	UFUNCTION()
		void StartFiringWeapon();
	UFUNCTION()
		void StopFiringWeapon();

// more code here
};



Character.cpp




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

#include "MyProject.h"
#include "Character_Simpl0S.h"


// Sets default values
ACharacter_Simpl0S::ACharacter_Simpl0S()
{
 	// 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;

}

// Called when the game starts or when spawned
void ACharacter_Simpl0S::BeginPlay()
{
	Super::BeginPlay();
	
	
}

// Called every frame
void ACharacter_Simpl0S::Tick( float DeltaTime )
{
	Super::Tick( DeltaTime );


}

// Called to bind functionality to input
void ACharacter_Simpl0S::SetupPlayerInputComponent(class UInputComponent* InputComponent)
{
	Super::SetupPlayerInputComponent(InputComponent);

	// Some Bindings here

	// Bind action to fire weapon
	InputComponent->BindAction("FireWeapon", IE_Pressed, this, &ACharacter_Simpl0S::StartFiringWeapon);
	InputComponent->BindAction("FireWeapon", IE_Released, this, &ACharacter_Simpl0S::StopFiringWeapon);

// More bindings here
}

// Some code here

// Firing weapon definition
void ACharacter_Simpl0S::StartFiringWeapon()
{
	pWeapon->StartFiringWeapon();
}

void ACharacter_Simpl0S::StopFiringWeapon()
{
	pWeapon->StopFiringWeapon();
}

// More code here



As a side note, you don’t really want to include your .h files for weapon in your character’s header file. It’s better to use forward declarations wherever possible. So instead of:


#include "Weapon.h"

in your Character.h just add


class AWeapon;

After your other includes.

As for your problem, you haven’t initialized your weapon object in your character class. It’s nullptr at the moment. I think you would want to make your pWeapon variable a UPROPERTY:



UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Weapon")
AWeapon *pWeapon;


And then assign a weapon object to your character blueprint.

Forward declaration gives me a compile error: “Check declaration of AWeapon”.

Since your enum field is a UPROPERTY as well, perhaps you could put it inside of the TEnumAsByte<> container? So, currently you have:



// Create bullet types
	UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Weapon Attributes")
		EBulletType BulletType;


but instead do:



// Create bullet types
	UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Weapon Attributes")
		TEnumAsByte<EBulletType::Type> BulletType;


And, declare you enum as:



UENUM(BlueprintType)
namespace EBulletType{
    enum Type{
                EKnifeBullet,
		EPistolBullet,
		EShotgunBullet,
		ERifleBullet,
		ESniperRifleBullet,
		EGrenadeLauncherBullet,
		ERocketLauncherBullet
    };
}


Also, have you run through in DebugGame mode and set breakpoints at the beginning of the function to see if any variables are being optimized away prematurely?

P.S. Why use a giant if else block when you can just as easily use a switch statement?

I’ll give it a try later.

How would you do this:

I try to avoid switch where I can. Don’t like them. Although you’re right that for cases like these you should use a switch statement.

Before I continue, what is your environment? Are you using an IDE? What is your OS that you are developing on?

IDE: Visual Studio 2013 Community

OS: Windows 8.1 x64

Okay, if you haven’t already, follow the instructions here:

To get the UnrealVS (Unreal Visual Studio Extension) installed and setup. Then in the dropdown for the configuration, set it to DebugGame.

To add a break point, select a line, go to the Debug menu option, select it, select Toggle Breakpoint.

Now, when this line of code is hit when you are running in the editor viewport or in any development viewport on your OS, it will ‘pause’ everything. You can select ‘Step Over’ on the menu to go over each line of code step by step. Finally, if you go to Inspect variables - Autos and Locals windows - Visual Studio (Windows) | Microsoft Docs you can set up the ability to view locals and autos to see variables and whether they are initialized properly or not.

Hope all of this helps!

P.S. I would try the code changes first.

I’ve actualy explained where the problem is in my previous post. The part about forward declarations is optional and does not have anything to do with the actual fix. :slight_smile:

Have you tried fixing your weapon variable?

However, it is the Weapon’s function that is crashing:


void AWeapon::StartFiringWeapon()
{
	// This is where it crashes
	// If I delete the if-statements 
	// And if I just call GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Cyan, TEXT("Function ran"));
	// Than the engine will not crash
		if (BulletType == EBulletType::EKnifeBullet)
		{
			if (GEngine)
			{
				GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Cyan, TEXT("Knife"));
			}
		}
		else if (BulletType == EBulletType::EPistolBullet)
		{
			if (GEngine)
			{
				GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Cyan, TEXT("Pistol"));
			}
		}
		else if (BulletType == EBulletType::EShotgunBullet)
		{
			if (GEngine)
			{
				GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Cyan, TEXT("Shotgun"));
			}
		}
}

As he stated. If pWeapon == NULL then it wouldn’t matter what the function definition is or the code inside does.

And it does not matter indeed. If he gets rid of the check:


if (BulletType == EBulletType::EKnifeBullet)

Then everything works fine as he’s already stated.

Already installed the Unreal Visual Studio Extension. Never used the Breakpoint with VS. I now know what that is. XCode on Mac OS has that feature by default installed if I remember correctly (Haven’t used UE4 on mac for a while now). I’ll change the code now and come back later.

I have tried changing some stuff with my pWeapon variable but still crashes on me. Also I got rid of the enums and used int32 for my BulletTypes but it stillt crashes when I perform if statements in StartFiringWeapon().

Okay so I rewrote the Weapon class. Changed from enum class to enum with namespace and TEnumAsByte container. Still crashed with the if statement and didn’t without. After that I went into my character.cpp and in StartFiringWeapon() I performed a check if(pWeapon) before using the pWeapon->StartFiringWeapon() function. Nothing got printed to the screen so I think IanBreeg is right. It’s a nullptr. But then again I don’t understand why pWeapon->StartFiringWeapon runs without the if statements.

Character.cpp



// Firing weapon functions definition
void ACharacter_Simpl0S::StartFiringWeapon()
{
	if (pWeapon)
	{
		pWeapon->StartFiringWeapon();
	}
	else if (pWeapon == nullptr)
	{
		if (GEngine)
		{
			GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Cyan, TEXT("pWeapon is a nullptr ==> ACharacter_Simpl0S::StartFiringWeapon()"));
		}
	}
}

void ACharacter_Simpl0S::StopFiringWeapon()
{
	if (pWeapon)
	{
		pWeapon->StopFiringWeapon();
	}
	else if (pWeapon == nullptr)
	{
		if (GEngine)
		{
			GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Cyan, TEXT("pWeapon is a nullptr ==> ACharacter_Simpl0S::StopFiringWeapon()"));
		}
	}
}


Result when shooting weapon:

Okay I got this working now. Just watched this video on Weapon Essentials:

https://www.youtube.com/watch?v=yhXMXNRtMqI

And I managed to fix the nullptr error. Thanks for helping me out guys! I didn’t know how to get a reference to a blueprint class in c++ so I could assign pWeapon to the Blueprint weapon object. ConstructorHelpers was the answer here (Didn’t know something like that existed haha). Now I can continue building my weapon class YAY.