캐릭터 체력 및 점수 관리 시스템 구현하기 | [언리얼 엔진 C++ (Unreal Engine C++)]

2026. 6. 11. 16:48·Unreal Engine/UE 기초

 

캐릭터 체력 및 점수 관리 시스템 구현하기

 


 

캐릭터 체력 시스템 구현하기

 

1️⃣ 캐릭터 클래스에 체력 변수 및 함수 선언

  • PlayerState를 쓰지 않는 이유
    • PlayerState: 각 플레이어마다의 어떤 정보를 관리해주는 클래스 
    • 언리얼 엔진에서 PlayerState는 주로 멀티플레이 환경에서 각 플레이어 간 데이터 동기화를 위해 사용합니다.
      • 예를 들어, 점수나 킬/데스 카운트처럼 서버와 클라이언트 모두가 공통으로 확인해야 하는 정보를 저장하죠.
    • 하지만 싱글 플레이 게임에서는 이런 동기화가 필요 없으므로, 캐릭터 클래스자체에 체력이나 스코어 변수를 넣어 관리해도 충분합니다.
    • 그럼에도 추후 확장성 생각하면 PlayerState 가 더 좋을 수 있지만 우선은 이렇게 합니다.
  • 캐릭터 클래스에 체력 관리 로직 추가
    • 싱글 플레이 환경을 가정하여, 플레이어 캐릭터를 담당하는 클래스에 체력 관리용 변수를 선언합니다.
  • MaxHealth: 캐릭터의 최대 체력을 나타냅니다.
  • Health: 캐릭터의 현재 체력을 나타냅니다.
  • TakeDamage(): 데미지를 받았을 때 호출되는 함수로, 내부에서 체력을 감소시키는 로직을 처리합니다.
  • AddHealth(): 아이템 등을 통해 체력을 회복할 때 호출하는 함수로, 내부에서 체력을 회복시킵니다.
  • OnDeath(): 체력이 0 이하가 되었을 때 호출되는 사망 처리 함수입니다.

(BlueprintPure: Get함수 사용시 주로 사용됩니다)

 


 

2️⃣ 데미지 및 회복 처리

  • 언리얼 엔진에는 데미지 시스템을 간편히 사용할 수 있는 UGameplayStatics::ApplyDamage(데미지 발생)와 AActor::TakeDamage(데미지 처리) 함수가 제공됩니다.
  • 데미지 처리 흐름 (ApplyDamage → TakeDamage)
    1. UGameplayStatics::ApplyDamage
      • 공격자 (또는 폭발물 등)가 데미지를 줄 대상 액터와 데미지 양, 데미지를 유발한 주체 등을 인자로 넘겨 호출합니다.
      • 내부적으로 대상 액터의 TakeDamage() 함수를 호출하려 시도합니다.
    2. AActor::TakeDamage
      • Actor의 가상 함수입니다. 모든 액터가 기본적으로 이 함수를 가지고 있으며, 필요하다면 자식 클래스(캐릭터 등)에서 오버라이드할 수 있습니다.
      • 실제로 체력 감소 또는 특수한 데미지 처리 로직을 이 안에서 구현하게 됩니다.
  • AddHealth(float Amount)
    • 체력을 일정 양만큼 회복합니다.
    • FMath::Clamp를 통해 최대 체력을 초과하지 않도록 제한합니다.
  • TakeDamage(...)
    • 언리얼 엔진의 기본 데미지 시스템을 사용하는 대표적인 함수입니다.
    • DamageAmount: 데미지 값
    • EventInstigator: 데미지를 유발한 주체(Controller)
    • DamageCauser: 데미지를 직접 발생시킨 오브젝트(총알, 폭발물 등)
    • 반환값: 실제 적용된 데미지(기본 로직에서는 DamageAmount와 동일한 경우가 많지만, 게임 상황에 따라 감소 또는 증폭 등을 처리할 수도 있습니다.)
  • OnDeath()
    • 체력이 0 이하로 떨어졌을 때 사망 로직을 처리하는 함수입니다.
    • 흔히 입력 비활성화, Ragdoll 적용, 사망 애니메이션 재생 등을 수행합니다.

MainCharacter.h

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "MainCharacter.generated.h"

// 미리 선언
// 전방 선언(Forward Declaration) 
class USpringArmComponent; // 스프링 암 관련 클래스 헤더
class UCameraComponent; // 카메라 관련 클래스 전방 선언


// Enhanced Input에서 액션 값을 받을 때 사용하는 구조체
struct FInputActionValue;

UCLASS()
class BC_CH3_ASSIGNMENT_5_API AMainCharacter : public ACharacter
{
	GENERATED_BODY()

public:
	// Sets default values for this character's properties
	AMainCharacter();
	
	
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Camera")
	USpringArmComponent* SpringArmComp  = nullptr;
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Camera")
	UCameraComponent* CameraComp = nullptr;
	
	// 현재 체력을 가져오는 함수
	UFUNCTION(BlueprintPure, Category = "Health")
	float GetHealth() const;
		
	// 체력을 회복시키는 함수
	UFUNCTION(BlueprintCallable, Category = "Health")
	void AddHealth(float Amount);
	
protected:
	
	// === Variables ===
	// 최대 체력
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Health")
	float  MaxHealth;
	// 현재 체력
	UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Health")
	float  Health;
	
	// = Movements
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Movement")
	float NormalSpeed; // 기본 걷기 속도
	
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Movement")
	float SprintSpeedMultiplier;  // "기본 속도" 대비 몇 배로 빠르게 달릴지
	
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Movement")
	float SprintSpeed; 	// 실제 스프린트 속도 SprintSpeed= NormalSpeed * SprintSpeedMultiplier
	
	
	// =====	
	
	// Called to bind functionality to input
	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;	
	
	UFUNCTION()
	void Move(const FInputActionValue& value);
	
	UFUNCTION()
	void Look(const FInputActionValue& value);
	
	// on off 의 형태의 것들은 그냥 나눠 주는 것이 좋다
	// 왜냐하면 이전에는 세세하게 처리하기가 상당이 까다로웠는데
	// EnhancedInputSystem은 그것들을 매우 편하게 변경해주었기 때문에 왠만하면 나눠 주는 것이 좋다
	UFUNCTION()
	void StartJump(const FInputActionValue& value);
	UFUNCTION()
	void StopJump(const FInputActionValue& value);
	
	UFUNCTION()
	void StartSprint(const FInputActionValue& value);
	UFUNCTION()
	void StopSprint(const FInputActionValue& value);
	
	
	// 사망 처리 함수 (체력이 0 이하가 되었을 때 호출)
	UFUNCTION(BlueprintCallable, Category = "Health")
	virtual void OnDeath();

	// 데미지 처리 함수 - 외부로부터 데미지를 받을 때 호출됨
	// 또는 AActor의 TakeDamage()를 오버라이드
	virtual float TakeDamage(
		float DamageAmount,
		struct FDamageEvent const& DamageEvent, // Damage 관련 추가 정보 (예: Skill 속성에 따른 반응) 
		AController* EventInstigator, //데미지를 발생 시킨 주체
		AActor* DamageCauser // 데미지를 일으킨 오브젝트
		) override;
	
};

 

MainCharacter.cpp

#include "MainCharacter.h"
#include "EnhancedInputComponent.h"
//#include "InputActionValue.h"
#include "MainPlayerController.h"

// 카메라, 스프링 암 실제 구현이 필요한 경우라서 include
// 전방 선언(Forward Declaration) 한것 여기서 (실질적으로 사용하는곳) 포함
#include "Camera/CameraComponent.h"
#include "GameFramework/SpringArmComponent.h" 

//
#include "GameFramework/CharacterMovementComponent.h" // GetCharacterMovement() 사용을 위해
#include "GameFramework/Actor.h"
#include "Kismet/GameplayStatics.h"

AMainCharacter::AMainCharacter()
{ 	
	PrimaryActorTick.bCanEverTick = false;	
	
	// (1) 스프링 암 생성
	SpringArmComp = CreateDefaultSubobject<USpringArmComponent>(TEXT("SpringArm"));
	// 스프링 암을 루트 컴포넌트 (CapsuleComponent)에 부착
	SpringArmComp->SetupAttachment(RootComponent);
	// 캐릭터와 카메라 사이의 거리 기본값 300으로 설정
	SpringArmComp->TargetArmLength = 300.0f;  
	// 컨트롤러 회전에 따라 스프링 암도 회전하도록 설정
	SpringArmComp->bUsePawnControlRotation = true;  
	
	
	//

	// (2) 카메라 컴포넌트 생성
	CameraComp = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));
	// 스프링 암의 소켓 위치에 카메라를 부착
	CameraComp->SetupAttachment(SpringArmComp, USpringArmComponent::SocketName);
	// 카메라는 스프링 암의 회전을 따르므로 PawnControlRotation은 꺼둠
	CameraComp->bUsePawnControlRotation = false;
	
	// Movements & Sprint Speed
	
	NormalSpeed = 600.0f;
	SprintSpeedMultiplier = 1.75f;
	SprintSpeed = NormalSpeed * SprintSpeedMultiplier;

	
	// #include "GameFramework/CharacterMovementComponent.h" 추가해야함
	// MaxWalkSpeed 변경시 캐릭터 이동속도가 즉시 변경
	GetCharacterMovement()->MaxWalkSpeed = NormalSpeed;
	
	
	// 초기 체력 설정
	MaxHealth = 100.0f;
	Health = MaxHealth;
}


// Called to bind functionality to input
void AMainCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);
	// Enhanced InputComponent로 캐스팅
    if (UEnhancedInputComponent* EnhancedInput = Cast<UEnhancedInputComponent>(PlayerInputComponent))
    {
        // IA를 가져오기 위해 현재 소유 중인 Controller를 AMainPlayerController로 캐스팅
        if (AMainPlayerController* PlayerController = Cast<AMainPlayerController>(GetController()))
        {
        	// null check
            if (PlayerController->MoveAction)
            {
                // IA_Move 액션 키를 "키를 누르고 있는 동안" Move() 호출
                EnhancedInput->BindAction(
                    PlayerController->MoveAction,
                    ETriggerEvent::Triggered,
                    this,
                    &AMainCharacter::Move
                );
            }
            
            if (PlayerController->JumpAction)
            {
                // IA_Jump 액션 키를 "키를 누르고 있는 동안" StartJump() 호출
                EnhancedInput->BindAction(
                    PlayerController->JumpAction,
                    ETriggerEvent::Triggered,
                    this,
                    &AMainCharacter::StartJump
                );
                
                // IA_Jump 액션 키에서 "손을 뗀 순간" StopJump() 호출
                EnhancedInput->BindAction(
                    PlayerController->JumpAction,
                    ETriggerEvent::Completed,
                    this,
                    &AMainCharacter::StopJump
                );
            }
            
            if (PlayerController->LookAction)
            {
                // IA_Look 액션 마우스가 "움직일 때" Look() 호출
                EnhancedInput->BindAction(
                    PlayerController->LookAction,
                    ETriggerEvent::Triggered,
                    this,
                    &AMainCharacter::Look
                );
            }
            
            if (PlayerController->SprintAction)
            {
                // IA_Sprint 액션 키를 "누르고 있는 동안" StartSprint() 호출
                EnhancedInput->BindAction(
                    PlayerController->SprintAction,
                    ETriggerEvent::Triggered,                   
                    this, 
                    &AMainCharacter::StartSprint
                );
                // IA_Sprint 액션 키에서 "손을 뗀 순간" StopSprint() 호출
                EnhancedInput->BindAction(
                    PlayerController->SprintAction, 
                    ETriggerEvent::Completed, 
                    this, 
                    &AMainCharacter::StopSprint
                );
            }    
        }
    }

}

void AMainCharacter::Move(const FInputActionValue& value)
{
	// 컨트롤러가 있어야 방향 계산이 가능
	if (!Controller) return;

	// Value는 Axis2D로 설정된 IA_Move의 입력값 (WASD)을 담고 있음
	// 예) (X=1, Y=0) → 전진 / (X=-1, Y=0) → 후진 / (X=0, Y=1) → 오른쪽 / (X=0, Y=-1) → 왼쪽
	const FVector2D MoveInput = value.Get<FVector2D>();

	// IsNearlyZero
	// 부동소수점들은 딱 0으로 안 떨어 질 수도 있기 때문에 작은 오차들은 0으로 처리 하기 위한 함수
	if (!FMath::IsNearlyZero(MoveInput.X))
	{		
		// 캐릭터가 바라보는 방향(정면)으로 X축 이동
		AddMovementInput(GetActorForwardVector(), MoveInput.X);
	}

	if (!FMath::IsNearlyZero(MoveInput.Y))
	{
		// 캐릭터의 오른쪽 방향으로 Y축 이동
		AddMovementInput(GetActorRightVector(), MoveInput.Y);
	}
}

void AMainCharacter::Look(const FInputActionValue& value)
{
	// 마우스의 X, Y 움직임을 2D 축으로 가져옴
	FVector2D LookInput = value.Get<FVector2D>();

	// X는 좌우 회전 (Yaw), Y는 상하 회전 (Pitch)
	// 좌우 회전
	AddControllerYawInput(LookInput.X);
	
	// 상하 회전
	// IA에서 반전해둔 상태
	// 여기엔 추후 변경 가능한 옵션 추가해주자
	AddControllerPitchInput(LookInput.Y);
}

void AMainCharacter::StartJump(const FInputActionValue& value)
{
	// Jump 함수는 Character가 기본 제공
	if (value.Get<bool>())
	{
		Jump();
	}
}
void AMainCharacter::StopJump(const FInputActionValue& value)
{	
	// StopJumping 함수도 Character가 기본 제공
	if (!value.Get<bool>())
	{
		StopJumping();
	}	
}

void AMainCharacter::StartSprint(const FInputActionValue& value)
{
	// Shift 키를 누른 순간 이 함수가 호출된다고 가정
	// 스프린트 속도를 적용
	if (GetCharacterMovement())
	{
		// 중간에 변경시 
		SprintSpeed = NormalSpeed * SprintSpeedMultiplier;		
		
		GetCharacterMovement()->MaxWalkSpeed = SprintSpeed;
	}

}
void AMainCharacter::StopSprint(const FInputActionValue& value)
{
	// Shift 키를 뗀 순간 이 함수가 호출
	// 평상시 속도로 복귀
	if (GetCharacterMovement())
	{
		GetCharacterMovement()->MaxWalkSpeed = NormalSpeed;
	}
}


float AMainCharacter::GetHealth() const
{
	return Health;
}

// 체력 회복 함수
void AMainCharacter::AddHealth(float  Amount)
{
	// 체력을 회복시킴. 최대 체력을 초과하지 않도록 제한함
	Health = FMath::Clamp(Health + Amount, 0.0f, MaxHealth);
	//UE_LOG(LogTemp, Log, TEXT("Health increased to: %f"), Health);
	
	if (GEngine)
	{ 
		GEngine->AddOnScreenDebugMessage(
				-1, 2.f, FColor::Green,
				 TEXT("Health Increased to: %f"), Health);
	}
}

// 데미지 처리 함수
float  AMainCharacter::TakeDamage(float  DamageAmount, FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser)
{
	// 기본 데미지 처리 로직 호출 (필수는 아님)
	int32 ActualDamage = Super::TakeDamage(DamageAmount, DamageEvent, EventInstigator, DamageCauser);

	// 체력을 데미지만큼 감소시키고, 0 이하로 떨어지지 않도록 Clamp
	Health = FMath::Clamp(Health - DamageAmount, 0.0f, MaxHealth);
	//UE_LOG(LogTemp, Warning, TEXT("Health decreased to: %f"), Health);
	if (GEngine)
	{ 
		GEngine->AddOnScreenDebugMessage(
				-1, 2.f, FColor::Yellow,
				 TEXT("Health decreased to: %f"), Health);
	}

	// 체력이 0 이하가 되면 사망 처리
	if (Health <= 0.0f)
	{
		OnDeath();
	}

	// 실제 적용된 데미지를 반환
	return ActualDamage;
}

// 사망 처리 함수
void AMainCharacter::OnDeath()
{
	//UE_LOG(LogTemp, Error, TEXT("Character is Dead!"));
	if (GEngine)
	{ 
		GEngine->AddOnScreenDebugMessage(
				-1, 2.f, FColor::Red,
				 TEXT("Character is Dead!"));
	}

	// 사망 후 로직
}

 


 

3️⃣ 지뢰 아이템 데미지 함수 수정

지뢰 아이템 (MineItem)이 폭발할 때, 주변 액터에게 데미지를 주려면 UGameplayStatics::ApplyDamage 함수를 호출해 해당 액터의 TakeDamage()가 실행되도록 하면 됩니다.

 

  • UGamePlayStatics::ApplyDamage() 라는 UE Static 제공 함수.
  • #include "Kismet/GameplayStatics.h" 해줘야 합니다.
  • UGameplayStatics::ApplyDamage는 언리얼 엔진에서 액터에게 손쉽게 데미지를 가하고 대상의 TakeDamage() 함수를 트리거하는 핵심 함수입니다
UGameplayStatics::ApplyDamage(
    AActor* DamagedActor,           // 데미지를 받을 대상 액터
    float BaseDamage,               // 기본 데미지 양
    AController* EventInstigator,   // 데미지를 입힌 주체의 컨트롤러 (데스 카운트 등에 사용)
    AActor* DamageCauser,           // 데미지를 실제로 발생시킨 액터 (무기, 투사체 등)
    TSubclassOf<UDamageType> DamageTypeClass // 데미지 타입 클래스
);

 

  • ApplyDamage()
    1. 대상 액터(Actor)가 존재하는지 확인.
    2. 대상 액터의 TakeDamage() 함수를 호출합니다.
    3. DamageType은 여러 가지 파생 클래스를 만들어 물리/화염/독 등 다양한 데미지 유형을 정의할 수 있습니다 (지금은 기본값 사용).
  • 지뢰는 독립적으로 스폰된 뒤 폭발하므로 EventInstigator를 nullptr로 둡니다.
    • 멀티플레이에서 “누가 지뢰를 설치했느냐”를 추적하려면, 생성 시점에 Instigator나 Controller 정보를 넣어줄 수도 있습니다.
#include "MineItem.h"
#include "Components/SphereComponent.h"
#include "Kismet/GameplayStatics.h"

AMineItem::AMineItem()
{
	ExplosionDelay = 5.0f;
	ExplosionRadius = 300.0f;
	ExplosionDamage = 30.0f;
	ItemType = "Mine";
    
	ExplosionCollision = CreateDefaultSubobject<USphereComponent>(TEXT("ExplosionCollision"));
	ExplosionCollision->InitSphereRadius(ExplosionRadius);
	//ExplosionCollision->SetCollisionProfileName(TEXT("OverlapAllDynamic")); 
	ExplosionCollision->SetCollisionProfileName(CollisionProfile_OverlapAllDynamic); 
	ExplosionCollision->SetupAttachment(Scene);
}

void AMineItem::ActivateItem(AActor* Activator)
{
	// 5초 후 폭발 실행
	GetWorld()->GetTimerManager().SetTimer(ExplosionTimerHandle, this, &AMineItem::Explode, ExplosionDelay);
}

void AMineItem::Explode()
{
	TArray<AActor*> OverlappingActors;
	ExplosionCollision->GetOverlappingActors(OverlappingActors);

	for (AActor* Actor : OverlappingActors)
	{
		if (Actor && Actor->ActorHasTag("Player"))
		{
			//GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Red, FString::Printf(TEXT("Player damaged %d by MineItem"), ExplosionDamage));
			
			// 데미지를 발생시켜 Actor->TakeDamage()가 실행되도록 함
			UGameplayStatics::ApplyDamage(
				Actor,                      // 데미지를 받을 액터
				ExplosionDamage,            // 데미지 양
				nullptr,                    // 데미지를 유발한 주체 (지뢰를 설치한 캐릭터가 없으므로 nullptr)
				this,                       // 데미지를 유발한 오브젝트(지뢰)
				UDamageType::StaticClass()  // 기본 데미지 유형
			);
			
			
		}
	}

	// 지뢰 제거
	DestroyItem();
}

 


 

4️⃣ 힐링 아이템 체력 회복 함수 수정

  • 아래 코드는 힐링 아이템(HealingItem)이 플레이어를 회복시킵니다. AddHealth() 함수를 직접 호출해 체력을 증가시킵니다.
  • ActorHasTag("Player")로 플레이어인지 판별하는 단순 로직입니다.
    • 캐릭터를 구분하는 더 안전한 방법(예: Cast<AMainCharacter>(Activator))을 쓰거나, Collision 채널을 이용하는 식으로도 바꿀 수 있습니다.
  • AddHealth()에서 FMath::Clamp를 사용해 최대 체력 이상으로 올라가지 않도록 제한합니다.

 


 

점수 관리 시스템 구현하기

 

1️⃣ GameMode와 GameState의 연계 이해하기

  • 언리얼 엔진에서 GameMode와 GameState는 게임의 전역 정보를 유지하고, 필요할 경우 멀티플레이어 환경에서 해당 정보를 서버와 클라이언트 간에 동기화하는 역할을 합니다.
    • GameMode
      • “게임의 규칙(룰)”을 정의하고 관리합니다.
      • 어떤 캐릭터를 스폰할지, 플레이어가 사망했을 때 어떻게 처리할지를 결정합니다.
      • 멀티플레이에서는 서버 전용으로 동작합니다(클라이언트에는 존재하지 않음).
    • GameState
      • 게임 플레이 전반에서 “공유되어야 하는 전역 상태”를 저장합니다. GameState는 기본적으로 “레벨당 1개” 존재하며, 엔진 내부에서 데이터 동기화를 고려해 설계되었기에 전역 데이터 관리용으로 적합합니다.
      • 대표적으로 점수, 남은 시간, 현재 게임 단계(Phase), 스폰된 오브젝트의 총 개수 등을 저장합니다.
      • 멀티플레이에서는 서버가 관리하고, 클라이언트는 이를 자동으로 동기화 받아볼 수 있습니다.
  • 싱글 플레이에서도 GameState가 굳이 필요 없을 것 같지만, “전역적으로 공유해야 할 정보”를 한 군데서 관리하면 유지보수가 더 편해집니다. “몇 개의 아이템이 스폰되었는지”, “현재 게임 진행도는 어느 정도인지” 같은 데이터를 GameState에서 일괄 관리할 수 있기 때문입니다.
  • 예시: 전역적인 스폰 제한 로직
    • “현재 게임에 스폰된 아이템이 30개를 넘으면, 더 이상 아이템을 스폰하지 않는다.”
    • 이 로직을 GameState에 두고 관리한다면:
      1. GameState 내부에서 “스폰된 아이템 개수” 변수를 관리
      2. 스폰이 일어날 때마다 이 변수를 증가시키고, 30개를 초과하는지 체크
      3. 초과 시 GameMode(또는 스폰 시스템)에서 “더 이상 스폰 금지” 로직 적용
    • 이처럼 전역적으로 공유되어야 할 데이터(점수, 스폰 개수, 타이머 등)는 GameState에 저장하고, 게임 규칙이나 흐름을 제어하는 로직은 GameMode 또는 별도의 매니저 클래스로 분리하는 것이 일반적입니다.

2️⃣ GameState에 점수 데이터 및 함수 추가

  • 이번에는 전역 점수를 GameState에 저장하고, 누군가 점수를 획득할 때마다 AddScore()를 호출해 점수가 증가하도록 구현해봅니다.
  • 언리얼 에디터에서 C++ Class → GameStateBase를 선택하고, 클래스 이름을 MainGameStateBase 라고 짓습니다.
  • Main GameStateBase.h와 MainGameStateBase.cpp에 아래와 같이 코드를 작성합니다.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/GameState.h"
#include "MainGameState.generated.h"


UCLASS()
class BC_CH3_ASSIGNMENT_5_API AMainGameState : public AGameState
{
	GENERATED_BODY()
	
public:
	AMainGameState();	
	
	// === Variables ===
	// 전역 점수를 저장하는 변수
	UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category="Score")
	int32 Score;

	
	// === Func ===
	// 현재 점수를 읽는 함수
	UFUNCTION(BlueprintPure, Category="Score")
	int32 GetScore() const;
	
	// 점수를 추가해주는 함수
	UFUNCTION(BlueprintCallable, Category="Score")
	void AddScore(int32 Amount);
	
};

 

#include "MainGameState.h"

AMainGameState::AMainGameState()
{
	
}

int32 AMainGameState::GetScore() const
{
	return Score;
}

void AMainGameState::AddScore(int32 Amount)
{
	Score += Amount;
}

 


 

3️⃣ GameMode와 GameState 연동

다음으로, 기존에 만들었던 GameMode 클래스에서 GameStateClass 멤버를 우리가 만든 MainGameState로 설정해줍니다.

  • 방금 만든 MainGameState 에 대한 C++ 코드가 컴파일되면, 언리얼 에디터에서 해당 클래스를 인식하게 됩니다.
  • MainGameState 를 더 확장하거나 블루프린트 비주얼 스크립팅을 사용하기 위해, C++ 클래스를 부모로 하는 블루프린트(BP_ MainGameState)를 만들어서 사용합니다.
  • Project Settings 적용
    • Edit → Project Settings → Maps & Modes로 들어가서 Default GameMode를 우리 커스텀 게임 모드( MainGameState 또는 그 블루프린트 클래스)로 설정합니다.
    • 그 뒤 Game State Class도 BP_MainGameState (또는 MainGameState )로 맞춰주면, 전역적으로 이 설정이 적용됩니다.
  • World Settings에서 설정 (레벨별로 오버라이드 가능)

#include "MainGameMode.h"
#include "MainCharacter.h"
#include "MainPlayerController.h"
#include "MainGameState.h"

AMainGameMode::AMainGameMode()
{
	// StaticClass(): 클래스 이름을 통해서 호출해주는것
	DefaultPawnClass = AMainCharacter::StaticClass();
	//
	PlayerControllerClass = AMainPlayerController::StaticClass();
	GameStateClass = AMainGameState::StaticClass();
}

 


 

4️⃣ 코인 아이템 점수 획득 함수 수정

  • GetWorld()->GetGameState<AMainGameState>()로 게임 스테이트를 가져오고, AddScore(PointValue) 함수를 호출해 점수를 올립니다.
  • PointValue(int32)는 이 코인 아이템이 제공하는 점수량이며, ACoinItem의 멤버 변수로 관리하고 있습니다.
  • 이 과정을 통해 플레이어가 코인을 획득하면, 전역적으로 관리되는 GameState에서 점수가 증가하고, 코인은 사라져서 한 번만 획득되도록 처리됩니다.
#include "CoinItem.h"
#include "Engine/World.h"
#include "MainGameState.h"

ACoinItem::ACoinItem()
{
	// 부모클래스라 안해도 되지만 
	// 점수 기본값을 0으로 설정
	PointValue = 0;
	ItemType = "DefaultCoin";
}

void ACoinItem::ActivateItem(AActor* Activator)
{
	// 플레이어 태그 확인
	if (Activator && Activator->ActorHasTag("Player"))
	{
		// 점수 획득 디버그 메시지
		GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Green, 
			FString::Printf(TEXT("Player Gained %i Points!"), PointValue));
        
		if (UWorld* World = GetWorld())
		{
			if (AMainGameState* GameState = World->GetGameState<AMainGameState>())
			{
				GameState->AddScore(PointValue);
			}
		}
		
		// 부모 클래스 (BaseItem)에 정의된 아이템 파괴 함수 호출
		DestroyItem();
	}
}

 

 

추천

 

[Unreal Engine/UE 기초] - 인터페이스 기반 아이템 클래스 설계하기 | [언리얼 엔진 C++ (Unreal Engine C++)]

 

인터페이스 기반 아이템 클래스 설계하기 | [언리얼 엔진 C++ (Unreal Engine C++)]

인터페이스 기반 아이템 클래스 설계하기 인터페이스 이해하기1️⃣ 인터페이스란?인터페이스 (Interface)란 클래스 (또는 오브젝트)가 반드시 구현해야 할 함수 목록만을 미리 정의해 두고, 실제

devcol.tistory.com

[Unreal Engine/UE 기초] - 충돌 이벤트로 획득되는 아이템 구현하기 | [언리얼 엔진 C++ (Unreal Engine C++)]

 

충돌 이벤트로 획득되는 아이템 구현하기 | [언리얼 엔진 C++ (Unreal Engine C++)]

충돌 이벤트로 획득되는 아이템 구현하기 이전 포스팅에서는 클래스 구조만 짜고 로직은 비여있던 상태.이전 포스팅: [Unreal Engine/UE 기초] - 인터페이스 기반 아이템 클래스 설계하기 | [언리얼

devcol.tistory.com

[Unreal Engine/UE 기초] - 아이템 스폰 및 레벨 데이터 관리하기 | [언리얼 엔진 C++ (Unreal Engine C++)]

 

아이템 스폰 및 레벨 데이터 관리하기 | [언리얼 엔진 C++ (Unreal Engine C++)]

아이템 스폰 및 레벨 데이터 관리하기 랜덤 위치에 아이템 스폰하기 1️⃣ 레벨 셋팅하기Resources 폴더에 Maps 폴더에는 3개의 레벨이 이미 존재합니다. 각각 난이도에 따라서 크기가 다른 맵이고,

devcol.tistory.com

 

 

[페이지] Unreal Engine | 언리얼 엔진

 

 

저작자표시 동일조건 (새창열림)
'Unreal Engine/UE 기초' 카테고리의 다른 글
  • UI 위젯 설계와 실시간 데이터 연동하기 | [언리얼 엔진 C++ (Unreal Engine C++)]
  • 게임 루프 설계를 통한 게임 흐름 제어하기 | [언리얼 엔진 C++ (Unreal Engine C++)]
  • 아이템 스폰 및 레벨 데이터 관리하기 | [언리얼 엔진 C++ (Unreal Engine C++)]
  • 충돌 이벤트로 획득되는 아이템 구현하기 | [언리얼 엔진 C++ (Unreal Engine C++)]
DevCol
DevCol
DevCol (Development Collaboration). 함께 개발 & 공부 & IT 정보 나눔장소
  • DevCol
    DevCol (Development Collaboration)
    DevCol
  • 블로그 메뉴

    • Unreal Engine
    • TIL
    • 게임국가기술자격검정 게임프로그래밍전문가 [한국콘텐츠진흥원]
    • 분류 전체보기 (73) N
      • Unreal Engine (31) N
        • Project (2) N
        • Dev Log (0)
        • Debugging (2) N
        • Blueprint (1)
        • UE 기초 (25) N
        • UE 심화 (0)
        • TA (1) N
      • Programming Language (0)
        • C++ (0)
        • C# (0)
      • Unity Engine (0)
      • 자격증 (3)
        • 게임국가기술자격검정 [한국콘텐츠진흥원] (3)
      • Coding Test | 코딩테스트 (0)
        • 프로그래머스 기초 (0)
        • 프로그래머스 입문 (0)
      • TIL (38) N
        • Boot Camp (32) N
      • Git & Github (1)
  • 링크

    • Youtube
    • GitHub
    • itch.io
    • Blog (En)
  • 공지사항

  • 인기 글

  • 태그

    기초
    cpp
    프로그래밍
    언리얼 엔진
    게임 개발
    게임개발
    내일배움캠프
    Unreal engine
    C++
    UE5
    Devlog
    Game Dev
    til
    c
    Programming
    Boot Camp
    코드 카타
    Code Kata
    코드카타
    UE
  • 최근 글

  • GitHub Youtube itch
  • hELLO · Designed By 정상우.v4.10.6
  • DevCol
    캐릭터 체력 및 점수 관리 시스템 구현하기 | [언리얼 엔진 C++ (Unreal Engine C++)]
    상단으로

    티스토리툴바