위 코스를 바탕으로 개인적인 정리를 위해 작성된 글입니다.
Actor을 Actor Component에서 Sin파동을 이용하여 이동시키는 방법
강의 제목 : Actor Component - Floating Mechanic
Actor Component를 생성하고, FMath를 사용하여 Sin파에 진폭과 움직일 범위(FVector)를 정해주고
ActorComonent의 부모(Actor)의 위치를 바꾸어 줍니다.
FloatingComponent.h
#pragma once
#include "CoreMinimal.h"
#include "Components/SceneComponent.h"
#include "FloatingComponent.generated.h"
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class COMPONENTSOFGAMEPLAY_API UFloatingComponent : public USceneComponent
{
GENERATED_BODY()
public:
// Sets default values for this component's properties
UFloatingComponent();
// Set Range to Move
UPROPERTY(EditAnywhere, Category = "Floating")
FVector FloatRange = FVector(0, 0, 1);
// Set Speed to Move
UPROPERTY(EditAnywhere, Category = "Floating")
float SpeedMultiplier = 1.f;
protected:
// Called when the game starts
virtual void BeginPlay() override;
public:
// Called every frame
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) override;
private:
AActor *Parent = nullptr;
FVector StartLocation;
};
FloatingComponent.cpp
#include "FloatingComponent.h"
#include "Math/UnrealMathUtility.h"
// 기본 생성자
UFloatingComponent::UFloatingComponent()
{
// 이 컴포넌트를 매 프레임마다 틱을 받도록 설정
PrimaryComponentTick.bCanEverTick = true;
}
// 플레이 시작 시 호출
void UFloatingComponent::BeginPlay()
{
Super::BeginPlay();
// 부모 액터 가져오기
Parent = GetOwner();
if (Parent)
{
// 플레이 시작 시 부모 클래스의 위치를 시작 지점으로 저장
StartLocation = Parent->GetActorLocation();
// 이 컴포넌트를 사용하는 모든 오브젝트는 움직임이 가능해야 하므로 움직임 속성을 '이동 가능'으로 설정하여
// 경고 메시지를 방지
Parent->GetRootComponent()->SetMobility(EComponentMobility::Movable);
}
}
// 매 프레임마다 호출
void UFloatingComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
// 현재 시간 저장
const float Time = GetWorld()->GetRealTimeSeconds();
// 사인파 업데이트
const float Sine = FMath::Sin(Time * SpeedMultiplier);
// 부모 액터가 있는지 확인
if (Parent)
{
// 부모의 위치 업데이트. ActorComponent는 자체적인 변환(Transform)을 가지지 않음을 기억하세요.
Parent->SetActorLocation(StartLocation + (FloatRange * Sine));
}
}
Actor을 Scene Component에서 Hover 적용하기
강의 제목 : Scene Component - Hover Mechanic
Scene Component를 생성합니다. (HoverComponent)
부모를 찾아서 위치 정보와 Movable로 전환해주고, 아래로 LineTraceSingleByChannel을 통해서 거리를 측정한다.
이후 거리와 MaxHoverForce, HoverForceDistance를 통해서 AddForce를 통해 Actor를 움직이게 합니다.
HoverComponent.h
#pragma once
#include "CoreMinimal.h"
#include "Components/SceneComponent.h"
#include "HoverComponent.generated.h"
UCLASS(ClassGroup = (Custom), meta = (BlueprintSpawnableComponent))
class TOPDOWNSHOOTER_API UHoverComponent : public USceneComponent
{
GENERATED_BODY()
public:
// Sets default values for this component's properties
UHoverComponent();
protected:
// Called when the game starts
virtual void BeginPlay() override;
public:
// Called every frame
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) override;
private:
UPrimitiveComponent *ParentRoot;
UPROPERTY(EditAnywhere, Category = "Hover Variables")
float MaxHoverForce = 100.f;
UPROPERTY(EditAnywhere, Category = "Hover Variables")
float HoverForceDistance = 100.f;
UPROPERTY(EditAnywhere, Category = "Debug")
bool bDrawDebug = false;
};
HoverComponent.cpp
#include "HoverComponent.h"
#include "Engine/EngineTypes.h"
#include "DrawDebugHelpers.h"
// 이 컴포넌트의 속성에 대한 기본값 설정
UHoverComponent::UHoverComponent()
{
// 게임이 시작할 때 이 컴포넌트를 초기화하고, 매 프레임마다 틱을 받도록 설정합니다.
// 필요하지 않다면 이 기능들을 끄고 성능을 개선할 수 있습니다.
PrimaryComponentTick.bCanEverTick = true;
// ...
}
// 게임 시작 시 호출
void UHoverComponent::BeginPlay()
{
Super::BeginPlay();
// GetComponentByClass() 대신 FindComponentByClass를 사용하여
// 클래스를 매개변수로 제공하는 대신 템플릿을 가져올 수 있습니다.
ParentRoot = GetOwner()->FindComponentByClass<UPrimitiveComponent>();
if (ParentRoot)
{
// 런타임에 이 타입의 컴포넌트가 필요로 하는 기본값을 설정합니다.
// 물리 시뮬레이션을 하지 않으면 런타임에 경고가 발생할 수 있습니다.
ParentRoot->SetMobility(EComponentMobility::Movable);
ParentRoot->SetSimulatePhysics(true);
// 선택 사항이지만, 이 컴포넌트를 사용하는 모든 클래스가 유사한 동작을 갖도록 도움이 됩니다.
ParentRoot->SetLinearDamping(2.0f);
ParentRoot->SetAngularDamping(2.0f);
// 큰 힘을 직접 입력하는 대신, 더 합리적인 힘에 개별 클래스 루트 컴포넌트의 질량을 곱합니다.
MaxHoverForce = MaxHoverForce * ParentRoot->GetMass();
}
}
// 매 프레임마다 호출
void UHoverComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
FHitResult Hit;
GetWorld()->LineTraceSingleByChannel(Hit, GetComponentLocation(), GetComponentLocation() - FVector(0, 0, HoverForceDistance),
ECollisionChannel::ECC_Visibility);
if (Hit.bBlockingHit)
{
const FVector StartLocation = GetComponentLocation();
const float Distance = (StartLocation - Hit.Location).Size();
// 거리를 0과 1 사이로 제한합니다.
const float Ratio = FMath::Clamp(Distance / HoverForceDistance, 0.0f, 1.0f);
const FVector Force = (1.0f - Ratio) * MaxHoverForce * Hit.ImpactNormal;
ParentRoot->AddForce(Force * 20, NAME_None, false);
if (bDrawDebug)
{
// 디버그 라인 그리기
DrawDebugLine(GetWorld(), StartLocation, Hit.Location, FColor::White, false, 0.0f);
DrawDebugLine(GetWorld(), Hit.Location, Hit.Location, FColor::Black, false, 0.0f);
}
}
}
C++에서 컴포넌트 추가하기
IHealthInterface와 AActor를 상속하는 DemoActor를 Mesh, HealthComp, DestructionComp를 소유하게 CreateDefaultSubobject를 통해 생성
DemoActor.h
#pragma once
#include "CoreMinimal.h"
#include "ComponentsOfGameplay/Interfaces/HealthInterface.h"
#include "GameFramework/Actor.h"
#include "DemoActor.generated.h"
// IHealthInterface 인터페이스를 다중 상속!
UCLASS()
class COMPONENTSOFGAMEPLAY_API ADemoActor : public AActor, public IHealthInterface
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
ADemoActor();
// ---------- FUNCTIONS ---------- //
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Health")
void HealthDepleted();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
UPROPERTY(EditAnywhere, Category = "Components")
UStaticMeshComponent *Mesh;
UPROPERTY(EditAnywhere, Category = "Components")
class UHealthComponent *HealthComp;
UPROPERTY(EditAnywhere, Category = "Components")
class UDestructionComponent *DestructionComp;
};
DemoActor.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "DemoActor.h"
#include "ComponentsOfGameplay/Components/HealthComponent.h"
#include "ComponentsOfGameplay/Components/DestructionComponent.h"
// Sets default values
ADemoActor::ADemoActor()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = false;
// 주의! 복붙하다가 TEXT를 똑같이 해서 크래쉬 발생
HealthComp = CreateDefaultSubobject<UHealthComponent>(TEXT("HealthComp"));
DestructionComp = CreateDefaultSubobject<UDestructionComponent>(TEXT("Destruction Comp"));
Mesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
RootComponent = Mesh;
}
// Called when the game starts or when spawned
void ADemoActor::BeginPlay()
{
Super::BeginPlay();
}
void ADemoActor::HealthDepleted_Implementation()
{
Destroy();
}
플레이어 호밍 기능 추가하기
Overlap을 이용하여 Sphere 범위안에 들어올경우, 플레이어(배열)을 따라갑니다. 범위에서 나갈경우 비활성화 됩니다.
AddDynamic을 통해 Overlap에 함수를 바인딩(리플리케이션) 시키고
SetCollisionResponseToAllChannels을 통해 Overlap로 만들고 Homing에 SetupAttachment 해줍니다.
SetRelativeLocation(FVector(0.f, 0.f, 0.f))를 통해서 상대 위치를 zero Vector로 만들어줍니다.
HomingComponent.h
#pragma once
#include "CoreMinimal.h"
#include "Components/SceneComponent.h"
#include "HomingComponent.generated.h"
class USphereComponent;
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class COMPONENTSOFGAMEPLAY_API UHomingComponent : public USceneComponent
{
GENERATED_BODY()
public:
// ---------- 함수들 ---------- //
// 이 컴포넌트의 속성에 대한 기본값을 설정합니다.
UHomingComponent();
// 매 프레임마다 호출됩니다.
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
// ---------- 변수들 ---------- //
// 부모 액터가 겹칠 때 이동할 액터 타입들.
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Homing")
TArray<TSubclassOf<AActor>> AttractActors;
// 호밍을 초기화하기 위한 겹침 검사 반경.
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Homing")
float MagneticSphereRadius = 500.0f;
// 호밍 중 이동 속도.
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Homing")
float HomingSpeed = 1.0f;
protected:
// ---------- 함수들 ---------- //
// 게임이 시작될 때 호출됩니다.
virtual void BeginPlay() override;
UFUNCTION(Category = "Homing")
void OnOverlapEnter(class UPrimitiveComponent* ThisComp, class AActor* OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
UFUNCTION(Category= "Homing")
void OnEndOverlap(class UPrimitiveComponent* ThisComp, class AActor* OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);
// 컴포넌트가 파괴될 때 호출됩니다, 에디터 내에서도 처리됩니다.
virtual void OnComponentDestroyed(bool bDestroyingHierarchy) override;
// 에디터에서 변경 사항을 보기 위한 유용한 함수 호출. BeginPlay()에서 많은 업데이트를 추가하는 것보다 성능을 절약합니다.
virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
// ---------- 변수들 ---------- //
AActor* Owner = nullptr;
AActor* LockedActor = nullptr;
bool bIsHoming = false;
UPROPERTY(EditAnywhere, Category="Homing")
USphereComponent* MagnetSphere = nullptr;
};
- 호밍 기능을 제공하는 언리얼 엔진 컴포넌트입니다. 클래스는 호밍 대상 액터, 호밍 반경, 호밍 속도 등의 속성을 정의하고 있으며, 겹침 이벤트에 대한 콜백 함수도 포함하고 있습니다.
HomingComponent.cpp
#include "HomingComponent.h"
#include "Components/SphereComponent.h"
#include "DrawDebugHelpers.h"
// 이 컴포넌트의 속성에 대한 기본값을 설정합니다.
UHomingComponent::UHomingComponent()
{
// 이 컴포넌트를 게임이 시작될 때 초기화하고, 매 프레임마다 틱(tick)하도록 설정합니다.
// 이 기능들을 끄면 성능을 개선할 수 있습니다(필요하지 않다면).
PrimaryComponentTick.bCanEverTick = true;
// 서브 씬 컴포넌트 생성
MagnetSphere = CreateDefaultSubobject<USphereComponent>("Magnet Sphere");
// 기본 Begin과 End Overlap 이벤트에 함수 호출을 바인딩합니다.
MagnetSphere->OnComponentBeginOverlap.AddDynamic(this, &UHomingComponent::OnOverlapEnter);
MagnetSphere->OnComponentEndOverlap.AddDynamic(this, &UHomingComponent::OnEndOverlap);
// SphereComponent의 중요한 속성들을 설정합니다.
MagnetSphere->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Overlap);
MagnetSphere->SetupAttachment(this);
MagnetSphere->SetRelativeLocation(FVector(0.f, 0.f, 0.f));
// 선택적입니다만, 기본값으로 32 유닛이 설정되어 있습니다.
// 자기장 반경의 바로 감지를 위해 좋습니다.
MagnetSphere->SetSphereRadius(MagneticSphereRadius);
Owner = GetOwner();
}
// 게임이 시작될 때 호출됩니다.
void UHomingComponent::BeginPlay()
{
Super::BeginPlay();
}
// 매 프레임마다 호출됩니다.
void UHomingComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
if(bIsHoming && LockedActor && Owner)
{
Owner->SetActorLocation(FMath::VInterpTo(this->GetComponentLocation(),LockedActor->GetActorLocation(), DeltaTime, HomingSpeed));
}
DrawDebugSphere(GetWorld(), MagnetSphere->GetComponentLocation(), MagnetSphere->GetScaledSphereRadius(), 10, FColor::Orange, false, 0);
}
void UHomingComponent::OnOverlapEnter(UPrimitiveComponent* ThisComp, AActor* OtherActor, UPrimitiveComponent* OtherComp,
int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
if(AttractActors.Contains(OtherActor->GetClass()))
{
LockedActor = OtherActor;
bIsHoming = true;
UE_LOG(LogTemp, Warning, TEXT("Locked Actor %s"), *LockedActor->GetName());
}
}
void UHomingComponent::OnEndOverlap(UPrimitiveComponent* ThisComp, AActor* OtherActor, UPrimitiveComponent* OtherComp,
int32 OtherBodyIndex)
{
if(LockedActor != nullptr && LockedActor == OtherActor)
{
UE_LOG(LogTemp, Warning, TEXT("Locked Actor unset from %s"), *LockedActor->GetName());
bIsHoming = false;
LockedActor = nullptr;
}
}
void UHomingComponent::OnComponentDestroyed(bool bDestroyingHierarchy)
{
Super::OnComponentDestroyed(bDestroyingHierarchy);
// HomingComponent와 함께 이것을 제거하는 것이 중요합니다.
// 그렇지 않으면 Parent Actor에 남게 됩니다.
MagnetSphere->DestroyComponent();
}
void UHomingComponent::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
Super::PostEditChangeProperty(PropertyChangedEvent);
// MagneticSphereRadius 값과 일치하도록 SphereRadius를 업데이트합니다.
// 에디터에서 플레이 전에 시각화하기 좋습니다.
MagnetSphere->SetSphereRadius(MagneticSphereRadius);
}
OnComponentDestroyed는 Homing이 파괴될 때 MagnetSphere를 같이 제거해 줍니다.(DestroyComponent)
기존에 존재하는 함수이기 때문에 Super를 사용해 줍니다.
void UHomingComponent::OnComponentDestroyed(bool bDestroyingHierarchy)
{
Super::OnComponentDestroyed(bDestroyingHierarchy);
// HomingComponent와 함께 이것을 제거하는 것이 중요합니다.
// 그렇지 않으면 Parent Actor에 남게 됩니다.
MagnetSphere->DestroyComponent();
}
PostEditChangeProperty는 에디터에서 어떤 속성이 변경될 때 호출됩니다. 따라서 MagnetSphereRadius를 변경할 때 뷰포트에서 바로 적용시켜 줄 수 있습니다.
void UHomingComponent::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
Super::PostEditChangeProperty(PropertyChangedEvent);
// MagneticSphereRadius 값과 일치하도록 SphereRadius를 업데이트합니다.
// 에디터에서 플레이 전에 시각화하기 좋습니다.
MagnetSphere->SetSphereRadius(MagneticSphereRadius);
}
DestructionComponent
DestructionComponent.h
// 프로젝트 설정의 설명 페이지에 저작권 고지를 작성하세요.
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "DestructionComponent.generated.h"
class UNiagaraSystem;
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class COMPONENTSOFGAMEPLAY_API UDestructionComponent : public UActorComponent
{
GENERATED_BODY()
public:
// 이 컴포넌트의 속성에 대한 기본값을 설정합니다.
UDestructionComponent();
protected:
// 게임이 시작될 때 호출됩니다.
virtual void BeginPlay() override;
// ---------- 변수들 ---------- //
UPROPERTY(EditAnywhere, Category="Destruction Properties")
bool bExplosive = false;
UPROPERTY(EditAnywhere, Category="Destruction Properties")
bool bDrawDebug = false;
UPROPERTY(EditAnywhere, Category="Destruction Properties")
float ExplosiveRadius = 100.0f;
UPROPERTY(EditAnywhere, Category = "Destruction FX")
UNiagaraSystem* ParticleSystem;
AActor* Owner = nullptr;
// ---------- 함수들 ---------- //
UFUNCTION()
void Destruct(AActor* DestroyedActor);
};
- 이 컴포넌트는 파괴 가능한 액터의 기능을 제공하는 데 사용되며, 폭발성, 디버그 드로잉, 폭발 반경, 파티클 시스템 등의 속성을 포함합니다. Destruct 함수는 주어진 액터를 파괴하는 데 사용됩니다.
DestructionComponent.cpp
#include "DestructionComponent.h"
#include "DrawDebugHelpers.h"
#include "NiagaraComponent.h"
#include "NiagaraFunctionLibrary.h"
// 이 컴포넌트의 속성에 대한 기본값을 설정합니다.
UDestructionComponent::UDestructionComponent()
{
// 이 컴포넌트를 게임이 시작될 때 초기화하고, 매 프레임마다 틱(tick)하도록 설정합니다.
// 이 기능들을 끄면 성능을 개선할 수 있습니다(필요하지 않다면).
PrimaryComponentTick.bCanEverTick = false;
}
// 게임이 시작될 때 호출됩니다.
void UDestructionComponent::BeginPlay()
{
Super::BeginPlay();
Owner = GetOwner();
if(Owner)
{
Owner->OnDestroyed.AddDynamic(this, &UDestructionComponent::Destruct);
}
}
void UDestructionComponent::Destruct(AActor* DestroyedActor)
{
// 파티클 시스템이 선택되었는지 확인하고, 선택된 경우 재생합니다.
if(ParticleSystem)
{
UNiagaraComponent* Effect = UNiagaraFunctionLibrary::SpawnSystemAtLocation(GetWorld(), ParticleSystem, Owner->GetActorLocation());
}
// 모든 파괴 컴포넌트가 폭발적일 필요는 없으므로, 여기서 확인하고 참인 경우 폭발적인 피해를 입힙니다.
if(bExplosive)
{
TArray<FHitResult> HitResults;
const FVector Start = Owner->GetActorLocation();
const FVector End = Owner->GetActorLocation();
const FCollisionShape ColShape = FCollisionShape::MakeSphere(ExplosiveRadius);
const bool Hit = GetWorld()->SweepMultiByChannel(HitResults, Start, End, FQuat::Identity, ECC_Camera,
ColShape);
if(bDrawDebug)
{
DrawDebugSphere(GetWorld(), Start, ExplosiveRadius, 50, FColor::Orange, true);
}
if(Hit)
{
for(FHitResult const HitResult : HitResults)
{
// 주석을 제거하면 어떤 액터들이 히트되었는지 피드백을 받을 수 있습니다.
// GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Orange,
//FString::Printf(TEXT("Hit: %s"), *HitResult.Actor->GetName()));
FPointDamageEvent PointDamage;
HitResult.GetActor()->TakeDamage(100.0f, PointDamage, GetWorld()->GetFirstPlayerController(), GetOwner());
}
}
}
}
HealthInterface, HealthComponent
HealthInterface.h
// 프로젝트 설정의 설명 페이지에 저작권 고지를 작성하세요.
#pragma once
#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "HealthInterface.generated.h"
// 이 클래스는 수정할 필요가 없습니다.
UINTERFACE(MinimalAPI)
class UHealthInterface : public UInterface
{
GENERATED_BODY()
};
/**
*
*/
class COMPONENTSOFGAMEPLAY_API IHealthInterface
{
GENERATED_BODY()
// 이 클래스에 인터페이스 함수를 추가하세요. 이 클래스는 이 인터페이스를 구현하기 위해 상속될 것입니다.
public:
// ---------- 함수들 ---------- //
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category="Health")
void HealthDepleted();
};
- HealthInterface.h 는 언리얼 엔진에서 사용되는 IHealthInterface 인터페이스의 정의를 포함하고 있습니다. 이 인터페이스는 건강(Health)과 관련된 기능을 제공하는 데 사용되며, HealthDepleted라는 함수를 포함하고 있습니다. 이 함수는 체력이 소진됐을 때 호출되는 기능을 구현하는 데 사용됩니다.
- UHealthInterface는 인터페이스에 대한 언리얼 엔진의 구현을 제공하며, 별도의 수정이 필요하지 않습니다.
HealthComponent.h
// 프로젝트 설정의 설명 페이지에 저작권 고지를 작성하세요.
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "HealthComponent.generated.h"
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class COMPONENTSOFGAMEPLAY_API UHealthComponent : public UActorComponent
{
GENERATED_BODY()
public:
// 이 컴포넌트의 속성에 대한 기본값을 설정합니다.
UHealthComponent();
protected:
// 게임이 시작될 때 호출됩니다.
virtual void BeginPlay() override;
// ---------- 변수들 ---------- //
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Health")
float DefaultHealth = 0;
UPROPERTY(BlueprintReadOnly)
float Health = 0;
AActor* Owner = nullptr;
// ---------- 함수들 ---------- //
UFUNCTION()
void TakeDamage(AActor* DamagedActor, float Damage, const class UDamageType* DamageType, class AController* InstigatedBy, AActor* DamageCauser);
};
HealthComponent.cpp
// 프로젝트 설정의 설명 페이지에 저작권 고지를 작성하세요.
#include "HealthComponent.h"
#include "ComponentsOfGameplay/Interfaces/HealthInterface.h"
// 이 컴포넌트의 속성에 대한 기본값을 설정합니다.
UHealthComponent::UHealthComponent()
{
// 이 컴포넌트를 게임이 시작될 때 초기화하고, 매 프레임마다 틱(tick)하도록 설정합니다.
// 이 기능들을 끄면 성능을 개선할 수 있습니다(필요하지 않다면).
PrimaryComponentTick.bCanEverTick = false;
DefaultHealth = 100.0f;
}
// 게임이 시작될 때 호출됩니다.
void UHealthComponent::BeginPlay()
{
Super::BeginPlay();
Owner = GetOwner();
if (Owner)
{
Owner->OnTakeAnyDamage.AddDynamic(this, &UHealthComponent::TakeDamage);
}
Health = DefaultHealth;
}
void UHealthComponent::TakeDamage(AActor *DamagedActor, float Damage, const UDamageType *DamageType,
AController *InstigatedBy, AActor *DamageCauser)
{
// 피해를 받으면 건강 수치를 감소시킵니다.
Health = FMath::Clamp(Health - Damage, 0.0f, DefaultHealth);
if (Health <= 0)
{
IHealthInterface *Interface = Cast<IHealthInterface>(Owner);
if (Interface)
{
Interface->Execute_HealthDepleted(Owner);
}
}
}
HealthInterface를 상속한 DemoActor.cpp
void ADemoActor::HealthDepleted_Implementation()
{
Destroy();
}
인터페이스의 Execute_
Execute_HealthDepleted는 언리얼 엔진에서 사용되는 인터페이스 함수의 실행을 위한 특별한 메서드입니다. 이 메서드는 IHealthInterface 인터페이스에 정의된 HealthDepleted 함수를 구체적으로 실행할 때 사용됩니다. Execute_HealthDepleted는 인터페이스 함수를 구현한 객체에 대해 호출되며, 해당 객체가 건강(Health)이 소진되었을 때 필요한 로직을 실행합니다.
Execute_HealthDepleted의 기본 개념
- 인터페이스 함수 호출: 인터페이스에 정의된 함수는 직접 호출할 수 없기 때문에, 언리얼 엔진은 Execute_ 접두사를 사용한 특별한 함수를 제공합니다. 이를 통해 인터페이스 함수를 실행할 수 있습니다.
- 동적 타입 확인: Execute_HealthDepleted는 객체가 IHealthInterface를 구현했는지 먼저 확인합니다. 이는 Cast 함수를 사용하여 수행됩니다. 객체가 인터페이스를 구현하고 있으면, HealthDepleted 함수를 호출합니다.
Execute_HealthDepleted의 사용 예시
IHealthInterface *Interface = Cast<IHealthInterface>(Owner);
if (Interface)
{
Interface->Execute_HealthDepleted(Owner);
}
이 코드는 다음과 같은 작업을 수행합니다:
- Owner 객체가 IHealthInterface를 구현하고 있는지 확인합니다.
- 만약 구현하고 있다면, HealthDepleted 함수를 호출합니다. 이 함수는 해당 인터페이스를 구현한 객체에서 정의해야 합니다.
주의사항
- 타입 안전성: Execute_HealthDepleted는 객체가 해당 인터페이스를 구현했는지 먼저 확인해야 합니다. 이는 Cast 함수를 사용하여 수행됩니다.
- 인터페이스 구현 필요: 이 메서드를 사용하기 전에, 해당 인터페이스(IHealthInterface 등)를 구현하는 클래스에서 HealthDepleted 함수의 구현이 필요합니다.
Execute_HealthDepleted는 인터페이스 기반 프로그래밍에서 중요한 개념으로, 언리얼 엔진에서 인터페이스를 통한 유연한 코드 구조를 구현하는 데 중요한 역할을 합니다.
'Unreal 공부' 카테고리의 다른 글
[UE] C++ for Blueprinters (0) | 2023.11.27 |
---|---|
언리얼 C++ 기본 타입과 문자열 (1) | 2023.11.03 |
언리얼 엔진 코딩 표준 (0) | 2023.11.02 |
[Unreal] 오버랩 이벤트, 히트 이벤트 (1) | 2023.10.05 |
[Unreal] C++ 파일이 먹통이 됬을때 (and UE 인텔리센스 Fix 펌) (1) | 2023.10.03 |