유니티 RPG - 59. 멀티플레이모드 구현 - 채팅 시스템 구현
Intro
이번 포스팅에서는 채팅 시스템을 구현해보았습니다.
-
멀티씬 입장시 채팅활성화
-
엔터키입력으로 채팅 입력모드 ON/OFF
-
새로운 채팅을 포커싱(스크롤가능)
GUI
채팅창 UI의 전체 구성입니다.
-
스크롤뷰를 사용하여 위아래로 스크롤이 가능한 형태의 채팅창을 만들었습니다.
- InputField에 입력된 채팅을 Content에 표시하게됩니다.
- Content 오브젝트에는 Virtical Layout Group, Content Size Fitter 컴포넌트를 추가하였습니다.
- ChatLog 프리팹을 생성하여 플레이어가 채팅을 입력할때마다 채팅창에 아래방향으로 쌓아가는 형태로 구현하였습니다
- ChatLog 프리팹은 Text UI입니다.
구현
ChatManager 클래스는 채팅시스템을 담당하는 클래스입니다.
public class ChatManager : MonoBehaviourPunCallbacks
{
[SerializeField] private TMP_InputField inputField; // 채팅 입력란
[SerializeField] private ScrollRect scrollRect; // 채팅 스크롤뷰
[SerializeField] private GameObject chatLogPrefab; // 채팅로그 프리팹
[SerializeField] private Transform contentTransform; // 채팅로그가 생성될 위치
private Queue<GameObject> messageQueue = new();
private int maxMessages = 10; // 최대 10개 메세지 표시
private bool isInputActive = false; // 입력모드인지 여부
private void Start()
{
// 플레이어 입장 메세지
string temp = $"<color=green>{PhotonNetwork.NickName} Joined the chat room.</color>";
photonView.RPC("ReceiveMessage",RpcTarget.All, temp);
// 엔터키 입력
inputField.onSubmit.AddListener(_ => SendChatMessage());
inputField.DeactivateInputField();
}
private void Update()
{
if(Input.GetKeyDown(KeyCode.Return))
{
// 입력모드가 아닐 경우 -> 입력시작
if(!isInputActive)
{
isInputActive = true;
GameManager.Instance.isChatting = true;
inputField.ActivateInputField();
}
else
{
// 입력모드일 때 -> 텍스트가 없으면 포커스해제
if(string.IsNullOrWhiteSpace(inputField.text))
{
isInputActive = false;
GameManager.Instance.isChatting = true;
inputField.DeactivateInputField();
EventSystem.current.SetSelectedGameObject(null); // 포커스해제
}
}
}
}
// 채팅 전송
private void SendChatMessage()
{
if(!string.IsNullOrEmpty(inputField.text))
{
// Player : ~~~
string message = PhotonNetwork.NickName + ": " + inputField.text;
photonView.RPC("ReceiveMessage", RpcTarget.All, message);
inputField.text = ""; // 입력창 초기화
// 포커스 유지
inputField.ActivateInputField();
}
}
[PunRPC]
void ReceiveMessage(string message)
{
AppendMessage(message);
}
void AppendMessage(string message)
{
// 채팅로그 생성
GameObject go = Instantiate(chatLogPrefab, contentTransform);
TMP_Text text = go.GetComponent<TMP_Text>();
text.text = "";
StartCoroutine(AssignMessageDelayed(text, message));
messageQueue.Enqueue(go);
if(messageQueue.Count > maxMessages)
{
GameObject oldestMessage = messageQueue.Dequeue();
Destroy(oldestMessage);
}
ScrollToBottom();
}
// 새로운 채팅으로 포커싱
void ScrollToBottom()
{
StartCoroutine(ScrollToBottomCoroutine());
}
private IEnumerator ScrollToBottomCoroutine()
{
yield return new WaitForEndOfFrame();
// 아래로 스크롤
scrollRect.verticalNormalizedPosition = 0f;
// Canvas 갱신
Canvas.ForceUpdateCanvases();
}
// 이전 메세지 출력 방지
private IEnumerator AssignMessageDelayed(TMP_Text text, string message)
{
yield return null;
text.SetText(message);
}
}
- 주요로직은 AppendMessage 메서드입니다.
- 플레이어가 채팅을 치고 엔터키 입력을하면 채팅로그 프리팹을 생성하여 채팅창(Content영역)에 출력합니다.
- 엔터키 입력을 받기위해 inputField의 onSubmit 이벤트를 활용하였습니다.
-
ScrollToBottom() 메서드는 새로운 채팅로그가 생겼을 때 거기에 포커싱하기위한 메서드입니다.
- AssignMessageDelayed() 코루틴은 채팅로그의 생성직후 레이아웃등의 계산이 완료되지 않았을 수 있기때문에 한 프레임 뒤 텍스트를 할당하여 문제를 방지합니다.
- 없이도 정상동작한다면 필요없습니다.
댓글남기기