1. 기본 로직
1 - 1. 게임 시작 시
- 레벨 BP
- UI를 띄워주고,
- UserID를 서버라면 HOST로 클라이언트라면 GUEST로 세팅해준다.
- PlayerController
- BeginPlay에서 아래와 같이 Widget의 이벤트 디스패처와 PlayerController를 Binding 해준다.
- GameMode
- BeginPlay에서 아래와 같이 게임에 필요한 값을 초기화 한다.
- 난수 생성
- turn 과 count 초기화
- 타이머 초기화
- BeginPlay에서 아래와 같이 게임에 필요한 값을 초기화 한다.
1 - 2. 숫자 입력 받기
숫자 입력
- UI에 /와 숫자를 입력한 다음 enter를 누르게 되면, UI의 SetMessagetoUserController가 call되는데 이 이벤트 디스패처는 PlayerController에 OnSendMessagetoServer와 바인딩 되어있다.
입력 처리
- UI 함수와 바인딩 된 이벤트는 아래와 같이 생겼고, /라는 문자를 잘라준 다음 GameMode의 GotMessageFromClient 이벤트를 call해서 로직 처리를 하게 된다.
1 - 3. 입력 받은 숫자 처리
입력받은 숫자는 모두 서버에서 처리를 해주고, 그 결과값을 다시 보내서 UI로 출력할 수 있도록 구현하였다
GameMode
- GameMode의 GotMessagefromClient 이벤트는 "서버에서 실행"으로 동작한다.
- 가장 먼저 현재 turn 과 userID를 체크하게 된다.
- 이때 return 값이 1이면 HOST, 2면 GUEST가 된다.
- 이 함수의 return 값이 1또는 2인 경우만 유효한 경우이기 때문에 다음 처리를 진행한다.
- 유효하지 않은 경우의 예시는 GUEST turn 일때 HOST가 입력하는 등의 경우를 말한다.
- Check Turn and ID 함수 코드
- 로직을 설명하자면, TurnIndex는 매 턴마다 1씩 증가하게 되고, 1부터 시작하기 때문에 홀수인 경우는 HOST 짝수인 경우는 GUEST로 판정하게 된다.
- 또한 인풋으로 받는 Message는 컨트롤러에서 가져온 UserID이다.
더보기
int32 ABaseBallGameMode::CheckTurnAndID(FString Message)
{
//odd number and "HOST"
if (TurnIndex % 2 != 0 && Message.Equals(TEXT("HOST")))
{
TurnIndex++;
HostCount--;
return 1;
}
//even number and "GUEST"
if (TurnIndex % 2 == 0 && Message.Equals(TEXT("GUEST")))
{
TurnIndex++;
GuestCount--;
return 2;
}
return 3;
}
- 이후 유효한 input인 경우 CheckResult 함수를 통해 스트라이크와 볼 판정을 해준다.
- CheckResult 코드
- 유효하지 않은 input의 경우 OUT을 리턴
- 정답인 경우 CLEAR 리턴
- 나머지 경우 스트라이크와 볼 갯수를 세서 리턴해준다.
더보기
FString ABaseBallGameMode::GetResult(FString Message)
{
//false
if (!ValidCheck(Message))
return FString(TEXT("OUT"));
int32 Res = FCString::Atoi(*Message);
TArray<int32> Temp; //input value
int32 Strike = 0;
int32 Ball = 0;
Temp.Add(Res / 100);
Res -= (Res / 100) * 100;
Temp.Add(Res / 10);
Res -= (Res / 10) * 10;
Temp.Add(Res);
for (int i = 0; i < Temp.Num(); ++i)
{
//if find
if (ResultArray.Find(Temp[i]))
{
if (ResultArray[Temp[i]] == i)
Strike++;
else
Ball++;
}
}
if (Strike == 3)
{
return FString("CLEAR");
}
return FString::Printf(TEXT("%d S %d B"), Strike, Ball);
}
- 결과를 가지고 Set Result Text 함수를 통해 화면에 출력해주게 된다.
- Input은 우리가 입력한 숫자를 말하고
- Result는 판정 결과를 말한다.
- 이 이벤트는 UI와 연동되는 함수이다. UI 로직은 아래에서 자세히 설명할 것이다.
- 이후 결과를 통해 승 패 결과를 WinCheck 함수를 통해 진행한다.
- 이 함수는 간단하게 매개변수 Message가 CLEAR 인지 확인하는 함수이다.
1 - 4. 승패 처리
이긴 경우
- 바로 위에서 본 WinCheck함수가 true 라면 SetWinTieText라는 이벤트를 통해 화면 중앙에 Win 이라는 문구를 출력해주게 된다.
- 이후 Check Turn and ID 함수를 통해 가져온 Turn 변수를 통해
- 1이면 Host이므로 Host의 win count 를 증가
- 2면 Guest 이므로 Guest의 win count를 증가시킨다.
- 마지막으로 Win Game 함수를 통해서 UI와 값들을 리셋해주면 된다.
비긴 경우
- Win Check가 false이고 Guest의 count가 0인 경우는 비긴 경우가 된다.
- 아래 사진과 같이 비겼다는 것을 알려주는 UI 출력을 위해 Set Win Tie Text 함수를 호출 후
- Tie Game 함수를 통해 UI및 값을 리셋해주면 된다.
이기거나 비기지 않은 일반적인 경우
- UI와 타이머 등을 업데이트 해주면 된다.
2. UI 로직
이게 맞는 방식인지는 잘 모르겠다.....
내가 만든 UI 로직 순서
- UserWidget BP에서 해당 UI를 세팅해주는 함수를 만든다.
- PlayerController에서 해당 UI 이벤트를 call하는 이벤트를 만든다.
- 이때 이 이벤트는 "소유중인 클라이언트에서 실행" 으로 설정한다.
- GameMode에서 PlayerController의 'UI이벤트를 call하는 함수"를 call하는 함수를 만든다.
- 이 GameMode에 존재하는 함수는 우리가 짜는 로직에서 사용하게 된다.
동작 순서
- 동작하는 방식은 함수 만드는 순서의 반대로 동작하면 된다.
- GameMode(서버) 의 로직에서 GameMode에 존재하는 이벤트를 call한다.
- GameMode의 이벤트는 PlayerController의 "소유중인 클라이언트에서 실행"인 함수를 call한다.
- 최종적으로 PlayerController의 함수는 UI의 함수를 call 한다.
3. 타이머 로직
아직 BP에 익숙하지 않아서 타이머 로직을 C++로 만들고 BP에서 사용하도록 구성했다.
- BeginPlay에서 Timer를 set한다. (GameMode)
- 참고) StartTimer 함수
더보기
void ABaseBallGameMode::StartTimer()
{
GetWorldTimerManager().SetTimer(Timer, this, &ABaseBallGameMode::EndTimer, 10.f, false);
}
- 이후 Tick을 통해 아래와 같이 현재 Timer의 값을 가져오는 함수와 UI를 업데이트 해주는 이벤트인 SetTimerText를 call한다.
- 참고) GetRemainTime 함수
더보기
void ABaseBallGameMode::GetRemainTime()
{
RemainingTime = GetWorldTimerManager().GetTimerRemaining(Timer);
}
- 만약 Turn이 끝나서 다음 Turn으로 넘어가게 된다면(아래의 경우들) Timer를 clear해주는 함수를 call해주면 된다.
- 제대로 입력이 끝나서 Turn이 넘어가거나
- 한명이 승리하거나
- 비기거나
- 참고) Clear Timer 함수
- Clear Timer는 이후 바로 다시 타이머를 시작하면 되기 때문에
- StartTimer 함수를 call하면 된다.
더보기
void ABaseBallGameMode::ClearTimer()
{
GetWorldTimerManager().ClearTimer(Timer);
StartTimer();
}
- 타이머가 끝난 경우
- StartTimer함수가 10초가 지나면 EndTimer라는 함수를 call해서 값들을 리셋해주는 작업이 필요하다.
- TurnIndex를 증가시켜주고, 현재 turn에 맞는 Count를 감소시키면 된다.
- 이후 Timer를 리셋하고, ResetValues라는 함수를 call한다.
- ResetValues 함수는 UI 나 변수를 리셋해주는 역할을 한다.
- 이 함수는 C++ 함수인데, BP의 함수를 불러와서 써야하는 상황이다!
- 아래의 BlueprintImplementableEvent 로 해결했다.
더보기
void ABaseBallGameMode::EndTimer()
{
TurnIndex++;
if (TurnIndex % 2 == 0)
HostCount--;
else
GuestCount--;
ClearTimer();
StartTimer();
ResetValues();
}
트러블슈팅
우리가 가진 로직은 BP에 존재하는데, Timer함수 관련 로직은 C++로 만들어서 C++로직 안에 BP에서 만든 이벤트를 호출하고 싶은 상황이다.
해결) C++에서 선언하고 BP에서 정의하는 함수
- 우리가 사용하는 기본적인 로직은 BP로 동작하는데, 타이머 로직은 c++로 구성하게 되었다.
- 이때 EndTimer안에서 BP의 로직을 넣어주고 싶은데, 어떤 방법이 있을지 찾아보다가 BlueprintImplementableEvent 지정자를 알게 되었다.
- 이 방식은 C++에서 함수 선언만 하고, 그 구현은 BP에서 하도록 하는 지정자이다.
함수 선언 (C++)
UFUNCTION(BlueprintImplementableEvent, Category = "MyEvents")
void ResetValues();
함수 정의 (BP)
4. 결과
https://youtu.be/ePo2tyuhsZM?si=GPG16644QaRRqRCe
'Unreal Engine' 카테고리의 다른 글
Unreal Engine - 데디케이티드 서버 개념 및 실습 (0) | 2025.03.27 |
---|---|
Unreal Engine - 플러그인 만들기 (0) | 2025.03.26 |
Unreal Engine - Standalone을 리슨 서버로 확장하기 (0) | 2025.03.24 |
Unreal Engine - Gameplay Framework (0) | 2025.03.24 |
Unreal Engine - UObject (0) | 2025.03.24 |