2024-04-25
깃허브! |
---|
https://github.com/ChangJin-Lee/ARproject |
★ https://github.com/ChangJin-Lee/ToonTank |
느낀점
Delegate 개념에 대해서 자세하게 알아보았다. c++ 오브젝트 상의 멤버 함수 호출을 하는 방식이었다. 그리고 그 델리게이트를 Serialize 가능하며, 그 함수를 이름으로 찾을 수도 있는 방식이 Dynamic Delegate이었다.
C++ 오브젝트 상의 멤버 함수를 가리키고 실행시키는 데이터 유형입니다.
Delegate (델리게이트)로 C++ 오브젝트 상의 멤버 함수 호출을 일반적이고 유형적으로 안전한 방식으로 할 수 있습니다. 델리게이트를 사용하여 임의 오브젝트의 멤버 함수에 동적으로 바인딩시킬 수 있으며, 그런 다음 그 오브젝트에서 함수를 호출할 수 있습니다. 호출하는 곳에서 오브젝트의 유형을 몰라도 말이지요.
델리게이트 오브젝트는 복사해도 완벽히 안전합니다. 델리게이트는 값으로 전달 가능하나 보통 추천할 만 하지는 않는데, heap 에 메모리를 할당해야 하기 때문입니다. 가급적이면 델리게이트는 항상 참조 전달해야 합니다.
델리게이트는 싱글-캐스트(형 변환)와 멀티-캐스트 모두 지원되며, 디스크에 안전하게 Serialize 시킬 수 있는 "다이내믹" 델리게이트도 물론입니다.
class FLogWriter
{
void WriteToLog( FString );
};
DECLARE_DELEGATE_OneParam( FStringDelegate, FString );
class FMyClass
{
FStringDelegate WriteToLogDelegate;
};
TSharedRef< FLogWriter > LogWriter( new FLogWriter() );
WriteToLogDelegate.BindSP( LogWriter, &FLogWriter::WriteToLog );
WriteToLogDelegate.Execute( TEXT( "델리게이트 쥑이네!" ) );
WriteToLogDelegate.ExecuteIfBound( TEXT( "함수가 바인딩되었을 때만 실행!" ) );
private:
UPROPERTY(EditDefaultsOnly, Category = "Combat")
class UStaticMeshComponent* ProjectileMesh;
AProjectile::AProjectile()
{
PrimaryActorTick.bCanEverTick = false;
ProjectileMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Projectile Mesh"));
RootComponent = ProjectileMesh;
}
void AProjectile::BeginPlay()
{
Super::BeginPlay();
ProjectileMesh->OnComponentHit.AddDynamic(this, &AProjectile::OnHit);
}
void AProjectile::OnHit(UPrimitiveComponent* HitComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit)
{
AActor* MyOwner = GetOwner();
if(MyOwner==nullptr)
{
Destroy();
return;
}
AController* MyOwnerInstigator = MyOwner->GetInstigatorController();
UClass* DamageTypeClass = UDamageType::StaticClass();
if(OtherActor && OtherActor != this && OtherActor != MyOwner)
{
UGameplayStatics::ApplyDamage(OtherActor, Damage, MyOwnerInstigator, this, DamageTypeClass);
if(HitParticles)
{
UGameplayStatics::SpawnEmitterAtLocation(this, HitParticles, GetActorLocation(), GetActorRotation());
}
if(HitSound)
{
UGameplayStatics::PlaySoundAtLocation(this, HitSound, GetActorLocation());
}
if(HitCameraShakeClass)
{
GetWorld()->GetFirstPlayerController()->ClientStartCameraShake(HitCameraShakeClass);
}
}
Destroy();
}
void UHealthComponent::BeginPlay()
{
Super::BeginPlay();
Health = MaxHealth;
GetOwner()->OnTakeAnyDamage.AddDynamic(this, &UHealthComponent::DamageTaken);
ToonTanksGameMode = Cast<AToonTanksGameMode>(UGameplayStatics::GetGameMode(this));
}
void UHealthComponent::DamageTaken(AActor* DamagedActor, float Damage, const UDamageType* DamageType, class AController* Instigator, AActor* DamageCauser)
{
if(Damage <= 0.f) return;
Health -= Damage;
UE_LOG(LogTemp, Warning, TEXT("Health: %f"), Health);
if( Health <= 0 && ToonTanksGameMode)
{
ToonTanksGameMode->ActorDied(DamagedActor);
}
}
실제 예시
FTimer로 Time Delegate를 구현한 사례이다.
void AToonTanksGameMode::HandleGameStart()
{
TargetTowers = GetTargetTower();
Tank = Cast<ATank>(UGameplayStatics::GetPlayerPawn(this,0));
ToonTanksPlayerController = Cast<AToonTanksPlayerController>(UGameplayStatics::GetPlayerController(this,0));
StartGame();
if (ToonTanksPlayerController)
{
ToonTanksPlayerController->SetPlayerEnabledState(false);
FTimerHandle PlayerEnableTimerHandle;
FTimerDelegate PlayerEnableTimerDelegate = FTimerDelegate::CreateUObject(
ToonTanksPlayerController,
&AToonTanksPlayerController::SetPlayerEnabledState,
true
);
GetWorldTimerManager().SetTimer(PlayerEnableTimerHandle,
PlayerEnableTimerDelegate,
StartDelay,
false
);
}
}