* 전투와 관련된 부가 요소를 추가해본다.
1. 플레이너는 게임 진행 중에 HP를 회복할 수 없고 오직 레벨업을 할 떄만 회복된다.
2. 캐릭터는 무기를 들 떄 더 긴 공격 범위를 가진다.
3. 무기에는 공격력 증가치가 랜덤으로 부여되며, 운이 없으면 오히려 무기에 의해 공격력이 저하될 수 있다.
4. 현재 게임 스코어가 높을수록 생성되는 NPC의 레벨도 증가한다.
* 1번은 이미 구현했으므로 이번에는 2번 기능을 구현해본다.
* 무기 액터인 ABWeapon에 AttackRange라는 속성을 추가한다.
* 캐릭터에 무기가 없으면 캐릭터의 AttackRange 속성을 사용하고, 캐릭터가 무기를 들면 무기의 AttackRange 속성을 사용하도록 로직을 수정한다.
* 캐릭터의 AttackRange 속성의 기본값을 80으로 낮춘다. 그리고 무기의 AttackRange 속성의 기본값을 150으로 지정하되, 해당 속성 키워드에 EditAnywgere, BlueprintReadWrite를 지정해 앞으로 ABweapon 클래스를 상속받은 무기 블루프린트에서도 공격범위 값을 다르게 설정할 수 있도록 기능을 부여한다.
* 또한 이번에는 캐릭터가 무기를 들고 있더라고 무기를 변경할 수 있도록 CanSetWeapon의 값을 무조건 true로 설정하고, 기존에 무기가 있는 경우 이를 없애고 새로운 무기를 습득하도록 로직을 변경한다.
//ABWeapon.h
class ARENABATTLE_API AABWeapon : public AActor
{
public:
float GetAttackRange() const;
protected:
UPROPERTY(EditAnywhere,BlueprintReadWrite,Category = Attack)
float AttackRange;
}
//ABWeapon.cpp
AABWeapon::AABWeapon()
{
.....
AttackRange = 150.0f;
}
float AABWeapon::GetAttackRange() const
{
return AttackRange;
}
//ABCharacter.h
class ARENABATTLE_API AABCharacter : public ACharacter
{
public:
float GetFinalAttackRange() const;
}
AABCharacter::AABCharacter()
{
AttackRange = 80.0f;
}
float AABCharacter::GetFinalAttackRange() const
{
return (nullptr != CurrentWeapon) ? CurrentWeapon->GetAttackRange() : AttackRange;
}
void AABCharacter::SetWeapon(AABWeapon * NewWeapon)
{
ABCHECK(nullptr != NewWeapon);
if (nullptr != CurrentWeapon)
{
CurrentWeapon->DetachFromActor(FDetachmentTransformRules::KeepWorldTransform);
CurrentWeapon->Destroy();
CurrentWeapon = nullptr;
}
FName WeaponSocket(TEXT("hand_rSocket"));
if (nullptr != NewWeapon)
{
NewWeapon->AttachToComponent(GetMesh(), FAttachmentTransformRules::SnapToTargetNotIncludingScale, WeaponSocket);
NewWeapon->SetOwner(this);
CurrentWeapon = NewWeapon;
}
}
void AABCharacter::AttackCheck()
{
float FinalAttackRange = GetFinalAttackRange(); // AttackRange -> FinalAttackRange
FHitResult HitResult;
FCollisionQueryParams Params(NAME_None, false, this);
bool bResult = GetWorld()->SweepSingleByChannel(
HitResult,
GetActorLocation(),
GetActorLocation() + GetActorForwardVector() * FinalAttackRange,
FQuat::Identity,
ECollisionChannel::ECC_GameTraceChannel2,
FCollisionShape::MakeSphere(AttackRadius),
Params);
#if ENABLE_DRAW_DEBUG
FVector TraceVec = GetActorForwardVector() * FinalAttackRange;
FVector Center = GetActorLocation() + TraceVec * 0.5f;
float HalfHeight = FinalAttackRange * 0.5f + AttackRadius;
FQuat CapsuleRot = FRotationMatrix::MakeFromZ(TraceVec).ToQuat();
FColor DrawColor = bResult ? FColor::Green : FColor::Red;
float DebugLifeTime = 5.0f;
.....
}
//BTDecorator_IsInAttackRange.cpp
bool UBTDecorator_IsInAttackRange::CalculateRawConditionValue(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) const
{
bool bResult = Super::CalculateRawConditionValue(OwnerComp, NodeMemory);
auto ControllingPawn = Cast<AABCharacter>(OwnerComp.GetAIOwner()->GetPawn()); //변경
if (nullptr == ControllingPawn)
return false;
auto Target = Cast<AABCharacter>(OwnerComp.GetBlackboardComponent()->GetValueAsObject(AABAIController::TargetKey));
if (nullptr == Target)
return false;
bResult = (Target->GetDistanceTo(ControllingPawn) <= ControllingPawn->GetFinalAttackRange());//변경
return bResult;
}
* 이제 캐릭터는 무기를 들 떄 무기가 없는 NPC보다 더 긴 공격 범위를 가진다.
* 이번에는 3번의 기획 요소를 구현해본다. 공격할 떄 무기가 있는 경우 기존 공격력을 증폭시키는 기능을 구현한다.
* 무기에 랜덤한 순수 공격력과 효과치 속성을 설정하고 최종 대미지를 산출할 떄 이 데이터를 활용하도록 로직을 구현해 본다.
//AABWeapon.h
class ARENABATTLE_API AABWeapon : public AActor
{
public:
float GetAttackRange() const;
float GetAttackDamage() const;
float GetAttackModifier() const;
protected:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Attack)
float AttackDamageMin;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Attack)
float AttackDamageMax;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Attack)
float AttackModifierMin;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Attack)
float AttackModifierMax;
UPROPERTY(Transient, VisibleInstanceOnly, BlueprintReadWrite, Category = Attack)
float AttackDamage;
UPROPERTY(Transient, VisibleInstanceOnly, BlueprintReadWrite, Category = Attack)
float AttackModifier;
}
//AABWeapon.cpp
AABWeapon::AABWeapon()
{
AttackRange = 150.0f;
AttackDamageMin = -2.5f;
AttackDamageMax = 10.0f;
AttackModifierMin = 0.85f;
AttackModifierMax = 1.25f;
}
// Called when the game starts or when spawned
void AABWeapon::BeginPlay()
{
Super::BeginPlay();
AttackDamage = FMath::RandRange(AttackDamageMin, AttackDamageMax);
AttackModifier = FMath::RandRange(AttackModifierMin, AttackModifierMax);
}
float AABWeapon::GetAttackDamage() const
{
return AttackDamage;
}
float AABWeapon::GetAttackModifier() const
{
return AttackModifier;
}
//AABCharacter.h
public:
float GetFinalAttackDamage() const;
//AABCharacter.cpp
float AABCharacter::GetFinalAttackDamage() const
{
float AttackDamage = (nullptr != CurrentWeapon) ? (CharacterStat->GetAttack() + CurrentWeapon->GetAttackDamage()) : (CharacterStat->GetAttack());
float AttackModifier = (nullptr != CurrentWeapon) ? (CurrentWeapon->GetAttackModifier()) : (1.0f);
return AttackDamage * AttackModifier;
}
void AABCharacter::AttackCheck()
{
.....
if (bResult)
{
if (HitResult.Actor.IsValid())
{
FDamageEvent DamageEvent;
HitResult.Actor->TakeDamage(GetFinalAttackDamage(), DamageEvent, GetController(), this);
}
}
}
* 로직을 반영하면, 무기를 습득할 때 해당 무기에 의해 플레이어의 공격 대미지가 증가하거나 감소되는 것을 확인할 수 있다.
* 이번에는 마지막 기획요소인 NPC의 레벨을 조정하는 기능을 구현해본다.
* NPC 캐릭터의 LOADING 스테이트에서 현제 게임 스코어를 게임모드에게 질의하고, 이를 기반으로 캐릭터의 레벨값을 설정한다.
//AABGameMode.h
public:
int32 GetScore() const;
//AABGameMode.cpp
int32 AABGameMode::GetScore() const
{
return ABGameState->GetTotalGameScore();
}
//ABCharacter.cpp
#include "ABGameMode.h"
void AABCharacter::SetCharacterState(ECharacterState NewState)
{
ABCHECK(CurrentState != NewState);
CurrentState = NewState;
switch (NewState)
{
case ECharacterState::LOADING :
{
if (bIsPlayer)
{
DisableInput(ABPlayerController);
ABPlayerController->GetHUDWidget()->BindCharacterStat(CharacterStat);
auto ABPlayerState = Cast<AABPlayerState>(GetPlayerState());
ABCHECK(nullptr != ABPlayerState);
CharacterStat->SetNewLevel(ABPlayerState->GetCharacterLevel());
}
else //추가
{
auto ABGameMode = Cast<AABGameMode>(GetWorld()->GetAuthGameMode());
ABCHECK(nullptr != ABGameMode);
int32 TargetLevel = FMath::CeilToInt(((float)ABGameMode->GetScore() * 0.8f));
int32 FinalLevel = FMath::Clamp<int32>(TargetLevel, 1, 20);
ABLOG(Warning, TEXT("New NPC Level : %d"), FinalLevel);
CharacterStat->SetNewLevel(FinalLevel);
}
SetActorHiddenInGame(true);
HPBarWidget->SetHiddenInGame(true);
bCanBeDamaged = false;
break;
}
.....
※ 게임 실행 중에 게임 모드의 포인터를 가져올 때는 월드의 GetAuthGameMode라는 함수를 사용한다.
※ 멀티 플레이 게임에서 게임 모드는 게임을 관리하는 방장 역황을 하며 게임에서 중요한 데이터를 인증하는 권한을 가진다.
'Unreal > Game 1 (C++)' 카테고리의 다른 글
22. 게임의 완성 4 (게임의 중지와 결과화면) (0) | 2019.05.13 |
---|---|
22. 게임의 완성 3 (타이틀 화면의 제작 ) (0) | 2019.05.12 |
22. 게임의 완성 1 (게임 데이터의 저장과 로딩) (0) | 2019.05.10 |
21. 게임플레이 제작 3 (게임 데이터의 관리) (0) | 2019.05.09 |
21. 게임플레이 제작 2 (플레이어 데이터와 UI 연동) (0) | 2019.05.08 |