https://youtu.be/-JDrNxF66qM?si=MOYP5CafxU-dsULo
캐릭터나 사물에 가하는 대미지를 처리하는 시스템입니다.
클래스 관계도는 다음과 같습니다.
대상에게 대미지를 전달할 때, 대상에게 Interface_PRDamageable 인터페이스가 존재하는지 확인한 후 존재할 경우 Interface_PRDamageable의 TakeDamage 함수로 대미지의 정보를 나타내는 PRDamageInfo 구조체를 전달합니다.
대미지를 전달하는 PRDamageInfo 구조체의 구조는 다음과 같습니다.
- Amount: 대미지 양입니다.
- DamageType: 대미지의 유형입니다.
- Melee: 근거리 공격으로 인한 대미지입니다.
- Projectile: 원거리(투사체) 공격으로 인한 대미지입니다.
- Environment: 환경으로 인한 대미지입니다. ex) 초원이 불에 탈 때 입는 화상 대미지
- DamageElement: 대미지의 속성입니다.
- Physio: 물리
- Pyro: 화염
- Hydro: 물
- Anemo: 바람
- Electro: 번개
- DamageResponse: 대미지에 대한 반응입니다.
- HitReaction: 적중반응
- Stagger: 비틀거림
- Stun: 스턴
- KnockBack: 넉백
- ImpactLocation: 대미지를 받은 위치입니다.
- IsCritical: 치명타인지 아닌지 나타냅니다.
- ShouldDamageInvincible: 무적상태에도 대미지를 입히는지 나타냅니다.
- CanBeBlocked: 방어 가능 여부를 나타냅니다.
- CanBeParried: 패링 가능 여부를 나타냅니다.
- ShouldForceImterrupt: 동작의 가능 여부를 나타냅니다.
대상은 TakeDamage 함수를 override해서 DamageSystemComponent에서 대미지를 처리하거나, 자체적으로 대미지를 처리합니다. 인터페이스와 TakeDamage 함수를 override하는 코드는 다음과 같습니다.
// Interface_PRDamageable.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "ProjectReplica.h"
#include "UObject/Interface.h"
#include "Interface_PRDamageable.generated.h"
UINTERFACE(MinimalAPI)
class UInterface_PRDamageable : public UInterface
{
GENERATED_BODY()
};
/**
* 대미지에 관련된 기능을 실행하는 Interface 클래스입니다.
*/
class PROJECTREPLICA_API IInterface_PRDamageable
{
GENERATED_BODY()
public:
/**
* 대미지를 받는 함수입니다.
*
* @param DamageInfo 대미지의 정보
* @return 대미지 적용 여부
*/
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Damageable")
bool TakeDamage(FPRDamageInfo DamageInfo);
};
// APRBaseCharacter.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
...
#include "Interfaces/Interface_PRDamageable.h"
...
/**
* 기본 캐릭터 클래스입니다.
*/
UCLASS()
class PROJECTREPLICA_API APRBaseCharacter : public ACharacter, public IInterface_PRDamageable
{
GENERATED_BODY()
...
#pragma region Interface_Damageable
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Interface_Damageable")
bool TakeDamage(FPRDamageInfo DamageInfo);
virtual bool TakeDamage_Implementation(FPRDamageInfo DamageInfo) override;
#pragma endregion
};
// PRBaseCharacter.cpp
bool APRBaseCharacter::TakeDamage_Implementation(FPRDamageInfo DamageInfo)
{
if(GetDamageSystem())
{
return GetDamageSystem()->TakeDamage(DamageInfo);
}
return false;
}
캐릭터는 DamageSystem에서 대미지를 관리하고 처리합니다. DamageSystem에서 받은 대미지를 처리하는 코드는 다음과 같습니다.
bool UPRDamageSystemComponent::TakeDamage(FPRDamageInfo DamageInfo)
{
if(!GetPROwner()
|| GetPROwner()->IsDead()
|| !StatSystem
|| !StateSystem)
{
return false;
}
FPRCharacterStat CharacterStat = StatSystem->GetCharacterStat();
// 대미지의 처리를 구분하여 실행합니다.
EPRCanBeDamaged CanBeDamagedResult = CanBeDamaged(DamageInfo.bShouldDamageInvincible, DamageInfo.bCanBeBlocked);
switch(CanBeDamagedResult)
{
case EPRCanBeDamaged::CanBeDamaged_BlockDamage:
if(OnBlockedDelegate.IsBound())
{
// 방어 상태이면서 방어할 수 있는 대미지이므로 패링합니다.
OnBlockedDelegate.Broadcast(DamageInfo.bCanBeParried);
}
break;
case EPRCanBeDamaged::CanBeDamaged_DoDamage:
// DamageAmount Spawn
if(DamageAmount && GetWorld())
{
APRDamageAmount* DamageAmountInstance = GetWorld()->SpawnActor<APRDamageAmount>(DamageAmount);
if(DamageAmountInstance)
{
DamageAmountInstance->Initialize(DamageInfo.ImpactLocation, DamageInfo.Amount, DamageInfo.bIsCritical, DamageInfo.DamageElement);
}
}
StatSystem->SetHealth(CharacterStat.Health -= DamageInfo.Amount);
if(CharacterStat.Health <= 0.0f)
{
// 캐릭터의 체력이 0이하(사망)일 경우 OnDeathDelegate를 실행합니다.
if(OnDeathDelegate.IsBound())
{
OnDeathDelegate.Broadcast();
}
}
else
{
// 동작을 강제로 중단할 수 있는 상태이거나 동작을 강제로 중단해야하는 대미지일 경우
// OnDamageResponseDelegate를 실행합니다.
if(StateSystem->IsInterruptible() || DamageInfo.bShouldForceInterrupt)
{
if(OnDamageResponseDelegate.IsBound())
{
OnDamageResponseDelegate.Broadcast(DamageInfo.DamageResponse);
return true;
}
}
}
break;
case EPRCanBeDamaged::CanBeDamaged_NoDamage:
default:
break;
}
return false;
}
DamageInfo의 값에 따라 방어하여 대미지를 받지 않을지, 대미지를 받을지, 대미지를 받지 않을지 판별하여 캐릭터의 체력에 반영합니다. 대미지를 판별하는 함수는 다음과 같습니다.
EPRCanBeDamaged UPRDamageSystemComponent::CanBeDamaged(const bool& bShouldDamageInvincible, const bool& bCanBeBlocked)
{
if(GetPROwner()
&& !GetPROwner()->IsDead()
&& (!GetPROwner()->IsInvincible() || bShouldDamageInvincible))
{
if(GetPROwner()->IsBlocking() && bCanBeBlocked)
{
return EPRCanBeDamaged::CanBeDamaged_BlockDamage;
}
return EPRCanBeDamaged::CanBeDamaged_DoDamage;
}
return EPRCanBeDamaged::CanBeDamaged_NoDamage;
}
사물의 경우 DamageSystem에서 대미지를 처리하지 않고 override한 TakeDamage 함수에서 처리합니다.
'Project > Replica' 카테고리의 다른 글
[Project Replica] EffectSystem (0) | 2024.04.30 |
---|---|
[Project Replica] Vaulting / Vault / 장애물 뛰어넘기 (0) | 2024.04.15 |
[Project Replica] AI 생성 시스템 AISpawnSystem (1) | 2023.12.04 |
[Project Replica] 극한회피 Extreme Dodge/TimeStopSystem (2) | 2023.10.26 |
[Project Replica] 범위 피해 스킬 Judgement Cut (0) | 2023.10.18 |