Download

Can't get my health bar to work, not sure if it's the code or the Blueprints

Like the title says, I can’t get my health bar to work, I’m not sure if the error is in the code or in the blueprints. Here is the code:

#include “MyProject3Character.h”
#include “MyProject3Projectile.h”
#include “Animation/AnimInstance.h”
#include “Camera/CameraComponent.h”
#include “Components/CapsuleComponent.h”
#include “Components/InputComponent.h”
#include “GameFramework/InputSettings.h”
#include “HeadMountedDisplayFunctionLibrary.h”
#include “Kismet/GameplayStatics.h”
#include “MotionControllerComponent.h”
#include “XRMotionControllerBase.h” // for FXRMotionControllerBase::RightHandSourceId
#include “Runtime/Engine/Classes/Engine/World.h”
#include “Components/SkeletalMeshComponent.h”
#include “Materials/MaterialInstanceDynamic.h”
#include “GameFramework/PlayerController.h”
#include “Perception/AIPerceptionStimuliSourceComponent.h”
#include “Perception/AISense_Sight.h”
#include “Kismet/KismetMathLibrary.h”
#include “TimerManager.h”
#include “Engine.h”

using namespace std;

DEFINE_LOG_CATEGORY_STATIC(LogFPChar, Warning, All);

//////////////////////////////////////////////////////////////////////////
// AMyProject3Character

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

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

// Create a CameraComponent	
FirstPersonCameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("FirstPersonCamera"));
FirstPersonCameraComponent->SetupAttachment(GetCapsuleComponent());
FirstPersonCameraComponent->SetRelativeLocation(FVector(-39.56f, 1.75f, 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->SetupAttachment(FirstPersonCameraComponent);
Mesh1P->bCastDynamicShadow = false;
Mesh1P->CastShadow = false;
Mesh1P->SetRelativeRotation(FRotator(1.9f, -19.19f, 5.2f));
Mesh1P->SetRelativeLocation(FVector(-0.5f, -4.4f, -155.7f));

// Create a gun mesh component
FP_Gun = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("FP_Gun"));
FP_Gun->SetOnlyOwnerSee(false);			// otherwise won't be visible in the multiplayer
FP_Gun->bCastDynamicShadow = false;
FP_Gun->CastShadow = false;
// FP_Gun->SetupAttachment(Mesh1P, TEXT("GripPoint"));
FP_Gun->SetupAttachment(RootComponent);

FP_MuzzleLocation = CreateDefaultSubobject<USceneComponent>(TEXT("MuzzleLocation"));
FP_MuzzleLocation->SetupAttachment(FP_Gun);
FP_MuzzleLocation->SetRelativeLocation(FVector(0.2f, 48.4f, -10.6f));

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

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

// Create VR Controllers.
R_MotionController = CreateDefaultSubobject<UMotionControllerComponent>(TEXT("R_MotionController"));
R_MotionController->MotionSource = FXRMotionControllerBase::RightHandSourceId;
R_MotionController->SetupAttachment(RootComponent);
L_MotionController = CreateDefaultSubobject<UMotionControllerComponent>(TEXT("L_MotionController"));
L_MotionController->SetupAttachment(RootComponent);

// Create a gun and attach it to the right-hand VR controller.
// Create a gun mesh component
VR_Gun = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("VR_Gun"));
VR_Gun->SetOnlyOwnerSee(false);			// otherwise won't be visible in the multiplayer
VR_Gun->bCastDynamicShadow = false;
VR_Gun->CastShadow = false;
VR_Gun->SetupAttachment(R_MotionController);
VR_Gun->SetRelativeRotation(FRotator(0.0f, -90.0f, 0.0f));

VR_MuzzleLocation = CreateDefaultSubobject<USceneComponent>(TEXT("VR_MuzzleLocation"));
VR_MuzzleLocation->SetupAttachment(VR_Gun);
VR_MuzzleLocation->SetRelativeLocation(FVector(0.000004, 53.999992, 10.000000));
VR_MuzzleLocation->SetRelativeRotation(FRotator(0.0f, 90.0f, 0.0f));		// Counteract the rotation of the VR gun model.

// Uncomment the following line to turn motion controllers on by default:
//bUsingMotionControllers = true;

}

void AMyProject3Character::BeginPlay()
{
// Call the base class
Super::BeginPlay();

FullHealth = 1000.0f;
Health = FullHealth;
HealthPercentage = 1.0f;
bCanBeDamaged = true;

FullMagic = 100.0f;
Magic = FullMagic;
MagicPercentage = 1.0f;
PreviousMagic = MagicPercentage;
MagicValue = 0.0f;
bCanUseMagic = true;

if (MagicCurve)
{
	FOnTimelineFloat TimelineCallback;
	FOnTimelineEventStatic TimelineFinishedCallback;

	TimelineCallback.BindUFunction(this, FName("SetMagicValue"));
	TimelineFinishedCallback.BindUFunction(this, FName("SetMagicState"));
	MyTimeline.AddInterpFloat(MagicCurve, TimelineCallback);
	MyTimeline.SetTimelineFinishedFunc(TimelineFinishedCallback);
}



//Attach gun mesh component to Skeleton, doing it here because the skeleton is not yet created in the constructor
FP_Gun->AttachToComponent(Mesh1P, FAttachmentTransformRules(EAttachmentRule::SnapToTarget, true), TEXT("GripPoint"));

// Show or hide the two versions of the gun based on whether or not we're using motion controllers.
if (bUsingMotionControllers)
{
	VR_Gun->SetHiddenInGame(false, true);
	Mesh1P->SetHiddenInGame(true, true);
}
else
{
	VR_Gun->SetHiddenInGame(true, true);
	Mesh1P->SetHiddenInGame(false, true);
}

}

void AMyProject3Character::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);

MyTimeline.TickTimeline(DeltaTime);

}

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

void AMyProject3Character::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent)
{
// Bind DisplayRaycast event
PlayerInputComponent->BindAction(“Raycast”, IE_Pressed, this, &AMyProject3Character::DisplayRaycast);

// set up gameplay key bindings
check(PlayerInputComponent);

// Bind jump events
PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &ACharacter::Jump);
PlayerInputComponent->BindAction("Jump", IE_Released, this, &ACharacter::StopJumping);

// Bind fire event
PlayerInputComponent->BindAction("Fire", IE_Pressed, this, &AMyProject3Character::OnFire);

// Enable touchscreen input
EnableTouchscreenMovement(PlayerInputComponent);

PlayerInputComponent->BindAction("ResetVR", IE_Pressed, this, &AMyProject3Character::OnResetVR);

// Bind movement events
PlayerInputComponent->BindAxis("MoveForward", this, &AMyProject3Character::MoveForward);
PlayerInputComponent->BindAxis("MoveRight", this, &AMyProject3Character::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
PlayerInputComponent->BindAxis("Turn", this, &APawn::AddControllerYawInput);
PlayerInputComponent->BindAxis("TurnRate", this, &AMyProject3Character::TurnAtRate);
PlayerInputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput);
PlayerInputComponent->BindAxis("LookUpRate", this, &AMyProject3Character::LookUpAtRate);

}

void AMyProject3Character::DisplayRaycast() // Display Raycast is a member of character class DisplayRaycast is now being defined
{
FHitResult* Hitresult = new FHitResult();
FVector StartTrace = FirstPersonCameraComponent->GetComponentLocation(); //Using location of the camera as the starting point for the raycast
FVector ForwardVector = FirstPersonCameraComponent->GetForwardVector(); // Tracing a line from the forward direction from any starting point
FVector EndTrace = ((ForwardVector * 3319.f) + StartTrace); //The end point of the line, forward vector * 3319 + the starting point
FCollisionQueryParams* TraceParams = new FCollisionQueryParams(); //What the line collides with, does it stop? or continue?

if (GetWorld()->LineTraceSingleByChannel(*Hitresult, StartTrace, EndTrace,
	ECC_Visibility, *TraceParams))// Single line using channel ECC_Visiblity
{
	if (Hitresult->Actor.IsValid())
	{
		DrawDebugLine(GetWorld(), StartTrace, EndTrace, FColor(255, 0, 0), true); // rendering the trace
		GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("You hit: %s"),
			*Hitresult->Actor->GetName())); //If we hit something it will will tell us what we hit
	}
}

}

void AMyProject3Character::OnFire()
{
// try and fire a projectile
if (ProjectileClass != nullptr && !FMath::IsNearlyZero(Magic, 0.001f) && bCanUseMagic)
{
UWorld* const World = GetWorld();
if (World != nullptr)
{
if (bUsingMotionControllers)
{
const FRotator SpawnRotation = VR_MuzzleLocation->GetComponentRotation();
const FVector SpawnLocation = VR_MuzzleLocation->GetComponentLocation();
World->SpawnActor(ProjectileClass, SpawnLocation, SpawnRotation);
}
else
{
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 = ((FP_MuzzleLocation != nullptr) ? FP_MuzzleLocation->GetComponentLocation() : GetActorLocation()) + SpawnRotation.RotateVector(GunOffset);

			//Set Spawn Collision Handling Override
			FActorSpawnParameters ActorSpawnParams;
			ActorSpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButDontSpawnIfColliding;

			// spawn the projectile at the muzzle
			World->SpawnActor<AMyProject3Projectile>(ProjectileClass, SpawnLocation, SpawnRotation, ActorSpawnParams);
		}

	}

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

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

	
		MyTimeline.Stop();
		GetWorldTimerManager().ClearTimer(MagicTimerHandle);
		SetMagicChange(-20.0f);
		GetWorldTimerManager().SetTimer(MagicTimerHandle, this, &AMyProject3Character::UpdateMagic, 5.0f, false);
	}

}

void AMyProject3Character::OnResetVR()
{
UHeadMountedDisplayFunctionLibrary::ResetOrientationAndPosition();
}

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

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

//Commenting this section out to be consistent with FPS BP template.
//This allows the user to turn without using the right virtual joystick

//void AMyProject12Character::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 (FMath::Abs(ScaledDelta.X) >= 4.0 / ScreenSize.X)
// {
// TouchItem.bMoved = true;
// float Value = ScaledDelta.X * BaseTurnRate;
// AddControllerYawInput(Value);
// }
// if (FMath::Abs(ScaledDelta.Y) >= 4.0 / ScreenSize.Y)
// {
// TouchItem.bMoved = true;
// float Value = ScaledDelta.Y * BaseTurnRate;
// AddControllerPitchInput(Value);
// }
// TouchItem.Location = Location;
// }
// TouchItem.Location = Location;
// }
// }
// }
//}

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

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

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

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

bool AMyProject3Character::EnableTouchscreenMovement(class UInputComponent* PlayerInputComponent)
{
if (FPlatformMisc::SupportsTouchInput() || GetDefault()->bUseMouseForTouch)
{
PlayerInputComponent->BindTouch(EInputEvent::IE_Pressed, this, &AMyProject3Character::BeginTouch);
PlayerInputComponent->BindTouch(EInputEvent::IE_Released, this, &AMyProject3Character::EndTouch);

	//Commenting this out to be more consistent with FPS BP template.
	//PlayerInputComponent->BindTouch(EInputEvent::IE_Repeat, this, &AMyProject12Character::TouchUpdate);
	return true;
}

return false;

}

float AMyProject3Character::GetHealth()
{
return HealthPercentage;
}

float AMyProject3Character::GetMagic()
{
return MagicPercentage;
}

FText AMyProject3Character::GetHealthIntText()
{
int32 HP = FMath::RoundHalfFromZero(HealthPercentage * 100);
FString HPS = FString::FromInt(HP);
FString HealthHUD = HPS + FString(TEXT("%"));
FText HPTEXT = FText::FromString(HealthHUD);
return HPTEXT;
}

FText AMyProject3Character::GetMagicIntText()
{
int32 MP = FMath::RoundHalfFromZero(MagicPercentage * 100);
FString MPS = FString::FromInt(MP);
FString FullMPS = FString::FromInt(FullMagic);
FString MagicHUD = MPS + FString(TEXT("/") + FullMPS);
FText MagicTEXT = FText::FromString(MagicHUD);
return MagicTEXT;
}

void AMyProject3Character::SetDamageState()
{
bCanBeDamaged = true;
}

void AMyProject3Character::DamageTimer()
{
GetWorldTimerManager().SetTimer(MemberTimerHandle, this, &AMyProject3Character::SetDamageState, 2.0f, false);
}

void AMyProject3Character::SetMagicValue()
{
TimelineValue = MyTimeline.GetPlaybackPosition();
CurveFloatValue = PreviousMagic + MagicValue * MagicCurve->GetFloatValue(TimelineValue);
Magic = CurveFloatValue * FullHealth;
Magic = FMath::Clamp(Magic, 0.0f, FullMagic);
MagicPercentage = CurveFloatValue;
MagicPercentage = FMath::Clamp(MagicPercentage, 0.0f, 1.0f);
}

void AMyProject3Character::SetMagicState()
{
bCanUseMagic = true;
MagicValue = 0.0f;
if (GunDefaultMaterial)
{
FP_Gun->SetMaterial(0, GunDefaultMaterial);
}
}

bool AMyProject3Character::PlayFlash()
{
if (redFlash)
{
redFlash = false;
return true;
}
return false;
}

void AMyProject3Character::ReceivePointDamage(float Damage, const UDamageType* DamageType, FVector HitLocation, FVector HitNormal, UPrimitiveComponent* HitComponent,
FName BoneName, FVector ShotFromDirection, AController* InstigatedBy, AActor* DamageCauser, const FHitResult& HitInfo)
{
bCanBeDamaged = false;
redFlash = true;
UpdateHealth(-Damage);
DamageTimer();
}

void AMyProject3Character::UpdateHealth(float HealthChange)
{
Health += HealthChange;
Health = FMath::Clamp(Health, 0.0f, FullHealth);
HealthPercentage = Health / FullHealth;

}

void AMyProject3Character::UpdateMagic()
{
PreviousMagic = MagicPercentage;
MagicPercentage = Magic/FullMagic;
MagicValue = 1.0f;
MyTimeline.PlayFromStart();
}

void AMyProject3Character::SetMagicChange(float MagicChange)
{
bCanUseMagic = false;
PreviousMagic = MagicPercentage;
MagicValue = MagicChange / FullMagic;
if (GunOverheatMaterial)
{
FP_Gun->SetMaterial(0, GunOverheatMaterial);
}

MyTimeline.PlayFromStart();

}

Thank you for any help that you can give me in advance! It is greatly appreciated.

Can you:

  • reformat the code so it’s more legible? You’ll increase your chances that others decide to look into it
  • limit the code to the relevant pieces? It looks like you posted the entire character class here
  • show all of the relevant code? I don’t see anything in there that actually deals with a health bar, which I assume is a widget somewhere?
  • explain what “can’t get my health bar to work” means in more detail? What doesn’t work? What are you expecting and what are you seeing?

The health bar shows up on the screen but when my character is expected to take damage the health bar does not decrease.

FullHealth = 1000.0f;
Health = FullHealth;
HealthPercentage = 1.0f;
bCanBeDamaged = true;

float AMyProject3Character::GetHealth()
{
return HealthPercentage;
}

float AMyProject3Character::GetMagic()
{
return MagicPercentage;
}

FText AMyProject3Character::GetHealthIntText()
{
int32 HP = FMath::RoundHalfFromZero(HealthPercentage * 100);
FString HPS = FString::FromInt(HP);
FString HealthHUD = HPS + FString(TEXT("%"));
FText HPTEXT = FText::FromString(HealthHUD);
return HPTEXT;
}

void AMyProject3Character::SetDamageState()
{
bCanBeDamaged = true;
}

void AMyProject3Character::DamageTimer()
{
GetWorldTimerManager().SetTimer(MemberTimerHandle, this, &AMyProject3Character::SetDamageState, 2.0f, false);
}

void AMyProject3Character::ReceivePointDamage(float Damage, const UDamageType* DamageType, FVector HitLocation, FVector HitNormal, UPrimitiveComponent* HitComponent,
FName BoneName, FVector ShotFromDirection, AController* InstigatedBy, AActor* DamageCauser, const FHitResult& HitInfo)
{
bCanBeDamaged = false;
redFlash = true;
UpdateHealth(-Damage);
DamageTimer();
}

void AMyProject3Character::UpdateHealth(float HealthChange)
{
Health += HealthChange;
Health = FMath::Clamp(Health, 0.0f, FullHealth);
HealthPercentage = Health / FullHealth;

}

these are the applicable pieces of code to my characters health. Thank you for your help and-rad.

I still don’t see any code related to the health bar. How is it updated? The code you posted just updates the character’s own internal health variable. How does the health bar know when this value has changed and where does it call GetHealth or GetHealthIntText?

The health bar itself is done through Blueprints:

And this function is bound to the health bar’s Percent variable? Have you checked

  • whether the binding is still valid? You can see that in the widget’s details panel
  • whether the function gets called at all?
  • whether the cast to your character class fails?

I’d also like to add that this way of updating your UI is not the most performant and events/ delegates are almost always the better solution.

yes, it’s still bound to the percent variable. How would I check to see if the cast is failing?

Hook up the Cast Failed output to a Print String node and see if it prints the string.