구현 내용
- CharacterOverlay Class 구현 (Health ProgressBar, Score, Defeats 관련)
- Projectile 피격 구현(Damage)
- BlasterGameMode Class 생성 및 관리 구현
- Elim Animation 및 Dissolve Material 구현
- Elim Bot 구현
- Weapon Change 구현
- Random Spawn 구현
중요한 부분
1. Projectile 피격 관련
- 대미지 적용 (ApplyDamage 호출): UGameplayStatics::ApplyDamage 함수를 호출하여 대미지를 적용하면, 해당 대상의 AActor 클래스로 대미지 정보가 전달되고 이 정보에는 대미지의 양, 대미지 유형, 어떤 컨트롤러가 대미지를 일으켰는지, 대미지의 원인이 된 액터 등이 포함된다.
- 대미지 이벤트 처리: ApplyDamage가 호출되면, 대상 액터의 TakeDamage 메서드가 내부적으로 호출
TakeDamage 메서드는 대상 액터에 대미지를 적용하고, 필요에 따라 특정 대미지 처리 로직을 실행 - 대미지 델리게이트 호출: TakeDamage 메서드 내에서는 OnTakeAnyDamage 델리게이트가 호출
이 델리게이트는 대미지를 받는 대상이 어떻게 반응해야 할지를 외부에서 정의할 수 있게 해 준다. - 콜백 함수 실행 (필자의 코드에서 ReceiveDamage 호출): OnTakeAnyDamage 델리게이트에 바인딩된 ReceiveDamage 함수가 호출됩니다. 이 함수 내에서는 대미지를 받은 후 원하는 로직을 실행할 수 있습니다. 예를 들어, 데미지를 기반으로 캐릭터의 체력을 줄이거나 특정 애니메이션을 재생하는 등의 처리를 수행할 수 있습니다.
Projectile 피격 관련
AProjectileBullet::OnHit함수의 UGameplayStatics::ApplyDamage()관련
해당 코드의 부분은 충돌되었을 때의 처리 과정이다. 위의 1 ~ 3번에 해당
void AProjectileBullet::OnHit(UPrimitiveComponent* HitComp, AActor* OtherActor,
UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit)
{
ACharacter* OwnerCharacter = Cast<ACharacter>(GetOwner());
if (OwnerCharacter)
{
AController* OwnerController = OwnerCharacter->Controller;
if (OwnerController)
{
// Hit되게되면 플레이어의 ReceiveDamage 콜백 함수가 실행이 됨
UGameplayStatics::ApplyDamage(OtherActor, Damage, OwnerController, this,
UDamageType::StaticClass());
}
}
// 발사체 파괴는 마지막이 되어야함 : 부모로 들어가서 발사체 파괴함
Super::OnHit(HitComp, OtherActor, OtherComp, NormalImpulse, Hit);
}
ABlasterCharacter::BeginPlay()의 ApplyDamage관련 콜백 함수 등록
ReceiveDamage함수에서는 받은 대미지를 처리하여 HP갱신, HitAnim, Elim관련 내용 실행.
또한 여기서 가장 중요한 부분은 바인딩 부분을 서버에서만 되도록 처리해 준 이유는 이런 식으로 구현해야 서버는 대미지 등을 구현했을 때 실제로 누구를 때렸는지에 대한 여부를 결정할 수 있고 부정행위를 방지하기 위해서임.
.h
UFUNCTION()
void ReceiveDamage(AActor* DamagedActor, float Damage, const UDamageType* DamageType, class AController* InstigatorController, AActor* DamageCauser);
.cpp
void ABlasterCharacter::BeginPlay()
{
Super::BeginPlay();
UpdateHUDHealth();
// 서버에서만 데미지 줘야함
if (HasAuthority())
{
OnTakeAnyDamage.AddDynamic(this, &ABlasterCharacter::ReceiveDamage);
}
}
2. Eliminated 관련(죽었을 때)
- ReceiveDamage함수에서 Health를 관리하고 Health가 0이 되었을 때 GameMode를 통해서 Elim 관련 함수 실행하도록 구조 설계
- GameMode는 기본적으로 언리얼엔진에서 게임의 규칙과 관련된 내용을 담당하기 때문에 플레이어의 Eliminated는 GameMode에서 처리하는 것
Eliminated 관련(죽었을 때)
Health가 0이 되었을 경우 BlasterGameMode를 얻어온 뒤 GameMode의 PlayerEliminated함수 실행
ReceiveDamage함수가 무조건 적으로 서버에서 실행되고 있기 때문에 GameMode를 얻어올 수 있는 것이다.
ABlasterCharacter::ReceiveDamage()
void ABlasterCharacter::ReceiveDamage(AActor* DamagedActor, float Damage, const UDamageType* DamageType,
AController* InstigatorController, AActor* DamageCauser)
{
// Health는 복제중 그래서 값이 바뀌는 순간 OnRep_Health()함수가 실행됨
Health = FMath::Clamp(FMath::CeilToInt(Health - Damage), 0, FMath::CeilToInt(MaxHealth));
// HUD 업데이트하고 몽타주 실행
UpdateHUDHealth();
PlayHitReactMontage();
// 죽었을때
if (Health == 0.f)
{
// 현재 해당 ReceiveDamage함수는 서버에서 실행되고 있기 때문에 게임모드를 얻어올 수 있다.
ABlasterGameMode* BlasterGameMode = GetWorld()->GetAuthGameMode<ABlasterGameMode>();
if (BlasterGameMode)
{
BlasterPlayerController = BlasterPlayerController == nullptr ? Cast<ABlasterPlayerController>(Controller) : BlasterPlayerController;
// 공격한 사람의 플레이어 컨트롤러
ABlasterPlayerController* AttackerController = Cast<ABlasterPlayerController>(InstigatorController);
BlasterGameMode->PlayerEliminated(this, BlasterPlayerController, AttackerController);
}
}
}
ABlasterGameMode::PlayerEliminated()
게임모드에서 플레이어의 Elim과 HUD관련된 내용을 전반적으로 담당 및 처리
void ABlasterGameMode::PlayerEliminated(ABlasterCharacter* ElimmedCharacter, ABlasterPlayerController* VictimController,
ABlasterPlayerController* AttackerController)
{
// 공격자 플레이어 상태
ABlasterPlayerState* AttackerPlayerState = AttackerController ? Cast<ABlasterPlayerState>(AttackerController->PlayerState) : nullptr;
// 피해자 플레이어 상태
ABlasterPlayerState* VictimPlayerState = VictimController ? Cast<ABlasterPlayerState>(VictimController->PlayerState) : nullptr;
if (AttackerPlayerState == nullptr) UE_LOG(LogTemp, Warning, TEXT("AttackerPlayerState 유효하지 않음"));
if (AttackerPlayerState == VictimPlayerState) UE_LOG(LogTemp, Warning, TEXT("AttackerPlayerState == VictimPlayerState"));
if (AttackerPlayerState && AttackerPlayerState != VictimPlayerState)
{
AttackerPlayerState->AddToScore(1.f);
}
if (VictimPlayerState)
{
VictimPlayerState->AddToDefeats(1);
}
if (ElimmedCharacter)
{
ElimmedCharacter->Elim();
}
}
이후는 영상 첨부
영상
'언리얼5' 카테고리의 다른 글
언리얼 엔진 PostEditChangeProperty에 대해서 (0) | 2023.09.05 |
---|---|
[UE5 Multiplayer Shooting-14] Reload, Match State, Scatter 알고리즘 구현 및 무기 추가 (0) | 2023.08.21 |
[UE5 Multiplayer Shooting-12] UE5 Framework 구조 (6) | 2023.08.11 |
[UE5 Multiplayer Shooting-11] Automatic Fire, CorsshiarHUD, Zoom 완성 (1) | 2023.08.11 |
[UE5 Multiplayer Shooting-10] 반자동 라이플 발사, Crosshair 일부 (2) | 2023.08.08 |
댓글