Cannot call a function from another class! No errors UE4 just closes itself

Hey Guys,
First of all, really sorry for my bad english :smiley:
I hope you can help me out. I am really new to c++ / UE an I am following a general c++ tutorial.
My problem is applying the things to unreal engine. I am struggling at calling a basic function from another class.

So I opened the standard FirstPerson Project from ue and with the help of a tutorial i created a raycast line which returns the name of the hitted object.
What I am trying to do now, is creating an Actor “EnemyActor” which has a private Variable ‘int health’ and a public function which is called ‘void ApplyDamage()’.

Whenever I use the Raycast hotkey, the Character class should call the function AEnemyActor::ApplyDamage().

Here is my Code:

EnemyActor.cpp

#include "MyRaycastProject.h"
#include "EnemyActor.h"
#include "Engine.h"


// Sets default values
AEnemyActor::AEnemyActor()
{
 	// 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;
	health = 100;

}

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

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

}

void AEnemyActor::ApplyDamage()
{
	health -= 10;
	GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("Applied Damage.. %d Health left"), health));
}

From the MyRaycastProjectCharacter.cpp…

void AMyRaycastProjectCharacter::PerformRaycast()
{

	AEnemyActor EActorObject;
	AEnemyActor* EActor = &EActorObject;

	FHitResult* HitResult = new FHitResult;
	FVector StartTrace = FirstPersonCameraComponent->GetComponentLocation();
	FVector ForwardVector = FirstPersonCameraComponent->GetForwardVector();
	FVector EndTrace = ((ForwardVector * 5000.0f) + StartTrace);
	FCollisionQueryParams* TraceParams = new FCollisionQueryParams;

	if (GetWorld()->LineTraceSingleByChannel(*HitResult, StartTrace, EndTrace, ECC_Visibility, *TraceParams))
	{
		DrawDebugLine(GetWorld(), StartTrace, EndTrace, FColor(255, 0, 0), true);
		//GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("You Hit: %s"), *HitResult->Actor->GetName()));

		
		if (!HitResult->GetActor()->IsValidLowLevel())
		{
			
			return;
		}

		//Assign the hit actor to EActor
		EActor= Cast<AEnemyActor>(HitResult->GetActor());
		
		

		EActor->ApplyDamage();
	}
}

The Raycast thing is alright. But whenever I try to call the function ApplyDamage(), my UE just closes and restarts.
I hope you guys can help me. I am facing this problem since 2-3 days. but there are no c++ tutorials for ue.
All I can see is Blueprint ;(

There are actually a lot of tutorials for C++, though it is admittedly somewhat difficult to dig through all the blueprint results.

From a quick check, it looks like you never assign EActor. The engine just crashes with no error report or anything? Not the restart screen?

Hello AnGaraa,

You are not assigning the hit actor, I.E HitResault->GetActor()

This is my interaction method so far for my player. I put all the line tracing into a static function library to keep everything nice and neat and callable from anywhere.

void AplayerCharacter::Interact( )
{
	FHitResult hitResaults(ForceInit); //This hit resault structure our line trace will output too.

	if (!UStaticFunctionLibrary::playerLineTrace(this, hitResaults))
	{
            //Logging here
		return;
	}

	AActor * hitActor = hitResaults.GetActor();

	if (Cast<AItem>(hitActor))
	{
		AItem * tmpItem = Cast<AItem>(hitResaults.GetActor());

		tmpItem->Interact(this);
	}
}

As you can see, I get the hit actor from the FHitResults structure: AActor * hitActor = hitResaults.GetActor();. In my static function I also check if the hit actor is valid:

if (outResaults.GetActor()->IsValidLowLevel())
{
        return true;
} 

So when KingCole32 said that EActor is never assigned is because it is never initialized so you will get bugs and errors. May I suggest using the Visual Studio Debugging Tool to debug if you are fairly new to C++! Using the debugger will allow you to add break points, check values of variables and what not. Make sure you follow this Visual Studio Debugger Extension(works with UE4 and Visual Studio 2015). It will allow you to see the values of some data structures defined in Unreal Engine, such as FString.

Back on topic, All you have to do is assign the hit actor to EActor and you should be all good to go, so try this:

 void AMyRaycastProjectCharacter::PerformRaycast()
 {
     
     AEnemyActor EActor;
 
     FHitResult* HitResult = new FHitResult;
     FVector StartTrace = FirstPersonCameraComponent->GetComponentLocation();
     FVector ForwardVector = FirstPersonCameraComponent->GetForwardVector();
     FVector EndTrace = ((ForwardVector * 5000.0f) + StartTrace);
     FCollisionQueryParams* TraceParams = new FCollisionQueryParams;
 
     if (GetWorld()->LineTraceSingleByChannel(*HitResult, StartTrace, EndTrace, ECC_Visibility, *TraceParams))
     {
         DrawDebugLine(GetWorld(), StartTrace, EndTrace, FColor(255, 0, 0), true);
         GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("You Hit: %s"), *HitResult->Actor->GetName()));

        //Check if hit actor is valid
        if (!HitResaults->GetActor()->IsValidLowLevel())
        {
                //Some logging here and what not
                return;
        } 

        //Assign the hit actor to EActor
        EActor = HitResults->GetActor();

         EActor.ApplyDamage();
     }
 }

Also, may I suggest to not use a pointer for HitResult, it is simply not needed(check my example).

Cheers,

Sorry, I forgot to add the cast. So try this:

EActor = Cast<AEnemyActor>(HitResults->GetActor());

So GetActor returns an AActor * and we have to cast it to an AEnemyActor, using the Cast method.

Oh I see… I totally forgot this part thank you.

I tried your Solution, but now its saying to me

EActor  = HitResult->GetActor();


no operator "=" matches these operands

I couldnt find a method to replace that “=”…
How can I fix this now?

Thx for the fast rep but it seems it has nothing to do with, what you are setting EActor equal to.

‘EActor’ just dont want to accept the operand “=”

I tried your new solution and its still the same error :confused:

Are you including the header that AEnemyActor is in?

Post your includes for this file please (#include blah at top of file)

First of all, please try to use comments instead of answers :smiley:
And the assigment does not work because of type incompatability. GetActor() returns a pointer to an actor, while your EActor is not a pointer but a regular object. Either use the assignment as is after dereferencing the pointer on the right side or change your EActor to type pointer of AEnemyActor. I recommend the latter.

This is the whole Class. standard FirstPerson Script from unreal.

My custom function PerformRaycast is at the end.

// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
#include "MyRaycastProject.h"
#include "MyRaycastProjectCharacter.h"
#include "MyRaycastProjectProjectile.h"
#include "Animation/AnimInstance.h"

#include "GameFramework/InputSettings.h"
#include "Engine.h"
#include "EnemyActor.h"

DEFINE_LOG_CATEGORY_STATIC(LogFPChar, Warning, All);

//////////////////////////////////////////////////////////////////////////
// AMyRaycastProjectCharacter

AMyRaycastProjectCharacter::AMyRaycastProjectCharacter()
{
	// Set size for collision capsule
	GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f);

	// set our turn rates for input
	BaseTurnRate = 45.f;
	BaseLookUpRate = 45.f;

	

	// Create a CameraComponent	
	FirstPersonCameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("FirstPersonCamera"));
	FirstPersonCameraComponent->AttachParent = GetCapsuleComponent();
	FirstPersonCameraComponent->RelativeLocation = FVector(0, 0, 64.f); // Position the camera
	FirstPersonCameraComponent->bUsePawnControlRotation = true;

	// Create a mesh component that will be used when being viewed from a '1st person' view (when controlling this pawn)
	Mesh1P = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("CharacterMesh1P"));
	Mesh1P->SetOnlyOwnerSee(true);
	Mesh1P->AttachParent = FirstPersonCameraComponent;
	Mesh1P->bCastDynamicShadow = false;
	Mesh1P->CastShadow = false;

	// Create a gun mesh component
	FP_Gun = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("FP_Gun"));
	FP_Gun->SetOnlyOwnerSee(true);			// only the owning player will see this mesh
	FP_Gun->bCastDynamicShadow = false;
	FP_Gun->CastShadow = false;
	FP_Gun->AttachTo(Mesh1P, TEXT("GripPoint"), EAttachLocation::SnapToTargetIncludingScale, true);


	// Default offset from the character location for projectiles to spawn
	GunOffset = FVector(100.0f, 30.0f, 10.0f);

	// Note: The ProjectileClass and the skeletal mesh/anim blueprints for Mesh1P are set in the
	// derived blueprint asset named MyCharacter (to avoid direct content references in C++)
}

//////////////////////////////////////////////////////////////////////////
// Input

void AMyRaycastProjectCharacter::SetupPlayerInputComponent(class UInputComponent* InputComponent)
{
	// set up gameplay key bindings
	check(InputComponent);

	InputComponent->BindAction("Raycast", IE_Pressed, this, &AMyRaycastProjectCharacter::PerformRaycast);
	InputComponent->BindAction("Jump", IE_Pressed, this, &ACharacter::Jump);
	InputComponent->BindAction("Jump", IE_Released, this, &ACharacter::StopJumping);
	
	//InputComponent->BindTouch(EInputEvent::IE_Pressed, this, &AMyRaycastProjectCharacter::TouchStarted);
	if( EnableTouchscreenMovement(InputComponent) == false )
	{
		InputComponent->BindAction("Fire", IE_Pressed, this, &AMyRaycastProjectCharacter::OnFire);
	}
	
	InputComponent->BindAxis("MoveForward", this, &AMyRaycastProjectCharacter::MoveForward);
	InputComponent->BindAxis("MoveRight", this, &AMyRaycastProjectCharacter::MoveRight);
	
	// We have 2 versions of the rotation bindings to handle different kinds of devices differently
	// "turn" handles devices that provide an absolute delta, such as a mouse.
	// "turnrate" is for devices that we choose to treat as a rate of change, such as an analog joystick
	InputComponent->BindAxis("Turn", this, &APawn::AddControllerYawInput);
	InputComponent->BindAxis("TurnRate", this, &AMyRaycastProjectCharacter::TurnAtRate);
	InputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput);
	InputComponent->BindAxis("LookUpRate", this, &AMyRaycastProjectCharacter::LookUpAtRate);
}

void AMyRaycastProjectCharacter::OnFire()
{ 
	// try and fire a projectile
	if (ProjectileClass != NULL)
	{
		const FRotator SpawnRotation = GetControlRotation();
		// MuzzleOffset is in camera space, so transform it to world space before offsetting from the character location to find the final muzzle position
		const FVector SpawnLocation = GetActorLocation() + SpawnRotation.RotateVector(GunOffset);

		UWorld* const World = GetWorld();
		if (World != NULL)
		{
			// spawn the projectile at the muzzle
			World->SpawnActor<AMyRaycastProjectProjectile>(ProjectileClass, SpawnLocation, SpawnRotation);
		}
	}

	// try and play the sound if specified
	if (FireSound != NULL)
	{
		UGameplayStatics::PlaySoundAtLocation(this, FireSound, GetActorLocation());
	}

	// try and play a firing animation if specified
	if(FireAnimation != NULL)
	{
		// Get the animation object for the arms mesh
		UAnimInstance* AnimInstance = Mesh1P->GetAnimInstance();
		if(AnimInstance != NULL)
		{
			AnimInstance->Montage_Play(FireAnimation, 1.f);
		}
	}

}

void AMyRaycastProjectCharacter::BeginTouch(const ETouchIndex::Type FingerIndex, const FVector Location)
{
	if( TouchItem.bIsPressed == true )
	{
		return;
	}
	TouchItem.bIsPressed = true;
	TouchItem.FingerIndex = FingerIndex;
	TouchItem.Location = Location;
	TouchItem.bMoved = false;
}

void AMyRaycastProjectCharacter::EndTouch(const ETouchIndex::Type FingerIndex, const FVector Location)
{
	if (TouchItem.bIsPressed == false)
	{
		return;
	}
	if( ( FingerIndex == TouchItem.FingerIndex ) && (TouchItem.bMoved == false) )
	{
		OnFire();
	}
	TouchItem.bIsPressed = false;
}

void AMyRaycastProjectCharacter::TouchUpdate(const ETouchIndex::Type FingerIndex, const FVector Location)
{
	if ((TouchItem.bIsPressed == true) && ( TouchItem.FingerIndex==FingerIndex))
	{
		if (TouchItem.bIsPressed)
		{
			if (GetWorld() != nullptr)
			{
				UGameViewportClient* ViewportClient = GetWorld()->GetGameViewport();
				if (ViewportClient != nullptr)
				{
					FVector MoveDelta = Location - TouchItem.Location;
					FVector2D ScreenSize;
					ViewportClient->GetViewportSize(ScreenSize);
					FVector2D ScaledDelta = FVector2D( MoveDelta.X, MoveDelta.Y) / ScreenSize;									
					if (ScaledDelta.X != 0.0f)
					{
						TouchItem.bMoved = true;
						float Value = ScaledDelta.X * BaseTurnRate;
						AddControllerYawInput(Value);
					}
					if (ScaledDelta.Y != 0.0f)
					{
						TouchItem.bMoved = true;
						float Value = ScaledDelta.Y* BaseTurnRate;
						AddControllerPitchInput(Value);
					}
					TouchItem.Location = Location;
				}
				TouchItem.Location = Location;
			}
		}
	}
}

void AMyRaycastProjectCharacter::MoveForward(float Value)
{
	if (Value != 0.0f)
	{
		// add movement in that direction
		AddMovementInput(GetActorForwardVector(), Value);
	}
}

void AMyRaycastProjectCharacter::MoveRight(float Value)
{
	if (Value != 0.0f)
	{
		// add movement in that direction
		AddMovementInput(GetActorRightVector(), Value);
	}
}

void AMyRaycastProjectCharacter::TurnAtRate(float Rate)
{
	// calculate delta for this frame from the rate information
	AddControllerYawInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds());
}

void AMyRaycastProjectCharacter::LookUpAtRate(float Rate)
{
	// calculate delta for this frame from the rate information
	AddControllerPitchInput(Rate * BaseLookUpRate * GetWorld()->GetDeltaSeconds());
}

bool AMyRaycastProjectCharacter::EnableTouchscreenMovement(class UInputComponent* InputComponent)
{
	bool bResult = false;
	if(FPlatformMisc::GetUseVirtualJoysticks() || GetDefault<UInputSettings>()->bUseMouseForTouch )
	{
		bResult = true;
		InputComponent->BindTouch(EInputEvent::IE_Pressed, this, &AMyRaycastProjectCharacter::BeginTouch);
		InputComponent->BindTouch(EInputEvent::IE_Released, this, &AMyRaycastProjectCharacter::EndTouch);
		InputComponent->BindTouch(EInputEvent::IE_Repeat, this, &AMyRaycastProjectCharacter::TouchUpdate);
	}
	return bResult;
}

void AMyRaycastProjectCharacter::PerformRaycast()
{

	AEnemyActor EActor;

	FHitResult* HitResult = new FHitResult;
	FVector StartTrace = FirstPersonCameraComponent->GetComponentLocation();
	FVector ForwardVector = FirstPersonCameraComponent->GetForwardVector();
	FVector EndTrace = ((ForwardVector * 5000.0f) + StartTrace);
	FCollisionQueryParams* TraceParams = new FCollisionQueryParams;

	if (GetWorld()->LineTraceSingleByChannel(*HitResult, StartTrace, EndTrace, ECC_Visibility, *TraceParams))
	{
		DrawDebugLine(GetWorld(), StartTrace, EndTrace, FColor(255, 0, 0), true);
		GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("You Hit: %s"), *HitResult->Actor->GetName()));

		if (!HitResult->GetActor()->IsValidLowLevel())
		{
			
			return;
		}

		//Assign the hit actor to EActor
		EActor = Cast<AEnemyActor>(HitResult->GetActor());
		
		

		EActor.ApplyDamage();
	}
}

wow, I tried different ways to fix it with pointers etc. before but I always got an error. And suddenly the error is gone and I can compile again. But my main problem (unreal engine 4 crash, when I press ‘R’ → BindAction for Raycast) is still there :frowning:

Try commenting out the line with GEngine->… and see if the behaviour changes

still the same. Its crashing when I try to call ‘ApllyDamage()’.

Is your EActor a pointer now? If it is, you need to call EActor->AplyDamage() instead of EActor.ApplyDamage(), maybe you want to update your code in your question :wink:

Edit: try removing your function call to ApplyDamage() at all and see if THAT is the problem. I can not see anything wrong with it. I think that something else might be amiss

I just updated my code in the question and yes it is a pointer.
But what I found out now is that ApplyDamage() wasnt the reason for my Crash.

The reason are these two lines:

AEnemyActor EActorObject;
	AEnemyActor *EActor = &EActorObject;

I commented them out and my code worked fine.
Now I dont know, if I have to define EActorObject somewhere else?

You dont need it at all as far as I can see. You get the reference from GetActor() already. There is no need to declare another variable and reference it if you are never going to use it. Also, the reason for the crash is probably the fact that you must use CreateDefaultObject or such (UClass::CreateDefaultObject | Unreal Engine Documentation) because UE does a lot of work behind the scenes.

but my problem is, I cant call ApplyDamage() whithout the declaration. I tried many things but its still crashing.

Okay AnDarra, try this:

AEnemyActor * EActor; //Create a variable, which is a pointer to a AEnemyActor class

if(HitResults->GetActor() )
{
    if( Cast<AEnemyActor>(HitResults->GetActor()) )
    {
        EActor = Cast<AEnemyActor>(HitResults->GetActor());
        EActor->ApplyDamage();
                 GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("Applied damage to %s !"), *HitResults->GetActor()->GetName());

    }
    else
    {
         GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("Could not cast actor %s to AEnemyActor !"), *HitResults->GetActor()->GetName());
    }
}

You could get errors, I just typed it on my phone. Let me know how you go.

Also, have you created the definition of the AEnemyActor::ApplyDamage() in your .cpp or .h file for AEnemyActor? E.G

AEnemyActor::ApplyDamage()
{
    //Call damage functions here
}

You are supposed to remove the declaration for the EnemyObject only. Turn this:

AEnemyActor EActorObject;
AEnemyActor* EActor = &EActorObject;

Into this:

AEnemyActor* EActor;

I dont see any reason why you would not be able to call the method this way.

There might actually be another problem. Your GetActor() may not return a valid AEnemyActor object. You may hit something else. You should use the UE casting methods to cast the return value of GetActor() and check if EActor is NULL afterwards. If you do not, you may run into a null reference exception