Hi guys,
So I’m making a FPS game (essentially) where I use a laser, spawned by the main Player (Player3d.h/cpp), to point at objects in the map and makes info boxes pop-up whenever the laser collides with an object on the map. To contain the information, I’ve created a custom class (SynchrotronComponent.h/cpp) which loads the appropriate data from a CSV file. I’ve instantiated the Component class and the game build fine. However, I run into a ‘UE4Editor.exe has triggered a breakpoint’ error which starts at the beginning of the SynchrotronComponent class. How do I fix this issue/instaniate it properly in UE++?
^This is the error and where it gets triggered
Here is the code for the classes:
Player3d.h
#pragma once
#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
#include "SynchrotronComponent.h"
#include "Engine.h"
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "Player3d.generated.h"
UCLASS()
class SYNCHROTRONVRCPP_API APlayer3d : public ACharacter
{
GENERATED_BODY()
public:
// Sets default values for this character's properties
APlayer3d();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
//Function used to convert actual component names to a friendlier format
FString FormatComponentName(FString componentName);
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
// Called to bind functionality to input
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
//World we're in
UWorld* World = ();
//Movement
//Moving forwards and backwards
UFUNCTION()
void MoveForward(float Value);
//Moving right and left
UFUNCTION()
void MoveRight(float Value);
//Jumping
//Set's jump flag to true when key is pressed
UFUNCTION()
void StartJump();
//Clears jump flag when key is released
UFUNCTION()
void StopJump();
//Camera
UPROPERTY(VisibleAnywhere)
UCameraComponent* PlayerCameraComponent;
//Creating laser
UPROPERTY()
bool LaserOn;
UFUNCTION()
void DrawLaser();
//UFUNCTION()
void TurnLaserOnOff();
//Show Textbox/Description
UPROPERTY()
bool LockOn;
//UFUNCTION() //This should be visible, no?
void TurnTextBoxOnOff();
//UPROPERTY()
ASynchrotronComponent curSynchrotronComponent;
};
Player3d.cpp
#include "Player3d.h"
#include "Engine.h"
// Sets default values
APlayer3d::APlayer3d()
{
// 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;
// Create a first person camera component.
PlayerCameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("FirstPersonCamera"));
// Attach the camera component to our capsule component.
PlayerCameraComponent->SetupAttachment(GetCapsuleComponent());
// Position the camera slightly above the eyes.
PlayerCameraComponent->SetRelativeLocation(FVector(0.0f, 0.0f, 150.0f + BaseEyeHeight));
// Allow the pawn to control camera rotation.
PlayerCameraComponent->bUsePawnControlRotation = true;
//Turn Gravity off for this guy => Move freely through space
//GetCharacterMovement()->GravityScale = 0;
//World we're working in
//UWorld* World = ();
// Laser
bool LaserOn = false; //Laser is initially off
//Textbox
bool LockOn = false; //The description box is initially not visible/doesn't exist
//curSynchrotronComponent = ASynchrotronComponent(); //= Cast<ASynchrotronComponent>(World->GetAuthGameMode());
}
// Called when the game starts or when spawned
void APlayer3d::BeginPlay()
{
Super::BeginPlay();
}
FString APlayer3d::FormatComponentName(FString componentName)
{
/*
- @param componentName := The static mesh's name
(as it appears in the game)
- returns the corresponding key for the component
(that the static mesh/componentName represents).
*/
//std::string cstr(TCHAR_TO_UTF8(*componentName)); //converting FString to string
FString curModule;
//For some reason, some modules have a bunch of actors (with the static mesh!) that don't have the exact same name...
std::vector<FString> module_11 = { "SM_MERGED_MODULE_11", "SM_MERGED_MODULE_14", "SM_MERGED_MODULE_28", "SM_MERGED_MODULE_17", "SM_MERGED_MODULE_179", "SM_MERGED_MODULE_21", "SM_MERGED_MODULE_17", "SM_MERGED_MODULE_122" };
std::vector<FString> module_116 = { "Module_86", "Module_33", "Module_208", "Module_204", "Module_117", "Module_158", "Module_127", "Module_100" };
std::vector<FString> module_117 = { "Module_89", "Module_36", "Module_94" };
std::vector<FString> module_47 = { "Module_57", "Module_56", "Module_25", "Module_198", "Module_31", "Module_170", "Module_165", "Module_46" };
/*
Have to use substr because, some modules are named:
SM_MERGED_Module_1_Simplified_With_Textures7_05
whilst others are named
SM_MERGED_Module_1_Simplified_With_Textures7_05
despite having the same static mesh
*/
if (componentName.Mid(0, 29) == FString("SM_MERGED_Module_1_Simplified")) { curModule = "1"; } // "Module 1"
else if (componentName == FString("Injection_area_V2_110")) { curModule = "2"; } // "Injection Area"
else if (find(module_11.begin(), module_11.end(), componentName) != module_11.end()) { curModule = "3"; } // "Module 11"
else if (find(module_116.begin(), module_116.end(), componentName) != module_116.end()) { curModule = "4"; } // "Module 116"
else if (componentName == FString("SM_MERGED_Module_118_With_Textures_Module_11")) { curModule = "5"; } // "Module 118"
else if (FString(componentName).Mid(0, 32) == "SM_MERGED_Module13_With_Textures") { curModule = "6"; } // "Module 13"
else if (FString(componentName).Mid(0, 20) == "SM_MERGED_Module_25_") { curModule = "7"; } // Module 25"
else if (FString(componentName).Mid(0, 20) == "SM_MERGED_Module_30_") { curModule = "8"; } // "Module 30"
else if (FString(componentName).Mid(0, 20) == "SM_MERGED_Module_45_") { curModule = "9"; } // "Module 45"
else if (componentName == FString("SM_MERGED_Module_46_Default_15")) { curModule = "10"; } // "Module 46"
else if (find(module_47.begin(), module_47.end(), componentName) != module_47.end()) { curModule = "11"; } // "Module 47"
else if (componentName == FString("SM_MERGED_Super_Period_3_Module_5_new_15")) { curModule = "12"; } // "Module 5"
else if (componentName == FString("SM_MERGED_Module_55_Cube_12")) { curModule = "13"; } // "Module 55"
else if (componentName == FString("SM_MERGED_Module_8_Cube_119")) { curModule = "14"; } // "Module 8"
else if (FString(componentName).Mid(0, 20) == "SM_MERGED_Module_83_") { curModule = "15"; } // "Module 83"
else if (componentName == FString("SM_MERGED_Module_86_With_Textures_87")) { curModule = "16"; } // "Module 86"
else if (FString(componentName).Mid(0, 32) == "SM_MERGED_Module_9_With_Textures") { curModule = "17"; } // "Module 9"
else if (FString(componentName).Mid(0, 20) == "Module_99_Simplified") { curModule = "18"; } // "Module 99"
else { curModule = "DWBOI"; } //i.e. the beam should ignore this component
//return FString(curModule.c_str());
return curModule;
}
// Called every frame
void APlayer3d::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
if (this->LaserOn) //Draw the laser if Laser is on
{
DrawLaser();
}
/*
if (this->LockOn)
{
//Sets the description
//curSynchrotronComponent->setNameDescription(curSynchrotronComponent->moduleCode);
}
*/
}
std::string FStringToString(FString myFstr)
{
std::string MyStdString(TCHAR_TO_UTF8(*myFstr));
return MyStdString;
}
// Called to bind functionality to input
void APlayer3d::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
//Movement bindings
PlayerInputComponent->BindAxis("MoveForward", this, &APlayer3d::MoveForward);
PlayerInputComponent->BindAxis("MoveRight", this, &APlayer3d::MoveRight);
//Look/Rotation bindings
PlayerInputComponent->BindAxis("Turn", this, &APlayer3d::AddControllerYawInput);
PlayerInputComponent->BindAxis("Lookup", this, &APlayer3d::AddControllerPitchInput);
//Jumping bindings
PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &APlayer3d::StartJump);
PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &APlayer3d::StopJump);
//Laser bindings
PlayerInputComponent->BindAction("Shoot", IE_Pressed, this, &APlayer3d::TurnLaserOnOff);
//Display Message/Load Component
PlayerInputComponent->BindAction("LockOn", IE_Pressed, this, &APlayer3d::TurnTextBoxOnOff);
}
void APlayer3d::TurnLaserOnOff()
{
this->LaserOn = !LaserOn;
}
void APlayer3d::TurnTextBoxOnOff()
{
if (this->LaserOn) //Laser must be on and you must be locked on.
{
this->LockOn = !LockOn;
}
else //If laser is off, you cannot see the message.
{
this->LockOn = false;
}
}
void APlayer3d::DrawLaser()
{
FHitResult OutHit;
FVector Start = GetActorLocation(); //PlayerCameraComponent->GetComponentLocation();
//Translating the laser so it shoots out the hand
//Start.Z += 10.f;
//Start.X += 100.f;
//Constants needed for laser
float LaserDistance = 500.f; //length of laser
float LaserThickness = 20.f; //width of laser
float LaserOffset = 200.f;
float LifeTimeOfLaser = -1; //-1 indicates that the laser is persistent
float msgDuration = 0.5f;
//Getting the vectors needed
FVector ForwardVector = PlayerCameraComponent->GetForwardVector(); //GetActorForwardVector();
Start += (ForwardVector * LaserOffset);
FVector End = (ForwardVector*LaserDistance) + Start;
FCollisionQueryParams CollisionParams;
FColor BeamColor;
//Component the laser's collided with
//FString CollisionComponent = FormatComponentName(OutHit.GetActor()->GetName()); //Potential nullptr exception
//Collision detection
if (this->World->LineTraceSingleByObjectType(OutHit, Start, End, ECC_WorldStatic, CollisionParams) //Is there a collision with any component(s)
&& (FormatComponentName(OutHit.GetActor()->GetName()) != "DWBOI")) //Are we interested in this component
{
//This outputs a message (the name really) of the first object that was
//hit by the laser. Used for testing purposes for now.
/*
GEngine->AddOnScreenDebugMessage(-1, msgDuration, FColor::Green,
FString::Printf(TEXT("The component being hit is: %s"), *FormatComponentName(OutHit.GetActor()->GetName())));
*/
//FString CollisionComponent = FormatComponentName(OutHit.GetActor()->GetName()); //Potential nullptr exception
curSynchrotronComponent.moduleCode = "1"; //FStringToString(FormatComponentName(OutHit.GetActor()->GetName()));
//std::string cur_mod = curSynchrotronComponent->moduleCode;
curSynchrotronComponent.setNameDescription("1"); //Should set the name and description
GEngine->AddOnScreenDebugMessage(-1, msgDuration, FColor::Blue,
FString::Printf(*(curSynchrotronComponent.name.ToString())));
/*
if (curSynchrotronComponent == NULL)
{
GEngine->AddOnScreenDebugMessage(-1, msgDuration, FColor::Blue,
FString::Printf(cur_mod));
//curSynchrotronComponent = new ASynchrotronComponent(); //Edit this
}
*/
BeamColor = FColor::Green;
//Instantiate the component
}
else
{
BeamColor = FColor::Red;
}
//Actually drawing the line.
DrawDebugLine(this->World, Start, End, BeamColor, false,
LifeTimeOfLaser, 0, LaserThickness);
}
void APlayer3d::StartJump()
{
bPressedJump = true;
}
void APlayer3d::StopJump()
{
bPressedJump = false;
}
void APlayer3d::MoveForward(float Value)
{
FVector Direction = FRotationMatrix(Controller->GetControlRotation()).GetScaledAxis(EAxis::X);
AddMovementInput(Direction, Value);
}
void APlayer3d::MoveRight(float Value)
{
FVector Direction = FRotationMatrix(Controller->GetControlRotation()).GetScaledAxis(EAxis::Y);
AddMovementInput(Direction, Value);
}
SynchrotronComponent.h
#pragma once
#include <iostream>
#include <fstream>
#include <string>
#include <tuple>
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "TextWidgetTypes.h"
#include "TextBlock.h"
#include "SynchrotronComponent.generated.h"
UCLASS()
class SYNCHROTRONVRCPP_API ASynchrotronComponent : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
ASynchrotronComponent();
//UPROPERTY()
UTextBlock* ScreenTextDisplay;
//The module index in the CSV file
std::string moduleCode;
UPROPERTY()
FText name;
UPROPERTY()
FText description;
//UFUNCTION()
FText strToFText(std::string some_str);
//UFUNCTION()
std::tuple<std::string, std::string> getDescription(std::string ComponentIndex);
UFUNCTION()
void DisplayDetails(); // This displays the module's name and description
void setNameDescription(std::string moduleIndex);
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
};
**SynchrotronComponent.cpp**
#include "SynchrotronComponent.h"
// Sets default values
ASynchrotronComponent::ASynchrotronComponent()
{
// 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;
}
FText ASynchrotronComponent::strToFText(std::string some_str)
{
FString temp_str(some_str.c_str());
return FText::FromString(temp_str);
}
std::tuple<std::string, std::string> ASynchrotronComponent::getDescription(std::string index)
{
/*
This method iterates through the file 'Component Descriptions.csv'
to find and return the name and description of a specific component.
N.B. The general structure of the csv is:
<name1>, <description1>
<name2>, <description2>
.
.
.
*/
//Opening CSV file
std::ifstream moddesc("Component Descriptions.csv");
std::string cur_index;
std::string module_name;
std::string module_desc;
bool found = false;
//We check through each row until we either find the component or reach the EOF
while (moddesc.good() && !found)
{
//Extracts the name from the line you're looking at
std::getline(moddesc, cur_index, ','); //Get the current index
std::getline(moddesc, module_name, ','); //Get name on line from file
std::getline(moddesc, module_desc, '\n'); //Get description on line from file
if (cur_index == index)
{
found = true;
}
}
//Returning the description
if (found)
{
return std::make_tuple(module_name, module_desc);
}
else
{
return std::make_tuple("N/A", "N/A");
}
}
void ASynchrotronComponent::DisplayDetails()
{
if (ScreenTextDisplay) {
ScreenTextDisplay->SetText(description);
//ScreenTextDisplay->GetName();
//Text should not wrap around
//SetColorAndOpacity (should be opaque)
//SetJustification //Sets alignment
//SetMinDesiredWidth
}
}
void ASynchrotronComponent::setNameDescription(std::string moduleIndex)
{
//Initialising etails of the component
std::string name_str, description_str;
std::tie(name_str, description_str) = getDescription(moduleIndex);
this->name = strToFText(name_str);
this->description = strToFText(description_str);
//Setting text for textbox
/*
if (ScreenTextDisplay) {
ScreenTextDisplay->SetText(description);
//ScreenTextDisplay->GetName();
//Text should not wrap around
//SetColorAndOpacity (should be opaque)
//SetJustification //Sets alignment
//SetMinDesiredWidth
}
*/
}
// Called when the game starts or when spawned
void ASynchrotronComponent::BeginPlay()
{
Super::BeginPlay();
//Set name and description once the game loads.
//initNameDescription();
}
// Called every frame
void ASynchrotronComponent::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}