📒학습 내용
🛡️ 캐릭터 체력 시스템
1. 핵심 구조
- 캐릭터 클래스 내에 Health (현재 체력)와 MaxHealth (최대 체력)를 선언한다.
- PlayerState 대신 Character 클래스 내부에서 체력을 관리한다.
- 변수 및 함수는 리플렉션 변수 → 가상함수 → 리플렉션 함수 → 일반 함수 순으로 정리한다.
구조화 팁: 변수와 함수 순서를 구분하여 정리함으로써 코드 가독성을 높인다.
2. 주요 기능 구현
- 체력 관련 변수와 함수가 정의된다.
- TakeDamage() (데미지 처리 함수)는 데미지 입력 후 체력을 감소시킨다.
- AddHealth() (체력 회복 함수)와 OnDeath() (캐릭터 사망 처리 함수)가 각각 체력 회복과 사망 처리를 수행한다.
2.1 체력 변수 및 함수 구현 - [기본 기능]
- 체력 관련 변수를 구현한다.
- MaxHealth: 캐릭터의 최대 체력을 저장한다.
- Health: 캐릭터의 현재 체력을 저장한다.
- 핵심 함수를 구현한다.
- TakeDamage(): 데미지 입력 후 체력을 감소시키며, 0 이하 시 OnDeath()를 호출한다.
- DamageAmount (입력된 데미지)와 실제 적용된 ActualDamage를 구분하여 처리한다.
- AddHealth(): 체력 회복 값을 추가하며, Clamp (값 제한 함수)를 사용하여 범위를 제한한다.
- OnDeath(): 체력이 0 이하일 때 캐릭터의 사망 처리를 수행한다.
- TakeDamage(): 데미지 입력 후 체력을 감소시키며, 0 이하 시 OnDeath()를 호출한다.
더보기
// SP_Pawn.h
#pragma once
#include "CoreMinimal.h"
#include "InputActionValue.h"
#include "Animation/AnimInstance.h"
#include "GameFramework/Pawn.h"
#include "SP_Pawn.generated.h"
UCLASS()
class START_API ASP_Pawn : public APawn
{
GENERATED_BODY()
public:
ASP_Pawn();
virtual void Tick(float DeltaTime) override;
// 루트 충돌 컴포넌트
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
class UBoxComponent* CollisionComp;
// 스켈레탈 메쉬 컴포넌트
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
class USkeletalMeshComponent* SkeletalMesh;
// 스프링 암 컴포넌트
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
class USpringArmComponent* SpringArm;
// 카메라 컴포넌트
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
class UCameraComponent* Camera;
UPROPERTY(BlueprintReadOnly, Category = "Input")
float MoveRightValue = 0.0f; // MoveRight 입력 값 저장
// 현재 체력을 가져오는 함수
UFUNCTION(BlueprintPure, Category = "Health")
float GetHealth() const;
// 체력 회복을 위한 함수
UFUNCTION(BlueprintCallable, Category = "Health")
void AddHealth(float Amount);
protected:
// 캐릭터 최대 체력 정의
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Health")
float MaxHealth = 100.0f;
// 캐릭터 현재 체력 정의
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Health")
float Health = 0.0f;
// 부스터 효과를 위한 컴포넌트
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Effects")
class UParticleSystemComponent* BoosterEffect;
// 부스터 사운드를 위한 컴포넌트
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Effects")
class UAudioComponent* BoosterSound;
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
// 데미지 처리 함수 오버라이드
virtual float TakeDamage(
float DamageAmount, // 데미지 양
struct FDamageEvent const& DamageEvent, // 데미지 유형 및 추가 정보 전달
class AController* EventInstigator, // 데미지 발생 주체 전달
AActor* DamageCauser // 데미지 유발 액터 전달
) override;
// 이동 및 기타 동작 함수 정의
void Move(const FVector& Direction, float AxisValue);
void MoveForward(const FInputActionValue& Value);
void MoveRight(const FInputActionValue& Value);
void Look(const FInputActionValue& Value);
void MoveUp(const FInputActionValue& Value);
void ActivateBooster(const FInputActionValue& Value);
void DeactivateBooster(const FInputActionValue& Value);
void RotateRoll(const FInputActionValue& Value);
void ToggleFlightHold(const FInputActionValue& Value);
void ApplyGravity(float DeltaTime);
void CheckGround();
void ApplyTiltEffect(float DeltaTime);
// 데미지 시스템 관련 함수 정의
void OnDeath();
private:
// 이동 속도, 회전 속도, 중력 등 물리 관련
const float MoveSpeed = 950.0f;
const float RotationSpeed = 90.0f;
const float Gravity = -500.0f;
const float Power = 90.0f;
const float AirControlFactor = 0.9f;
const float GroundCheckDistance = 1.0f;
float HoverAltitude = 0.0f;
FVector Velocity = FVector::ZeroVector;
bool bIsGrounded = false;
bool bIsFlightHold = false;
bool bIsBoosting = false;
float BoostSpeed = 3000.0f;
};
#include "SP_Pawn.h"
#include "SP_PlayerController.h"
#include "EnhancedInputComponent.h"
#include "Components/BoxComponent.h"
#include "Components/SkeletalMeshComponent.h"
#include "GameFramework/SpringArmComponent.h"
#include "Camera/CameraComponent.h"
#include "Particles/ParticleSystemComponent.h"
#include "Components/AudioComponent.h"
// Sets default values
ASP_Pawn::ASP_Pawn()
{
PrimaryActorTick.bCanEverTick = true;
// 충돌 컴포넌트 (루트)
CollisionComp = CreateDefaultSubobject<UBoxComponent>(TEXT("CollisionComp"));
CollisionComp->SetBoxExtent(FVector(50.f, 50.f, 50.f));
CollisionComp->SetSimulatePhysics(false);
RootComponent = CollisionComp;
// 스켈레탈 메쉬 컴포넌트
SkeletalMesh = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("SkeletalMesh"));
SkeletalMesh->SetupAttachment(CollisionComp);
SkeletalMesh->SetSimulatePhysics(false);
// 스프링 암 컴포넌트
SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("SpringArm"));
SpringArm->SetupAttachment(SkeletalMesh);
SpringArm->TargetArmLength = 300.0f;
// Pawn회전과 독립적으로 동작, 왜? 이동 방향을 카메라의 Forward/Right 벡터를 기준으로 계산하여 회전해야 하기 때문에.
SpringArm->bUsePawnControlRotation = false;
// 카메라 컴포넌트
Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));
Camera->SetupAttachment(SpringArm, USpringArmComponent::SocketName);
Camera->bUsePawnControlRotation = false; // 카메라는 스프링 암의 회전만 따르도록 설정
// 부스터 효과 (파티클 및 사운드)
BoosterEffect = CreateDefaultSubobject<UParticleSystemComponent>(TEXT("BoosterEffect"));
BoosterEffect->SetupAttachment(SkeletalMesh);
BoosterEffect->bAutoActivate = false;
BoosterSound = CreateDefaultSubobject<UAudioComponent>(TEXT("BoosterSound"));
BoosterSound->SetupAttachment(SkeletalMesh);
BoosterSound->bAutoActivate = false;
Health = MaxHealth;
}
void ASP_Pawn::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
ApplyGravity(DeltaTime);
CheckGround();
ApplyTiltEffect(DeltaTime);
// 부스터 상태에 따른 파티클 효과 업데이트
if (bIsBoosting)
{
if (BoosterEffect && !BoosterEffect->IsActive())
{
BoosterEffect->Activate();
}
if (BoosterSound && !BoosterSound->IsPlaying())
{
BoosterSound->Play();
}
}
else
{
if (BoosterEffect && BoosterEffect->IsActive())
{
BoosterEffect->Deactivate();
}
if (BoosterSound && BoosterSound->IsPlaying())
{
BoosterSound->Stop();
}
}
}
// 입력 바인딩
void ASP_Pawn::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
if (UEnhancedInputComponent* EnhancedInput = Cast<UEnhancedInputComponent>(PlayerInputComponent))
{
if (ASP_PlayerController* PlayerController = Cast<ASP_PlayerController>(GetController()))
{
if (PlayerController->MoveForwardAction)
EnhancedInput->BindAction(PlayerController->MoveForwardAction, ETriggerEvent::Triggered, this, &ASP_Pawn::MoveForward);
if (PlayerController->MoveRightAction)
EnhancedInput->BindAction(PlayerController->MoveRightAction, ETriggerEvent::Triggered, this, &ASP_Pawn::MoveRight);
if (PlayerController->LookAction)
EnhancedInput->BindAction(PlayerController->LookAction, ETriggerEvent::Triggered, this, &ASP_Pawn::Look);
if (PlayerController->MoveUpAction)
EnhancedInput->BindAction(PlayerController->MoveUpAction, ETriggerEvent::Triggered, this, &ASP_Pawn::MoveUp);
if (PlayerController->SprintAction)
{
EnhancedInput->BindAction(PlayerController->SprintAction, ETriggerEvent::Started, this, &ASP_Pawn::ActivateBooster);
EnhancedInput->BindAction(PlayerController->SprintAction, ETriggerEvent::Completed, this, &ASP_Pawn::DeactivateBooster);
}
if (PlayerController->RotateRollAction)
EnhancedInput->BindAction(PlayerController->RotateRollAction, ETriggerEvent::Triggered, this, &ASP_Pawn::RotateRoll);
if (PlayerController->FlightHoldAction)
EnhancedInput->BindAction(PlayerController->FlightHoldAction, ETriggerEvent::Triggered, this, &ASP_Pawn::ToggleFlightHold);
}
}
}
void ASP_Pawn::Move(const FVector& Direction, float AxisValue)
{
if (FMath::IsNearlyZero(AxisValue))
{
Velocity = FMath::VInterpTo(Velocity, FVector::ZeroVector, GetWorld()->GetDeltaSeconds(), 2.0f);
return;
}
float CurrentSpeed = bIsBoosting ? BoostSpeed : MoveSpeed;
float SpeedMultiplier = (bIsGrounded && !bIsFlightHold) ? 1.0f : AirControlFactor;
FVector TargetVelocity = Direction * (AxisValue * CurrentSpeed * SpeedMultiplier);
// 부드러운 속도 보간
Velocity = FMath::VInterpTo(Velocity, TargetVelocity, GetWorld()->GetDeltaSeconds(), 6.0f);
}
void ASP_Pawn::MoveForward(const FInputActionValue& Value)
{
Move(GetActorForwardVector(), Value.Get<float>());
}
void ASP_Pawn::MoveRight(const FInputActionValue& Value)
{
float AxisValue = Value.Get<float>();
// A/D 입력값 전달 (왼쪽: -1, 오른쪽: 1, 중립: 0)
MoveRightValue = AxisValue;
Move(GetActorRightVector(), Value.Get<float>());
}
void ASP_Pawn::MoveUp(const FInputActionValue& Value)
{
if (bIsFlightHold) return; // 호버링 상태에서는 수직 이동 입력을 무시하여 높이 고정
Move(FVector::UpVector, Value.Get<float>());
}
void ASP_Pawn::ActivateBooster(const FInputActionValue& Value)
{
bIsBoosting = true;
}
void ASP_Pawn::DeactivateBooster(const FInputActionValue& Value)
{
bIsBoosting = false;
}
void ASP_Pawn::Look(const FInputActionValue& Value)
{
FVector2D LookInput = Value.Get<FVector2D>();
float DeltaTime = GetWorld()->GetDeltaSeconds();
// Yaw 회전 (좌우 회전)
AddActorLocalRotation(FRotator(0.0f, LookInput.X * RotationSpeed * DeltaTime, 0.0f));
// Pitch 회전 (카메라 상하 회전)
FRotator NewSpringArmRot = SpringArm->GetRelativeRotation();
NewSpringArmRot.Pitch = FMath::ClampAngle(NewSpringArmRot.Pitch + LookInput.Y * RotationSpeed * DeltaTime, -60.0f, 60.0f);
SpringArm->SetRelativeRotation(NewSpringArmRot);
}
void ASP_Pawn::RotateRoll(const FInputActionValue& Value)
{
float AxisValue = Value.Get<float>();
if (FMath::IsNearlyZero(AxisValue)) return;
FRotator CurrentRotation = GetActorRotation();
CurrentRotation .Roll += AxisValue * RotationSpeed * GetWorld()->GetDeltaSeconds();
// Roll 보간 적용
SetActorRotation(CurrentRotation);
}
// ============ //
// 중력 기능 추가 //
// ============ //
void ASP_Pawn::ApplyGravity(float DeltaTime)
{
if (bIsFlightHold)
{
// 비행 홀드 시 중력 제거, BUT 이동 가능하도록 속도 유지
Velocity.Z = FMath::FInterpTo(Velocity.Z, 0.0f, DeltaTime, 2.0f);
//return; : 활성화 하면 호버링 상태로 고정돼버림
}
if (!bIsGrounded)
{
Velocity.Z += Gravity * DeltaTime;
}
FVector NewLocation = GetActorLocation() + Velocity * DeltaTime;
// 호버링 상태이면 수직 위치를 기록된 HoverAltitude로 고정
if (bIsFlightHold)
{
NewLocation.Z = HoverAltitude;
}
// 호버링 상태이면 수직 위치를 고정
if (bIsFlightHold)
{
NewLocation.Z = HoverAltitude;
}
FHitResult Hit;
// Sweep 기능으로 이동 시 충돌이 발생하면 Hit에 반영되어 지면에서 멈춥니다.
SetActorLocation(NewLocation, true, &Hit);
if (Hit.IsValidBlockingHit())
{
bIsGrounded = true;
Velocity.Z = 1.0f;
}
}
void ASP_Pawn::CheckGround()
{
FVector Start = GetActorLocation();
FVector End = Start + FVector(0, 0, -GroundCheckDistance);
FHitResult Hit;
FCollisionQueryParams Params;
Params.AddIgnoredActor(this);
bool bHit = GetWorld()->LineTraceSingleByChannel(Hit, Start, End, ECC_Visibility, Params);
if (bHit)
{
// 땅에 너무 가까우면 HoverAltitude로 높이를 조정
if (Hit.Distance < GroundCheckDistance + 10.0f)
{
HoverAltitude = Hit.Location.Z + 50.0f; // 땅에서 50 유닛 높이 유지
}
bIsGrounded = true;
}
else
{
bIsGrounded = false;
}
}
// 틸트 기능!!
void ASP_Pawn::ApplyTiltEffect(float DeltaTime)
{
FRotator CurrentRotation = GetActorRotation();
const float MaxTiltAngle = 15.0f;
const float TiltSpeed = 5.0f; // 틸트 반응 속도
const float RecoverySpeed = 5.5f; // 회복 속도
const float SineFactor = 0.8f; // 사인 그래프를 조절하는 팩터
// 비행 홀드 상태면 기울기를 원래 상태로 복구
if (bIsFlightHold)
{
FRotator TargetRotation = CurrentRotation;
TargetRotation.Pitch = 0.0f;
TargetRotation.Roll = 0.0f;
// 사인 그래프 보간을 적용하여 부드럽게 복구
float Alpha = FMath::Sin(RecoverySpeed * DeltaTime);
SetActorRotation(FMath::Lerp(CurrentRotation, TargetRotation, Alpha));
return;
}
// 이동 방향 벡터 정규화
FVector LocalVelocity = GetActorRotation().UnrotateVector(Velocity).GetSafeNormal();
// 목표 틸트 각도 계산 (속도가 작을수록 기울기도 작아지도록 보정)
float TargetPitch = -LocalVelocity.X * MaxTiltAngle;
float TargetRoll = LocalVelocity.Y * MaxTiltAngle;
// 이동이 멈추면 원래 자세로 복귀
if (Velocity.IsNearlyZero())
{
TargetPitch = 0.0f;
TargetRoll = 0.0f;
}
float Alpha = FMath::Sin(TiltSpeed * DeltaTime * SineFactor);
FRotator TargetRotation = CurrentRotation;
TargetRotation.Pitch = FMath::Lerp(CurrentRotation.Pitch, TargetPitch, Alpha);
TargetRotation.Roll = FMath::Lerp(CurrentRotation.Roll, TargetRoll, Alpha);
SetActorRotation(TargetRotation);
}
// 호버링(비행 홀드) 기능
void ASP_Pawn::ToggleFlightHold(const FInputActionValue& Value)
{
bIsFlightHold = !bIsFlightHold;
// 호버링 전환 시 수직 속도를 제거, 현재 높이를 기록하여 고정
if (bIsFlightHold)
{
HoverAltitude = GetActorLocation().Z;
Velocity.Z = 0.0f;
}
}
//======================
// 캐릭터 HP, Damage 관련
//======================
float ASP_Pawn::GetHealth() const
{
return Health;
}
void ASP_Pawn::AddHealth(float Amount)
{
Health = FMath::Clamp(Health + Amount, 0.0f, MaxHealth);
UE_LOG(LogTemp, Warning, TEXT("Health increased to : %f"), Health);
}
float ASP_Pawn::TakeDamage(float DamageAmount, struct FDamageEvent const& DamageEvent,
class 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 : %f"), Health);
if (Health <= 0.0f)
{
OnDeath();
}
return ActualDamage;
}
void ASP_Pawn::OnDeath()
{
// 게임 종료 로직 호출
}
체력 관련 연산 시 Clamp (값 범위 제한 함수)를 사용하여 오류를 방지한다.
🎯 데미지 처리 시스템
- 데미지 처리는 AActor::TakeDamage() 함수를 통해 수행된다.
- 체력 감소 후 0 이하이면 OnDeath()를 호출하여 캐릭터 사망 처리를 수행한다.
- UGameplayStatics::ApplyDamage()로 공격자가 데미지를 적용한다.
1. 데미지 처리 흐름 구현
- AActor::TakeDamage()를 오버라이드하여 체력을 감소시킨다.
- 입력된 DamageAmount를 사용하여 체력을 감소시키며, Clamp 함수를 통해 최소 0과 최대 체력을 제한한다.
- 체력이 0 이하가 되면 OnDeath() 함수를 호출하여 캐릭터 사망 처리를 수행한다.
- UGameplayStatics::ApplyDamage()를 호출하여 데미지 처리를 진행한다.
- 해당 함수는 내부적으로 대상 액터의 TakeDamage() 함수를 실행한다.
- 데미지 발생 주체와 원인을 전달하여 정확한 데미지 처리가 이루어진다.
// ASP_Pawn.cpp
float ASP_Pawn::TakeDamage(float DamageAmount, struct FDamageEvent const& DamageEvent,
class AController* EventInstigator, AActor* DamageCauser)
{
float ActualDamage = Super::TakeDamage(DamageAmount, DamageEvent, EventInstigator, DamageCauser);
Health = FMath::Clamp(Health - DamageAmount, 0.0f, MaxHealth);
if (Health <= 0.0f)
{
OnDeath();
}
return ActualDamage;
}
// ASP_Pawn.cpp
void ASP_Pawn::AddHealth(float Amount)
{
Health = FMath::Clamp(Health + Amount, 0.0f, MaxHealth);
UE_LOG(LogTemp, Warning, TEXT("Health added to : %f"), Health);
}
- 체력 회복 함수는 입력 값을 Clamp하여 체력 범위를 유지한다.
데미지 처리 팁: 플레이어의 방어력 또는 저항 시스템 도입 시, DamageAmount와 실제 적용된 ActualDamage를 구분하여 처리해야 한다.
💣 지뢰 및 힐링 아이템 연동
- 지뢰 아이템은 범위 내 캐릭터에 데미지를 적용한다.
- 힐링 아이템은 캐릭터의 체력을 회복시킨다.
- 액터 태그와 캐스팅을 이용하여 대상 액터를 확인한다.
1. 지뢰 아이템 연동
- 지뢰 아이템은 범위 내 겹친 액터를 검출한다.
- GetOverlappingActors()를 사용하여 겹친 액터 목록을 가져온다.
- ActorHasTag("Player")를 통해 플레이어 여부를 확인한다.
- UGameplayStatics::ApplyDamage()를 호출하여 대상 액터에 데미지를 적용한다.
- 아이템 사용 후 제거한다.
// MineItem.cpp
void AMineItem::Explode()
{
TArray<AActor*> OverlappingActors;
MineCollision->GetOverlappingActors(OverlappingActors);
for (AActor* Actor : OverlappingActors)
{
if (Actor && Actor->ActorHasTag("Player"))
{
UGameplayStatics::ApplyDamage(
Actor, // 데미지 대상
ExplosiveDamage, // 데미지 양
nullptr, // 발생 주체 (게임 구현상 없음)
this, // 데미지 원인 객체
UDamageType::StaticClass()); // 기본 데미지 유형을 사용한다.
}
}
DestroyItem();
}
- UGameplayStatics 사용 시 헤더 파일을 포함한다.
2. 힐링 아이템 연동
- 힐링 아이템은 액터 태그를 확인하여 플레이어 캐릭터를 식별한다.
- ActorHasTag("Player")로 플레이어 여부를 확인한다.
- 캐릭터를 ASP_Pawn으로 캐스팅한 후, AddHealth() 함수를 호출하여 체력을 회복시킨다.
- 아이템 사용 후 제거한다.
// HealingItem.cpp
void AHealingItem::ActivateItem(AActor* Activator)
{
if (Activator && Activator->ActorHasTag("Player"))
{
if (ASP_Pawn* PlayerCharacter = Cast<ASP_Pawn>(Activator))
{
PlayerCharacter->AddHealth(HealAmount);
}
DestroyItem();
}
}
🎮 점수 관리 시스템
- GameMode와 GameState를 활용하여 게임 전역의 점수 및 규칙을 관리한다.
- GameState는 모든 클라이언트에 동기화되어 전역 데이터를 제공한다.
- 코인 아이템과 연동하여 점수 증가 로직이 구현된다.
1. GameMode와 GameState 활용 - [전역 데이터 관리]
- GameMode는 게임 규칙, 캐릭터 스폰 및 흐름을 관리하며 서버 전용으로 동작한다.
- GameState는 점수, 남은 시간 등 전역 데이터를 관리하며 모든 클라이언트에 공유된다.
- 싱글 플레이에서도 유지보수를 위해 GameState를 활용한다.
GameMode | 게임 규칙 및 캐릭터 스폰 로직을 관리하며 서버 전용으로 동작한다. |
GameState | 전역 데이터를 관리하며 모든 클라이언트에 동기화되어 제공된다. |
2. GameState에 점수 데이터 및 함수 추가 - [점수 관리 기능]
- MyGameStateBase 클래스에서 전역 점수를 관리한다.
- Score 변수를 선언하여 점수 데이터를 저장한다.
- GetScore() 함수는 현재 점수를 반환한다.
- AddScore() 함수는 입력된 점수만큼 증가시킨다.
// MyGameStateBase.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/GameStateBase.h"
#include "MyGameStateBase.generated.h"
UCLASS()
class START_API AMyGameStateBase : public AGameStateBase
{
GENERATED_BODY()
public:
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Score")
int32 Score;
// 점수를 가져오는 함수를 선언한다.
UFUNCTION(BlueprintPure, Category = "Score")
int32 GetScore() const;
// 점수를 증가시키는 함수를 선언한다.
UFUNCTION(BlueprintCallable, Category = "Score")
void AddScore(int32 Amount);
};
// MyGameStateBase.cpp
#include "MyGameStateBase.h"
AMyGameStateBase::AMyGameStateBase()
{
Score = 0;
}
int32 AMyGameStateBase::GetScore() const
{
return Score;
}
void AMyGameStateBase::AddScore(int32 Amount)
{
Score += Amount;
}
🎮GameMode와 GameState 연동
- SP_GameMode 클래스에서 GameStateClass를 AMyGameStateBase로 설정한다.
- 이를 통해 게임 내 전역 데이터가 서버와 클라이언트 간 자동 동기화된다.
// SP_GameMode.cpp (일부 발췌)
#include "SP_GameMode.h"
#include "SP_Pawn.h"
#include "SP_PlayerController.h"
#include "MyGameStateBase.h"
ASP_GameMode::ASP_GameMode()
{
DefaultPawnClass = ASP_Pawn::StaticClass();
PlayerControllerClass = ASP_PlayerController::StaticClass();
GameStateClass = AMyGameStateBase::StaticClass();
}
- 위 설정은 싱글 플레이와 멀티 플레이 환경 모두에서 전역 데이터의 일관된 관리를 보장한다.
🪙코인 아이템 점수 획득 적용
- 플레이어가 코인 아이템을 획득하면, 현재 게임 월드에서 GameState를 가져온다.
- Engine/World.h 헤더를 포함하여 게임 월드를 참조한다.
- MyGameStateBase의 AddScore() 함수를 호출하여 점수를 증가시킨다.
- 아이템 사용 후 해당 아이템을 제거한다.
// CoinItem.cpp
#include "CoinItem.h"
#include "Engine/World.h"
#include "MyGameStateBase.h"
ACoinItem::ACoinItem()
{
PointValue = 0;
ItemType = "DefaultCoinItem";
}
void ACoinItem::ActivateItem(AActor* Activator)
{
if (Activator && Activator->ActorHasTag("Player"))
{
if (UWorld* World = GetWorld())
{
if (AMyGameStateBase* GameState = World->GetGameState<AMyGameStateBase>())
{
GameState->AddScore(PointValue);
}
}
DestroyItem();
}
}
- UI 연동 시 BlueprintPure 함수를 활용하여 실시간 점수 표시가 용이하도록 구성한다.
'Dev. > UE 언리얼 엔진' 카테고리의 다른 글
[TIL_250214] UI 위젯 설계와 실시간 데이터 연동하기 (0) | 2025.02.14 |
---|---|
[TIL_250213_3] 게임 루프 설계를 통한 게임 흐름 제어 (0) | 2025.02.13 |
[TIL_250213_1] 머티리얼 시스템 이해 (기초) 2 #그래픽스 (0) | 2025.02.13 |
[TIL] 아이템 랜덤 스폰 및 레벨 데이터 관리(아이템 테이블) (0) | 2025.02.12 |
[TIL_250211_2] 충돌 이벤트를 활용한 아이템 획득 시스템 구현하기 (0) | 2025.02.11 |