멀티플레이어 게임 개발 6 (FMath, 캐싱, 판정 로직)

김여울·2025년 9월 5일
1

내일배움캠프

목록 보기
70/111

1 FMath

  • FMath는 함수가 아니라 struct
  • 언리얼 엔진에서 수학 관련 정적 함수(static function)들을 모아둔 수학 유틸리티 모음집

1.1 특징

  • 직접 객체를 만들 필요 없음 → 그냥 FMath::함수이름() 으로 호출
  • 대부분 C++ 표준 수학 함수들을 언리얼 스타일로 묶어둔 거 + 게임 개발용 유틸 추가

1.2 자주 쓰는 함수들

랜덤 관련

int32 Value = FMath::Rand();            // 임의의 정수
int32 ValueInRange = FMath::RandRange(1, 10);   // 1~10 사이
FMath::RandInit(Seed);                  // 랜덤 시드 초기화

보간/보정

float Lerp = FMath::Lerp(0.f, 100.f, 0.5f);   // 50.0 (선형 보간)
float Clamp = FMath::Clamp(120.f, 0.f, 100.f); // 100.0 (최소/최대 제한)

삼각/제곱/제곱근

float SinVal = FMath::Sin(PI / 2);      // 1.0
float Power = FMath::Pow(2.f, 3.f);     // 8.0
float Sqrt = FMath::Sqrt(16.f);         // 4.0

비교/최소/최대

int32 MinVal = FMath::Min(3, 7);        // 3
int32 MaxVal = FMath::Max(3, 7);        // 7
bool IsNearly = FMath::IsNearlyZero(0.0001f); // true

1.3 정리

  • FMath = 수학 함수 모음 struct
  • 객체 생성 X → 그냥 FMath::함수() 로 호출
  • 랜덤, 보간, 삼각함수, Clamp 등 게임 개발에서 자주 쓰이는 수학 기능 지원

2. 캐싱

  • 데이터를 미리 저장해 두고 나중에 빠르게 접근하기 위해 사용하는 기법

  • 데이터를 반복적으로 계산/처리하지 않고 미리 저장해서 성능을 향상시키는 방법

  • 수학 문제를 풀 때 한 번 푼 값을 저장해두고 다시 계산하지 않게 하는 거랑 비슷한 개념

    • 캐시된 값은 빠르게 읽을 수 있는 메모리에 저장되므로, 처리 시간을 줄여줌
// CXGameModeBase.h

...

class ACXPlayerController;

/**
 * 
 */
UCLASS()
class CHATX_API ACXGameModeBase : public AGameModeBase
{
	GENERATED_BODY()

public:
	...
	
	virtual void BeginPlay() override;
	
	void PrintChatMessageString(ACXPlayerController* InChattingPlayerController, const FString& InChatMessageString);

protected:
	FString SecretNumberString;  // 만든 SecreatNumber을 저장해주기 -> 캐싱

	TArray<TObjectPtr<ACXPlayerController>> AllPlayerControllers;  // 참가자들 저장	
};

3 판정 로직

// CXGameModeBase.cpp

...
#include "Player/CXPlayerController.h"
#include "EngineUtils.h"

// 로그인 후에 호출되는 함수
void ACXGameModeBase::OnPostLogin(AController* NewPlayer)  // 컨트롤러 넣어줌
{
	...

	ACXPlayerController* CXPlayerController = Cast<ACXPlayerController>(NewPlayer);  // 형변환
	if (IsValid(CXPlayerController) == true)  
	{
		AllPlayerControllers.Add(CXPlayerController);  
	}
}

...

void ACXGameModeBase::BeginPlay()
{
	Super::BeginPlay();

  // 게임 시작 시 한 번만 정답 번호 만들기 -> GenerateSecreatNumber()
	// 캐싱하기 -> SecretNumberString
	SecretNumberString = GenerateSecretNumber();  
	// UE_LOG(LogTemp, Error, TEXT("%s"), *SecreatNumberString();
}

// GameMode는 서버 전용 액터니까 클라이언트에서 서버 RPC로 호출하기
void ACXGameModeBase::PrintChatMessageString(ACXPlayerController* InChattingPlayerController, const FString& InChatMessageString)
{
	// FString ChatMessageString = InChatMessageString;  // 채팅메세지 받아오기 -> 이거 활용 안함
	int Index = InChatMessageString.Len() - 3;
	FString GuessNumberString = InChatMessageString.RightChop(Index);
	if (IsGuessNumberString(GuessNumberString) == true)  // 이 번호가 추측하려고 낸 번호인지 확인
	{
		FString JudgeResultString = JudgeResult(SecretNumberString, GuessNumberString);  // 담아주기
		for (TActorIterator<ACXPlayerController> It(GetWorld()); It; ++It)  // 플레이어 컨트롤러 전체 순회 돌기
		{
			ACXPlayerController* CXPlayerController = *It;
			if (IsValid(CXPlayerController) == true)
			{
				FString CombinedMessageString = InChatMessageString + TEXT(" -> ") + JudgeResultString;  // 결과에 대한 문자열을 combine
				CXPlayerController->ClientRPCPrintChatMessageString(CombinedMessageString);  // 클라한테
			}
		}
	}
	else  // GuessNumber가 아니라면 -> 게임하려고 친 게 아니라 다른 거 친 것 같아
	{
		for (TActorIterator<ACXPlayerController> It(GetWorld()); It; ++It)
		{
			ACXPlayerController* CXPlayerController = *It;  // CXPC로 해야할듯?
			if (IsValid(CXPlayerController) == true)
			{
			  // CXPC로 해야할듯?
				CXPlayerController->ClientRPCPrintChatMessageString(InChatMessageString);  // 클라한테 ChatMessage 그대로 넣어줌
			}
		}
	}
}

(AController* NewPlayer)

// 로그인 후에 호출되는 함수
void ACXGameModeBase::OnPostLogin(AController* NewPlayer)  // 컨트롤러 넣어줌
{
}

인자로 NewPlayer → 접속한 플레이어의 AController

ACXPlayerController* CXPlayerController = Cast(NewPlayer);

  • NewPlayer는 기본적으로 AController* 타입
  • 여기에서는 커스텀 컨트롤러(ACXPlayerController)를 쓰고 있으니까 캐스팅
  • 성공 → 이제 커스텀 기능 사용 가능 (PrintChatMessageString 같은 함수)

AllPlayerControllers.Add(CXPlayerController);

  • AllPlayerControllers = 아마 TArray 타입의 멤버 변수
  • 지금 로그인한 플레이어 컨트롤러를 배열에 넣음
  • 현재 게임에 접속한 모든 PlayerController들의 리스트를 관리함

GameMode는 서버 전용 액터니까 클라이언트에서 서버 RPC로 호출하기

  • 클라이언트가 뭔가 “판정”이나 “정답 비교” 같은 로직을 GameMode에 시키고 싶으면,
  • 클라 → 서버 RPC 호출 → 서버(GameMode)에서 처리 → 서버 → 다시 클라에 결과를 브로드캐스트로 전달

InChatMessageString.Len() - 3

  • InChatMessageString.Len() → 입력 문자열 전체 길이 구하기
  • 3 → 마지막 3글자의 시작 인덱스를 구하려는 것
  • "문자열 끝에서 3글자 전의 위치"를 Index에 저장

InChatMessageString.RightChop(Index);

  • 문자열에서 앞부분을 잘라내고, 뒤쪽 나머지를 가져옴

2개의 댓글

comment-user-thumbnail
2025년 9월 15일

FMath에 대한 고찰,
풀어내는 여유로움,
흠 잡을 곳이 없는 칠칠맞은 글이였습니다.

1개의 답글