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);
}