지금까지 아이템들을 구현했고 이제 아이템이 어떤 기능을 하는지 실제로 구현을 해야한다.
캐릭터
캐릭터에 체력 관련 변수와 함수를 추가하고 데미지를 받는 함수 TakeDamage를 추가한다.
// JinCharacter.h
public:
UFUNCTION(BlueprintPure, Category = "Health")
float GetHealth() const; // 현재 체력을 가져오는 함수
UFUNCTION(BlueprintCallable, Category = "Health")
void AddHealth(float Amount); // 체력 회복 함수
protected:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Health")
float MaxHealth; // 최대 체력
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Health")
float Health; // 현재 체력
void OnDeath(); // 체력이 0 이하가 될 때 사망 처리 함수
// 데미지 처리 함수 - 외부로부터 데미지를 받을 때 호출됨
// 또는 AActor의 TakeDamage()를 오버라이드
virtual float TakeDamage(
float DamageAmount, // 입은 데미지 양
struct FDamageEvent const& DamageEvent, // 어떤 유형으로 데미지를 입혔는지(여기선 필요없음)
AController* EventInstigator, // 데미지를 발생시킨 주체(여기선 필요없음)
AActor* DamageCauser) override; // 데미지를 일으킨 오브젝트(지뢰)
// JinCharacter.cpp
// 생성자
MaxHealth = 100.0f;
Health = MaxHealth;
float AJinCharacter::GetHealth() const
{
return Health;
}
void AJinCharacter::AddHealth(float Amount) // HealthItem에서 사용할 함수
{ // Clamp함수로 최솟값과 최댓값을 제한한다.
Health = FMath::Clamp(Health + Amount, 0.0f, MaxHealth);
UE_LOG(LogTemp, Warning, TEXT("Health increased to: %.1f"), Health);
}
float AJinCharacter::TakeDamage(
float DamageAmount,
FDamageEvent const& DamageEvent,
AController* EventInstigator,
AActor* DamageCauser)
{
float ActualDamage = Super::TakeDamage(DamageAmount, DamageEvent, EventInstigator, DamageCauser);
Health = FMath::Clamp(Health - DamageAmount, 0.0f, MaxHealth);
UE_LOG(LogTemp, Warning, TEXT("Health decreased to: %.1f"), Health);
if (Health <= 0.0f)
{
OnDeath();
}
return ActualDamage;
}
void AJinCharacter::OnDeath() // 사망 로직은 미구현, 로그 출력만
{
UE_LOG(LogTemp, Error, TEXT("Character is Dead!"));
}
AActor::TakeDamage - 데미지를 받는 함수 (여기선 Character)
Actor의 가상 함수, 모든 액터가 기본적으로 이 함수를 가지고 있으며, 필요하다면 자식 클래스(캐릭터 등)에서 오버라이드할 수 있다.
실제로 체력 감소 또는 특수한 데미지 처리 로직을 이 안에서 구현하게 된다.
UGamePlayStatics::ApplyDamage() - 데미지를 주는 함수 (여기선 MineItem, 지뢰에서 구현함)
공격자(또는 폭발물 등)가 데미지를 줄 대상 액터와 데미지 양, 데미지를 유발한 주체 등을 인자로 넘겨 호출한다.
내부적으로 대상 액터의 TakeDamage() 함수를 호출하려 시도한다.
UGameplayStatics::ApplyDamage(
Actor,
ExplosionDamage,
Instigator, // EventInstigator (Controller)
this, // DamageCauser (지뢰 액터)
UDamageType::StaticClass() // DamageType
);
=> ApplyDamage에 넣은 인자들이 자동으로 TakeDamage의 인자로 전달된다.
float TakeDamage(
float DamageAmount, // = ExplosionDamage
const FDamageEvent& DamageEvent, // = DamageTypeClass로 만든 이벤트
AController* EventInstigator, // = Instigator
AActor* DamageCauser // = this (지뢰)
);
지뢰 아이템
지뢰 아이템 (MineItem)이 폭발할 때, 주변 액터에게 데미지를 주려면 UGameplayStatics::ApplyDamage 함수를 호출해 해당 액터의 TakeDamage()가 실행되도록 하면 됩니다.
void AMineItem::Explode()
{
TArray<AActor*> OverlappingActors;
ExplosionCollision->GetOverlappingActors(OverlappingActors);
for (AActor* Actor : OverlappingActors)
{
if (Actor && Actor->ActorHasTag("Player"))
{
UGameplayStatics::ApplyDamage(
Actor, // 데미지를 입을 액터(이 액터의 TakeDamage 함수가 자동 호출됨)
ExplosionDamage, // 입힐 데미지 양
nullptr, // 데미지를 준 주체(컨트롤러, 지뢰를 심은 플레이어가 있다면 그 플레이어)
this, // 데미지를 주는 오브젝트(지뢰)
UDamageType::StaticClass() //데미지 타입은 기본 타입
);
}
}
DestroyItem();
}
- ApplyDamage()
- 대상 액터(Actor)가 존재하는지 확인.
- 대상 액터의 TakeDamage() 함수를 호출합니다.
- DamageType은 여러 가지 파생 클래스를 만들어 물리/화염/독 등 다양한 데미지 유형을 정의할 수 있습니다. (지금은 기본값 사용).
- 지뢰는 독립적으로 스폰된 뒤 폭발하므로 EventInstigator를 nullptr로 둡니다.
- 멀티플레이에서 “누가 지뢰를 설치했느냐”를 추적하려면, 생성 시점에 Instigator나 Controller 정보를 넣어줄 수도 있습니다.
힐링 아이템
힐링 아이템(HealingItem)이 플레이어를 회복시킵니다. AddHealth() 함수를 직접 호출해 체력을 증가시킵니다.
void AHealingItem::ActivateItem(AActor* Activator)
{
if (Activator && Activator->ActorHasTag("Player"))
{
if (AJinCharacter* PlayerCharacter = Cast<AJinCharacter>(Activator))
{
PlayerCharacter->AddHealth(HealAmount);
}
DestroyItem();
}
}
점수 관리 시스템 구현(GameState)
언리얼 엔진에서 GameMode와 GameState는 게임의 전역 정보를 유지하고, 필요할 경우 멀티플레이어 환경에서 해당 정보를 서버와 클라이언트 간에 동기화하는 역할을 한다.
- GameMode
- “게임의 규칙(룰)”을 정의하고 관리합니다.
- 어떤 캐릭터를 스폰할지, 플레이어가 사망했을 때 어떻게 처리할지를 결정합니다.
- 멀티플레이에서는 서버 전용으로 동작합니다(클라이언트에는 존재하지 않음).
- GameState
- 게임 플레이 전반에서 “공유되어야 하는 전역 상태”를 저장합니다. GameState는 기본적으로 “레벨당 1개” 존재하며, 엔진 내부에서 데이터 동기화를 고려해 설계되었기에 전역 데이터 관리용으로 적합합니다.
- 대표적으로 점수, 남은 시간, 현재 게임 단계(Phase), 스폰된 오브젝트의 총 개수 등을 저장합니다.
- 멀티플레이에서는 서버가 관리하고, 클라이언트는 이를 자동으로 동기화 받아볼 수 있습니다.
싱글 플레이에서는 전역적으로 공유해야 할 정보를 GameState를 사용해서 한군데서 관리하면 유지보수가 편해진다.
GameStateBase를 상속받는 클래스를 생성하고 이 클래스에 점수 변수와 점수 획득 함수를 구현한다.
// JinGameStateBase.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/GameStateBase.h"
#include "JinGameStateBase.generated.h"
UCLASS()
class UNREAL_CPP_STUDY_API AJinGameStateBase : public AGameStateBase
{
GENERATED_BODY()
public:
AJinGameStateBase();
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Score")
int32 Score; // 전역 점수를 저장하는 변수
UFUNCTION(BlueprintPure, Category = "Score")
int32 GetScore() const; // 현재 점수를 읽는 함수
UFUNCTION(BlueprintCallable, Category = "Score")
void AddScore(int Amount); // 점수를 추가해주는 함수
};
// JinGameStateBase.cpp
#include "JinGameStateBase.h"
AJinGameStateBase::AJinGameStateBase()
{
Score = 0;
}
int32 AJinGameStateBase::GetScore() const
{
return Score;
}
void AJinGameStateBase::AddScore(int Amount)
{
Score += Amount;
UE_LOG(LogTemp, Warning, TEXT("Score: %d"), Score);
// 필요하면 여기서 최대 점수 제한, 점수 획득 사운드 재생 등을 넣을 수 있다.
}
기존에 만든 GameMode에서 GameStateClass를 지금 만든 클래스로 설정한다.
// FirstGameMode.cpp
#include "FirstGameMode.h"
#include "JinCharacter.h"
#include "JinPlayerController.h"
#include "JinGameStateBase.h"
AFirstGameMode::AFirstGameMode()
{
DefaultPawnClass = AFirstGameMode::StaticClass();
PlayerControllerClass = AJinPlayerController::StaticClass();
GameStateClass = AJinGameStateBase::StaticClass(); // 추가
}
코인 아이템
코인 아이템에서 JinGameStateBase에서 설정한 점수 획득 함수를 사용하여 점수가 오르도록 한다.
// CoinItem.cpp
void ACoinItem::ActivateItem(AActor* Activator)
{
if (Activator && Activator->ActorHasTag("Player"))
{
if (UWorld* World = GetWorld())
{
if (AJinGameStateBase* GameState = World->GetGameState<AJinGameStateBase>())
{
GameState->AddScore(PointValue);
}
}
DestroyItem();
}
}
'언리얼 + cpp' 카테고리의 다른 글
언리얼C++ 9일차(UMG, UI, 바인딩, SetText) (0) | 2025.09.24 |
---|---|
언리얼C++ 9일차(게임 흐름 로직(게임 루프), Game Instance) (0) | 2025.09.24 |
언리얼C++ 8일차(스폰, 데이터 테이블, 확률) (0) | 2025.09.23 |
언리얼C++ 8일차(Collision, 이벤트 바인딩, 타이머 핸들) (0) | 2025.09.23 |
언리얼C++ 7일차(인터페이스) (0) | 2025.09.22 |