이번에 구현할것은 언리얼에서 멀티플레이 채팅 야구게임을 구현할 것이다.
그리고 스스로 판단하기에 C++로 멀티플레이 채팅을 구현하는 과정은 검색이나 GPT나 Grok등의 AI를 이용하면 간단히 구현할 수 있기때문에 스스로 부족하다고 느꼈던 블루프린트로만 구현해보았다.
이미 프로젝트는 완성된 상태에서 설명하는 방식으로 진행하겠다.
우선 채팅 야구게임에서의 핵심은 멀티플레이라는 것으로 플레이어가 2명인 리슨서버환경에서 채팅시스템을 넣어 채팅으로 플레이어2명이서 턴제 방식에 야구게임을 할 수 있는 게임을 만들어 보았다.
우선 이전에 썻던 글을 참조하면 이번 글을 이해하기 쉬워질 것이다.
https://parkcy723.tistory.com/90
UE5 Blueprint로 멀티플레이 채팅 구현
이번글을 아래에 있는 유튜브 내용을 토대로 구현하였으며 조금에 기능을 더 추가하였다.https://youtu.be/PcfJiD6RTmo?si=radeiyWu9IBLW3nS Widget Blueprint생성 이름은 WBP_ChatPanel 1. • On Text Committed (Play
parkcy723.tistory.com
https://parkcy723.tistory.com/94
RPC
✅ RPC 옵션 요약 (그림 기준)옵션의미실행 주체용도리플리케이트되지 않음그냥 로컬 함수아무 네트워크 전파 없음UI 로직, 비주얼 등서버에서 실행 (Run on Server)클라이언트 → 서버로 호출서버에
parkcy723.tistory.com
야구 게임을 구현해야하는데있어서 우선 야구게임이 무엇인지 설명부터하겠다.
야구게임 규칙
1. 랜덤으로 3자리 숫자가 생성됩니다.
- 1 ~ 9 까지의 숫자중 중복되지 않는 랜덤한 숫자 3개 -
2. 상대방과 차례대로 3자리 숫자를 입력합니다.
입력을 하면 변하는 Strike, Ball, Out, Count
• Strike = 같은 자리에 숫자를 입력하면 같은 자리만큼 Strike가 증가
• Ball = 자리는 다르지만 같은 숫자가 있는만큼 Ball이 증가
• Out = 입력한 값 중에 같은 숫자가 없다면 Out이 1씩 증가
• Count = 모든 플레이어는 6번에 기회를가지면 한번 입력할때마다 1씩 감소
3. Strike와 Ball은 매번 0으로 초기화되지만 Out은 초기화되지 않습니다.
승 / 패 조건
• Strike가 3이 되면 승리
• Out이 3이 되면 패배
• Count가 0이되면 패배
위와같이 채팅 야구게임은 1 ~ 9까지 숫자중 중복되지않는 랜덤 3자리 숫자가 있을때 플레이어가 채팅으로 3자리 숫자를 입력하였을 때 같은 위치에 숫자를 맞추면 같은 자리를 입력한 만큼 Strike가 증가되고, 자리는 다르지만 입력한 숫자가 랜덤숫자에 있기만하면 있는 만큼 Ball이 증가되며, 만약 입력한 숫자가 랜덤숫자중에 없으면 Out이 1씩 증가되는 원리에 게임으로 Strike가 3자리 숫자와 모두 일치하면 승리하고, Out이 3이되면 패배하는 원리이다.
그리고 여기에 추가로 입력할 수 있는 기회인 Count를 넣어서 각 플레이어는 6번이라는 기회를 가지게 되고, 이 Count가 0이되면 패배하게 된다.
그럼 이제 이 채팅 게임을 어떻게 구현하였는지 차근차근 설명하겠다.
랜덤 숫자 생성
GameMode = BP_GM_Chat
1. 블루프린트안에서 Custom 이벤트로 StartBaseballGame을 생성 (리플리케이트는 하지않음.)
2. 1 ~ 9까지의 배열을 가진 integer변수인 AvaliableNumbers를 생성하고, Shuffle로 AvaliableNumbers 배열안에있는 요소들을 섞어준다.
3. 그런다음 Get(a copy)노드를 사용하여 0번째 배열에 있는 값을 First라는 integer변수에 저장.
4. 그런다음 RemoveIndex노드를 사용하여 AvaliableNumbers배열에 있는 0번째 요소 삭제
5. 다시 AvaliableNumbers배열에 있는 0번째 요소를 Get(a copy)노드로 들고와서 Second라는 integer변수에 저장
6. RemoveIndex노드로 다시 AvaliableNumbers배열에 있는 0번째 요소 삭제
7. 다시 AvaliableNumbers배열에 있는 0번째 요소를 Get(a copy)노드로 들고와서 Third라는 integer변수에 저장
8. First, Second, Third변수에 들어있는 값을 Append노드를 사용하여 SecretNumber라는 String변수에 순서대로 저장.
이렇게 SecretNumbers라는 string변수에 랜덤한 3자리 숫자가 들어가있게되었다.
그럼 이걸 이제 사용해야하는데 어디서 사용하냐면 GameState에서 사용하게 된다.
Game State = BP_GM_Chat
GameState에서 BeginPlay이벤트에서 GameMode를 캐스팅하여 게임모드에서 만든 StartBaseballGame을 실행하여 게임모드에 저장된 SecretNumber를 GameState에있는 SecretNumber에 저장해주자.
이렇게하는 이유는 GameMode는 서버이기때문에 GameMode에서 만든 SecretNumber를 다른곳에서 막 가져다가 쓰게되버리면 지금 구현한 리슨서버같은 경우 서버와 클라이언트를 가진 플레이어만 SecretNumber를 사용할 수 있게 되기때문에 굳이 귀찮더라도 GameState에서 GameMode에있는 SecretNumber를 들고와서 한번 더 저장해 주자.
다 만들고 생각해보니 GameMode에서 랜덤3자리숫자를 만들지 말고 처음부터 GameState에서 구현했으면 좋았던 것 같다.
그럼 이제 GameState에있는 SecretNumber에 들어있는 값을 어디서 사용하냐면 Widget BluePrint안에서 사용하게 된다.
Widget BluePrint = WBP_ChatPanel
위젯 블루프린트안에 있는 내용을 다 작성할 순 없어서 쉽게 설명하면
1. 플레이를 누르면 아래와같은 화면이 나타날때 게임 시작 버튼을 누를 수 있다.
2. 게임 시작 버튼을 누르면
이름을 입력할 수 있게 된다.
플레이어가 닉네임을 입력하면 난수를 생성되도록 하는게 좋은 것 같아서 "닉네임 입력칸" 으로 보이는 Text Box를 변수로 만들어주고 이벤트로 텍스트 커밋 시를 체크하여 On Text Committed을 만들어 준다.
On Text Committed이란
텍스트가 커밋될 때 호출하는 이벤트로 지금과 같이 플레이어가 닉네임을 적고 엔터를 누르면 한번만 호출되니 이때 GameState에있는 SecretNumber를 들고오면 된다.
위에 사진처럼 중간에 다른 과정이 조금 많긴 하지만 실제로는 잘 작동하게 되며 확인을 위해 디버그용으로 print string을 해보면
닉네임을 입력하면 랜덤3자리수가 생성되는 것을 확인 할 수 있다.
그럼 이제 플레이어가 3자리 숫자를 입력할 수 있어야하고, 그 숫자와 SecretNumber을 비교하여 Strike, Ball, Out을 판단하고, 채팅을 칠때마다 Count가 1씩 감소하게 해줘야한다.
지난번에 작성한 블로그 내용에서 플레이어가 채팅을 치는 건 구현했으니 거기서 구현하겠다.
해야할 작업은 대략적으로 아래와 같다.
1. 3자리 숫자까지만 입력
2. 플레이어가 3자리 숫자를 입력한 값을 저장
3. 플레이어가 입력한 3자리 숫자와 SecretNumber를 비교
4. Strike, Ball, Out을 판단
그럼 우선 플레이어가 입력한 3자리 숫자 값을 저장하는 것부터 하면
플레이어가 채팅을 치는 Text Box를 변수로 만들어 준다.
텍스트 박스 를 선택하고 변수 여부에 체크해주자.
그리고 텍스트 변경 시와 텍스트 커밋 시를 클릭하여 이벤트를 만들어 주자.
텍스트 변경 시는 텍스트 박스안에 플레이어가 입력할때마다 실행이다.
텍스트 커밋 시는 플레이어가 텍스트 박스안에있는 글자를 Enter를 누르거나 텍스트 상자에 포커스를 잃어버리면 실행된는 것이다.
그럼 플레이어가 3자리 숫자까지만 입력가능하게하는건 텍스트 변경 시 이벤트에서 구현해주고, 나머지는 전부 텍스트 커밋 시 이벤트에서 구현해주면 된다.
1. 3자리 숫자까지만 입력
텍스트 박수안에 Mid 노드를 추가하여 0부터 3개를 카운트하니 [0, 1, 2]까지 카운트가 될 것이고 플레이어가 3자리까지만 입력가능하게 된다. 그 값을 TextBox를 타겟(ChatInputBox라고 변수명을 변경했었다.)으로하여 텍스트박스에서는 3자리만 입력되고 더이상 입력이 안되게 된다.
2. 플레이어가 3자리 숫자를 입력한 값을 저장
그럼 이렇게 Equal (Enum)노드를 추가해서 플레이어가 엔터를 눌렀을 때만 작동되게하였고, 플레이어가 입력한 텍스트를 Player Input이라는 string변수에 넣어주었다. 그럼 플레이어가 입력한 값이 Player Input변수안에 저장되어진다.
3. 플레이어가 입력한 Player Input에 3자리 숫자와 SecretNumber를 비교
PlayerInput과 SecretNumber를 각 배열 요소에 위치를 Get Substring노드를 사용하여 비교해 준다.
이건 첫번째 비교인데 Player Input과 SecretNumber에 첫번째 배열에 있는 요소들에 값을 비교해 줄 수 있게되었다. 이러한 원리로 PlayerInput에 모든 요소를 Get Substring노드로 들고오고 SecretNumber에 모든 요소도 Get Substring노드로 들고와서 비교해 줄 수 있게 되었다.
이러한 원리를 통해 PlayerInput과 SecretNumber에 모든 경우에수를 계산해 줄 수 있게 된다.
4. Strike, Ball, Out을 판단
PlayerState = BP_PS_Chat
기본적으로 플레이어에 Strike와 Ball, Out, Count변수는 PlayerState블루프린트에서 관리하므로 PlayerState를 캐스팅하여 각 변수들을 들고와서 더해주었다.
BP_PS_Chat에 Strike, Ball, Out, Count변수
초기값
Strike = 0
Ball = 0
Out = 0
Count = 6
WBP_ChatPanel에서에 Strike, Ball, Out 판단 로직
Strike판단 로직
Ball판단 로직
Out 판단 로직
각 Branch노드는 위에서 PlayerInput과 SecretNumber에 각 요소들에 경우에수를 넣어주었다.
Stirke는 1개 참이면 1이 증가될거고 2개가 참이면 2가 증가되는 방식으로 Ball도 똑같은 원리로 설계됐다.
Out은 PlayerInput에 들어있는 수가 SecretNumber에 하나도 없다면 1이 증가되는 방식이다.
여기서 WBP_ChatPanel에 integer타입에 copy strike와 copy out변수를 만들어주어 PlayerState에 있는 strike값과 out을 연동해준다. 이건 차후에 게임 종료 로직을 위해서이다.
이렇게 하면 기본적으로 야구게임에 운영체계를 다 구현해 주었는데 이제 턴제 방식을 추가하려고한다.
턴제 방식에 구현은 BP_PC_Chat에서 구현하였다.
우선 그전에 리슨서버에서의 클라이언트를 쉽게 설명하기위해서 서버를 가진 클라이언트는 "서버클"이라고 부르고,
클라이언트만 가지고있는 클라이언트는 "노멀클"이라고 부르겠다.
서버클과 노멀클은 둘다 GameState에 IsMyTurn이라는 bool변수를 가지고있다. IsMyTurn에 기본값은 True인 상태인데 IsMyTurn이 True면 서버클이 채팅이 쳐지게 할 것이고 False상태라면 노멀클이 채팅이 쳐지게 할 것이다.
그럼 채팅을 칠때마다 IsMyTurn에 값을 계속 반전시켜주면 턴제방식이 해결된다.
그럼 첫번째로 IsMyTurn을 확인하고 True면 서버클에서 채팅이 쳐지게하는것과 False면 노멀클에서 채팅이 쳐지게 하는것부터 구현하겠다.
BP_PC_Chat에서 IsPlayed함수 생성
Switch Has Authority에서 Authority는 서버클을 관리하고, Remote는 노멀클을 관리하게된다.
Authority일때
1. GameState에 IsMyTurn변수를 Get으로 가져온다. 기본 값인 True가 들고 와졌을 것이다.
2. XOR Boolean으로 두개의 값을 확인하는데 True와 True가 되고 XOR Boolean은 두개의 값이 같으면 False를 반환한다.
3. 그럼 False라는 값이 NOT Boolean을 만나 True로 바뀌게 된다.
4. 로컬 변수1을 만들어 NOT Boolean에 True값을 넣어준다.
5. Change Value를 실행한다. (Change Value는 밑에서 설명)
6. Return노드에 출력핀을 추가하고 타입을 Boolean으로 정한다음 로컬 변수1에 값을 Get으로 가져와 넣어준다.
Remote일때
1. GameState에 IsMyTurn변수를 Get으로 가져온다. 기본 값인 True가 들고 와졌을 것이다.
2. XOR Boolean으로 두개의 값을 확인하는데 True와 False가 되고 XOR Boolean은 두개의 값이 다르면 True를 반환한다.
3. 그럼 True라는 값이 NOT Boolean을 만나 False로 바뀌게 된다.
4. 로컬 변수2를 만들어 NOT Boolean에 False값을 넣어준다.
5. Change Value를 실행한다. (Change Value는 밑에서 설명)
6. Return노드에 출력핀을 추가하고 타입을 Boolean으로 정한다음 로컬 변수2에 값을 Get으로 가져와 넣어준다.
BP_PC_Chat에서 ChangeValue 이벤트 생성
Custom Event를 생성하고 리플리케이트는 서버에서 실행으로 바꿔준다음 입력핀을 추가하고 Boolean타입을 넣어준다.
ChangeValue에서 입력받은 Boolean타입 값을 가져와서 Branch로 확인한다음 True면 GameState에 있는 IsMyTun에 값을 NOT Boolean으로 반전시켜준다음 그값을 GameState에 IsMyTurn변수에 저장해준다.
쉽게 설명하면 ChangeValue에 입력받은 값이 True라면 리플리케이트인 서버에서 실행을 통해 서버클과 노멀클에GameState에 있는 IsMyTurn이 모두 반대로 바뀐다는 뜻이된다.
그럼 이제 이 과정이 어떻게 이루어지는지 순서대로 설명하겠다.
IsPlayed함수가 실행되면 서버클과 노멀클에서 각각에 GameState에 IsMyTurn 변수를 확인하고
IsMyTurn = True면 XOR Boolean과 NOT Boolean을 통해 서버클에서의 로컬변수1값이 True가 되고,
IsMyTurn = False면 XOR Boolean과 NOT Boolean을 통해 노멀클에서의 로컬변수2값이 True가 된다.
ChangeValue를 거치게되는데 로컬변수 1과 2에있는 값을 확인해본다.
처음에 GameState에 있는 IsMyTurn에 기본 값이 True였기때문에 IsPlayed에있는 Authority핀에 연결된 로컬변수1에 값이 True인 상태이고 그럼 Authority에서에 Change Value가 실행되며 입력값을 확인해보고 True가 맞으니 GameState에있는 IsMyTurn에 값을 NOT Boolean으로 반전시켜주게 될 것이다.
반대로 IsPlayed에있는 Remote핀은 값이 False가 나왔기 때문에 Change Value가 실행되지 않을 것이다.
그럼 Authority와 Remote에서에 Return Node값에 출력핀에 각각 로컬변수1과 2를 넣어준다.
그럼 이제 서버클과 노멀클에는 각각 True와 False라는 값이 들어가있을 것이다.
그럼 이제 WBP_ChantPanel에서 서버클과 노멀클에서 채팅을 쳤을때 BP_PC_Chat에있는 IsPlayed를 실행해주고 값이 True가 나왔을때만 채팅이 쳐지게 하면 된다.
그럼 서버클에서 채팅을 치면 True값이 리턴값으로 나오며 채팅이 가능하고 IsPlayed에서 ChangeValue를 통해 서버클과 노멀클에 IsMyTurn변수가 반대로 바뀌게된다.
그럼 다시한번 서버클에서 채팅을 칠려고하면 서버클에 리턴값이 False로 바뀌고, 노멀클에 리턴값이 True로바뀌며 서버클에서는 채팅을 칠 수 없게되고, 노멀클에서는 채팅을 칠 수 있는 상황이 된다.
이렇게 서버클과 노멀클이 턴제형식으로 채팅을 칠 수 있게 된다.
'Unreal Engine 5 > UE5 멀티채팅 야구 게임' 카테고리의 다른 글
UE5 멀티플레이 채팅 야구게임만들기(2) (0) | 2025.03.27 |
---|---|
UE5 Blueprint로 멀티플레이 채팅 구현 (0) | 2025.03.16 |