RPG 게임에서 보면 필수적으로 있는 것중 하나인 유저들간의 대화이다.
대부분의 경우 채팅을 통해 의사소통을 하곤한다.
따라서, 채팅을 한번 구현해보자.
채팅의 경우 서버의 역할이 따지고보면 크게 없다 그냥 어떤 플레이어가 어떤 말을 했는지 브로드캐스트 해주는 것이기 때문이다. 물론 전체채팅이나 유저가 매우 많을 경우 최적화가 반드시 필요할 것이다. 이유는 플레이어의 움직임 동기화랑 같은 맥락이다.
따라서 필자는 주변에 있는 플레이어들만 대화할 수 있게 구성한다.
채팅 UI
검정 배경에 채팅들이 쌓일 것이며 전송을 통해 내가 친 채팅을 보낼 수 있다.
물론 InputField에 포커싱에 되어있을 경우엔 엔터를 통해서 포커싱할 수 있으며, 포커싱이 되어 있는 중에는 플레이어의 키 입력을 전부 막는다.(인벤 열기 등등)
그리고 모두에게 라고 적힌 버튼은 구현하진 않았지만 추후 친구에게 등으로 바꿀 수 있게 하기 위한 버튼이다.
전송 버튼 옆에 위아래 화살표는 채팅창을 접었다 폈다 할 수 있는 버튼이다. 이것이 없다면 화면을 너무 가려 보기 불편할 것이다.
채팅 처리
메시지가 서버로부터 왔을 경우 메시지를 하나의 Queue에 담는다. Queue에 담긴 size가 저 채팅창의 크기를 벗어났을 경우 하나씩 Dequeue하는 방식으로 구현할 것이다.
그러면 Queue에 담긴 메시지들이 채팅창의 size를 넘겼는지 어떻게 알 수 있을까
채팅창의 크기랑 똑같은 TestUI를 유저에게 보이지 않는 화면에 만들어 두고 먼저 그 TestUI에 Text를 입력한다.
그리고 textInfo에서 lineCount를 통해 몇줄인지 세고 그것이 maxLine를 넘는지 확인하는 과정을 거치는 것이다.
이때 ForceUpdateCanvases를 하지 않으면 바로 업데이트가 되지 않는 Canvas 특성상 제대로 작동하지 않을 수 있다.
public void AddChatMessage(string message)
{
backupChatLines.Enqueue(message);
GetText((int)Texts.ChatTestText).text = string.Join("\n", backupChatLines);
Canvas.ForceUpdateCanvases();
while (GetText((int)Texts.ChatTestText).textInfo.lineCount > maxLines)
{
backupChatLines.Dequeue();
GetText((int)Texts.ChatTestText).text = string.Join("\n", backupChatLines);
Canvas.ForceUpdateCanvases();
}
if (!isFull)
{
chatLines.Clear();
chatLines.Enqueue(message);
}
else
{
chatLines = new Queue<string>(backupChatLines);
}
lastchat = message;
GetText((int)Texts.ChatText).text = string.Join("\n", chatLines);
}
채팅창 사이즈 변경
위에서 말한대로 화살표를 누르면 사이즈가 변경되게 만들 것이다.
string lastchat = "";
public void ChangeChatSize()
{
if (isFull)
{
isFull = false;
string lastMessage = lastchat;
chatLines.Clear();
chatLines.Enqueue(lastMessage);
GetText((int)Texts.ChatText).text = "";
GetObject((int)GameObjects.ChatBackground).transform.DOLocalMoveY(-105f, 0.3f).SetEase(Ease.OutExpo);
GetObject((int)GameObjects.ChatBackground).GetComponent<RectTransform>().DOSizeDelta(new Vector2(600, 80), 0.3f).SetEase(Ease.OutExpo);
GetText((int)Texts.ChatText).GetComponent<RectTransform>().DOSizeDelta(new Vector2(580, 30), 0.3f).SetEase(Ease.OutExpo).OnComplete(
() => {
GetText((int)Texts.ChatText).overflowMode = TextOverflowModes.Ellipsis;
GetText((int)Texts.ChatText).text = lastMessage;
});
}
else
{
isFull = true;
chatLines = new Queue<string>(backupChatLines);
GetText((int)Texts.ChatText).text = "";
GetObject((int)GameObjects.ChatBackground).transform.DOLocalMoveY(0, 0.3f).SetEase(Ease.OutExpo);
GetObject((int)GameObjects.ChatBackground).GetComponent<RectTransform>().DOSizeDelta(new Vector2(600, 300), 0.3f).SetEase(Ease.OutExpo);
GetText((int)Texts.ChatText).GetComponent<RectTransform>().DOSizeDelta(new Vector2(580, 240), 0.3f).SetEase(Ease.OutExpo).OnComplete(
() => {
GetText((int)Texts.ChatText).overflowMode = TextOverflowModes.Overflow;
GetText((int)Texts.ChatTestText).overflowMode = TextOverflowModes.Overflow;
GetText((int)Texts.ChatText).text = string.Join("\n", chatLines);
});
}
}
Dotween을 이용해 부드러운 연출을 진행했다. 채팅창이 줄여졌을 경우에는 한줄씩 출력하게 되는데 그때, 마지막인 채팅을 기억해서 출력하는 형식이다.
채팅 보내기
public void OnEnterChat()
{
string chatting = ChatField.text;
if (chatting.Equals(""))
return;
ChatField.text = "";
C_Chatting chattingPacekt = new C_Chatting();
chattingPacekt.Content = chatting;
Managers.Network.Send(chattingPacekt);
}
채팅을 치고 엔터키를 누르거나 전송 버튼을 누르면 작동하는 함수다. 말 그대로 서버에 내가 이러한 채팅을 쳤다고 알려주는 역할이다.
채팅 말풍선
유저가 말하는 채팅이 UI에 보여지는 것도 좋지만 말풍선이 있으면 더 좋다. 그렇기 때문에 각 유저마다 하나의 채팅창을 만들어두는 것이 좋다.
백그라운드 말풍선 이미지에 다음과 같이 추가하면 채팅 사이즈에 따라 상하좌우로 늘어나게 된다.
이렇게 하면 코드의 처리 없이 말풍선의 사이즈를 조절할 수 있다.
이때 주의해야하는 점은 Image의 type을 sliced 로 해야한다는 점이다.
서버로 부터 채팅이 왔을경우 밑에와 같이 동작하는데 MarkOutChat을 통해 말풍선을 만들어준다.
그 밑은 채팅창을 Update 해주는 모습이다.
public static void S_ChattingHandler(PacketSession session, IMessage packet)
{
S_Chatting chatPacekt = (S_Chatting)packet;
GameObject go = Managers.Object.FindById(chatPacekt.ObjectId);
PlayerController pc = go.GetComponent<PlayerController>();
if (pc == null) return;
pc.MarkOutChat(chatPacekt.Content);
UI_GameScene gameSceneUI = Managers.UI.SceneUI as UI_GameScene;
if (gameSceneUI != null)
{
gameSceneUI.AddChatMessage($"{pc.objectInfo.Name} : {chatPacekt.Content}");
}
}
public void MarkOutChat(string chat)
{
chat = InsertLineBreaks(chat, 15);
TextObject.SetActive(true);
ChatText.text = chat;
if (Chatting != null)
StopCoroutine(Chatting);
Chatting = StartCoroutine(DestroyChat());
}
private string InsertLineBreaks(string text, int lineLength)
{
for (int i = lineLength; i < text.Length; i += lineLength + 1)
{
text = text.Insert(i, "\n");
}
return text;
}
IEnumerator DestroyChat()
{
yield return new WaitForSeconds(3f);
TextObject.SetActive(false);
}
자연스럽게 보이기 위해 15글자마다 \n을 추가해서 말풍선을 처리한다.
결과
'Unity > 온라인 RPG' 카테고리의 다른 글
[Unity 3D] 3D RPG 게임 테스트 플레이 영상 (0) | 2024.08.21 |
---|---|
[Unity 3D] 퀘스트 DB에 저장하기 (0) | 2024.08.14 |
[Unity 3D] 퀘스트 시스템 구현하기 (1) | 2024.08.06 |
[Unity 3D] 각종 사운드 추가 (0) | 2024.07.29 |
[Unity 3D] 보스 드래곤의 패턴 만들기 (0) | 2024.07.29 |