#include "HealthComponent.h" #include "PlayerCharacter.h" #include "Components/WidgetComponent.h" #include "UObject/FastReferenceCollector.h" UHealthComponent::UHealthComponent() { PrimaryComponentTick.bCanEverTick = true; Owner = GetOwner(); } // Called when the game starts void UHealthComponent::BeginPlay() { Super::BeginPlay(); CurrentHealth = MaxHealth; bIsInvulnerable = false; UWorld* World = GetWorld(); if (World) { PlayerController = UGameplayStatics::GetPlayerController(GetWorld(), 0); } if (Owner->IsA(APlayerCharacter::StaticClass())) { IsPlayer = true; } if (!HealthBarComponent) { HealthBarComponent = NewObject(Owner); HealthBarComponent->RegisterComponent(); HealthBarComponent->AttachToComponent(Owner->GetRootComponent(), FAttachmentTransformRules::KeepRelativeTransform); } if (HealthBarClass) { HealthBarComponent->SetWidgetClass(HealthBarClass); HealthBarComponent->SetVisibility(false); } HealthBarWidget = Cast(HealthBarComponent->GetWidget()); if (HealthBarWidget) { // HealthBarWidget->UpdateHealth(CurrentHealth/MaxHealth); } else { UE_LOG(LogTemp, Error, TEXT("HealthBarWidget is nullptr!")); } if (bShowDamageFeedback && !IsPlayer) { StaticMeshComponent = Owner->FindComponentByClass(); OriginalMaterial = StaticMeshComponent->GetMaterial(0); } if (bShowDamageFeedback && IsPlayer) { TArray SkeletalMeshComponents; Owner->GetComponents(SkeletalMeshComponents); for (USkeletalMeshComponent* SkeletalMesh : SkeletalMeshComponents) { if (SkeletalMesh->GetSkeletalMeshAsset()) { SkeletalMeshComponent = SkeletalMesh; OriginalMaterial = SkeletalMeshComponent->GetMaterial(0); break; } } } } void UHealthComponent::EndPlay(const EEndPlayReason::Type EndPlayReason) { Super::EndPlay(EndPlayReason); if (GetWorld()) { GetWorld()->GetTimerManager().ClearAllTimersForObject(this); } SkeletalMeshComponent = nullptr; StaticMeshComponent = nullptr; HealthBarComponent = nullptr; } void UHealthComponent::TakeDamage(float const Value) { if (!IsValid(Owner)|| bIsInvulnerable || !PlayerController || CurrentHealth <= 0 || Value <= 0) { return; } if (!HealthBarComponent->IsVisible() && bShowHealthBar) { HealthBarComponent->SetVisibility(true); } CurrentHealth -= Value; CurrentHealth = FMath::Clamp(CurrentHealth, 0.0f, MaxHealth); OnHealthChanged.Broadcast(this, CurrentHealth, MaxHealth); // Non-player health bar rotation if (HealthBarWidget && PlayerController && !IsPlayer) { if (APawn* PlayerPawn = PlayerController->GetPawn()) { FVector CameraLocation = PlayerController->PlayerCameraManager->GetCameraLocation(); FVector Direction = (CameraLocation - HealthBarComponent->GetComponentLocation()).GetSafeNormal(); FRotator NewRotation = Direction.Rotation(); HealthBarComponent->SetWorldRotation(NewRotation); HealthBarWidget->UpdateHealth(CurrentHealth/MaxHealth); } } // Player damage feedback if (bShowDamageFeedback && IsPlayer && SkeletalMeshComponent && DamageFeedbackMaterial && GetWorld()) { bIsInvulnerable = true; SkeletalMeshComponent->SetMaterial(0, DamageFeedbackMaterial); FTimerHandle TimerHandle; GetWorld()->GetTimerManager().SetTimer(TimerHandle, [this]() { if (IsValid(Owner) && IsValid(SkeletalMeshComponent)) { SkeletalMeshComponent->SetMaterial(0, OriginalMaterial); bIsInvulnerable = false; } }, DamageFeedbackDuration, false); } else if (bShowDamageFeedback && !IsPlayer && StaticMeshComponent && DamageFeedbackMaterial && GetWorld()) { StaticMeshComponent->SetMaterial(0, DamageFeedbackMaterial); FTimerHandle TimerHandle; GetWorld()->GetTimerManager().SetTimer(TimerHandle, [this]() { if (StaticMeshComponent) { StaticMeshComponent->SetMaterial(0, OriginalMaterial); } }, DamageFeedbackDuration, false); } if (CurrentHealth <= 0) { // Depends on the actor (if player or something else) if (Owner->IsA(APlayerCharacter::StaticClass())) { UE_LOG(LogTemp, Warning, TEXT("Player died!")); // Load game over menu } else { UE_LOG(LogTemp, Warning, TEXT("Actor died!")); Owner->Destroy(); } } } void UHealthComponent::Heal(float const Value) { CurrentHealth += Value; CurrentHealth = FMath::Clamp(CurrentHealth, 0.0f, MaxHealth); OnHealthChanged.Broadcast(this, CurrentHealth, MaxHealth); }