Version : Unity 2021.3.5f1
Photon Chat은 PUN2 에 포함되어 있다.
처음 접속을 하면 로비로 이동하여 Room목록을 확인
Room에 접속하면 자동으로 채팅 및 음성이 연결되는 구조
포톤 기능 RND 목적으로 간단하게 만든 앱이라서 발생할 수 있는 모든 문제를 해결하지 않음
(예를 들어 채팅중 네트워크 오류, 입장 불가 등… )
무료 라이센스라서 20명 이상 동시 접속 시 터질 수 있음
Photon PUN2 + Chat
APP.CS
using System.Collections; using System.Collections.Generic; using UnityEngine; using System; using System.IO; using Newtonsoft.Json; namespace Com.Lycos7560 { public struct PlayerData { public string NickName; public string UserGuid; public PlayerData(string _nicknName, string _userGuid = null) { NickName = _nicknName; UserGuid = _userGuid; } } public class APP : SingletonMonobehaviour<APP> { protected override void Awake() { base.Awake(); if (CheckPlayerData()) { PlayerPrefs.DeleteAll(); LoadPlayerNickName(); } else { PlayerPrefs.DeleteAll(); PlayerPrefs.SetString("NickName", string.Empty); PlayerPrefs.SetString("UserGuid", Guid.NewGuid().ToString()); SavePlayerNickName(); } } // 로컬에 데이터가 존재하는지 확인 private bool CheckPlayerData() { bool exists = File.Exists(Application.persistentDataPath + "/PlayerInfo.json"); if (exists) Debug.Log("Existing User"); else Debug.Log("NewUser"); return exists; } // 플레이어의 정보를 로컬에 저장 public void SavePlayerNickName() { PlayerData _PlayerData = new (PlayerPrefs.GetString("NickName"), PlayerPrefs.GetString("UserGuid")); var json = JsonConvert.SerializeObject(_PlayerData); File.WriteAllText(Application.persistentDataPath + "/PlayerInfo.json", json); } // 로컬에 저장된 플레이어의 정보를 불러온다. public void LoadPlayerNickName() { var _Json = File.ReadAllText(Application.persistentDataPath + "/PlayerInfo.json"); var _Info = JsonConvert.DeserializeObject<PlayerData>(_Json); PlayerPrefs.SetString("NickName", _Info.NickName); PlayerPrefs.SetString("UserGuid", _Info.UserGuid); } } }
Connection.CS
using UnityEngine; using UnityEngine.UI; using TMPro; using Photon.Pun; using Photon.Realtime; namespace Com.Lycos7560 { public class Connection : MonoBehaviourPunCallbacks { public GameObject _ConnetMain; public TMP_InputField _InputFieldName; public CanvasGroup ConnetGroup; public Button ConnetBTN; public TMP_Text ConnectionStatusText; public override void OnEnable() { if (PlayerPrefs.GetString("NickName") != string.Empty) _InputFieldName.text = PlayerPrefs.GetString("NickName"); else _InputFieldName.text = string.Empty; } private void Awake() { _InputFieldName.onValueChanged.AddListener((_value) => { if (_value.Length > 0 || _value != string.Empty) { ConnetGroup.alpha = 1f; ConnetGroup.interactable = true; ConnetGroup.blocksRaycasts = true; } else { ConnetGroup.alpha = 0.3f; ConnetGroup.interactable = false; ConnetGroup.blocksRaycasts = false; } }); ConnetBTN.onClick.AddListener(() => { ConnetGroup.alpha = 0.3f; ConnetGroup.interactable = false; ConnetGroup.blocksRaycasts = false; _InputFieldName.interactable = false; PlayerPrefs.SetString("NickName", _InputFieldName.text); APP.Instance.SavePlayerNickName(); ConnectionStatusText.text = "Connecting . . ."; NetworkManager.Instance.Connect(); }); _ConnetMain.SetActive(true); } private void Start() { NetworkManager.Instance.OnDisconnectedAction += ConnectionOnDisconnected; NetworkManager.Instance.OnJoinedLobbyAction += ConnectionOnJoinedLobby; NetworkManager.Instance.OnChangeNetworkClientState += OnChangeNetworkClientState; if (PlayerPrefs.GetString("NickName") != string.Empty) _InputFieldName.text = PlayerPrefs.GetString("NickName"); else _InputFieldName.text = string.Empty; } // 액션 등록 private void ConnectionOnJoinedLobby() { Debug.Log("Connet OnJoinedLobby()"); _ConnetMain.SetActive(false); } // 액션 등록 public void ConnectionOnDisconnected() { Debug.Log("연결 종료"); _ConnetMain.SetActive(true); ConnetGroup.alpha = 1f; ConnetGroup.interactable = true; ConnetGroup.blocksRaycasts = true; _InputFieldName.interactable = true; _InputFieldName.text = PlayerPrefs.GetString("NickName"); } private void OnChangeNetworkClientState(ClientState _clientState) { ConnectionStatusText.text = _clientState.ToString(); } } }
Lobby.CS
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using TMPro; using Photon.Pun; using Photon.Realtime; namespace Com.Lycos7560 { public class Lobby : MonoBehaviourPunCallbacks { public GameObject _LobbyMain; public Button _ExitBtn; public Button _CreateRoomBtn; public TMP_Text LobbyPeopleCountText; public Transform _Content; public List<TypedLobbyInfo> _LobbyInfoList; public GameObject RoomItemPrefab; private List<RoomItem> _RoomItemList = new(); private TypedLobby _CntLobby => PhotonNetwork.CurrentLobby; private List<RoomInfo> _CntRoomList; private Dictionary<string, RoomInfo> _SachedRoomList = new(); private Coroutine _LobbyCoroutine; public GameObject _RoomJoinPopUp; public Button _PopUpCloseBtn; public TMP_Text _RoomJoinFailText; private void Awake() { _ExitBtn.onClick.AddListener(() => { NetworkManager.Instance.DisconnectMainServer(); }); _CreateRoomBtn.onClick.AddListener(() => { _RoomJoinPopUp.SetActive(true); NetworkManager.Instance.CreateRoomWithNickName(false); }); _PopUpCloseBtn.onClick.AddListener(() => { _RoomJoinPopUp.SetActive(false); }); for (int i = 0; i < 20; i++) { RoomItem _RoomItem = Instantiate<GameObject>(RoomItemPrefab, _Content).GetComponent<RoomItem>(); _RoomItem.ChangeRoomInfo("Defalut", 0, 0); _RoomItemList.Add(_RoomItem); _RoomItemList[i].gameObject.SetActive(false); _RoomItem.JoinRoomBtn.onClick.AddListener(() => { _RoomJoinPopUp.SetActive(true); _RoomJoinFailText.text = "Try to access the chat room."; _PopUpCloseBtn.gameObject.SetActive(false); NetworkManager.Instance.JoinRoom(_RoomItem.RoomName.text); }); } _LobbyMain.SetActive(false); } private void Start() { // 액션 등록 NetworkManager.Instance.OnDisconnectedAction += LobbyOnDisconnected; NetworkManager.Instance.OnJoinedLobbyAction += LobbyOnJoinedLobby; NetworkManager.Instance.OnJoinRoomFailedAction += LobbyOnJoinRoomFailed; NetworkManager.Instance.OnJoinedRoomAction += LobbyOnJoinedRoom; NetworkManager.Instance.OnLeftRoomAction += LobbyOnLeftRoom; } #region ActionFuctions private void LobbyOnJoinedLobby() { _LobbyMain.SetActive(true); _RoomJoinFailText.text = "Attempt to connect . . ."; _PopUpCloseBtn.gameObject.SetActive(false); _SachedRoomList.Clear(); if (_LobbyCoroutine != null) { StopCoroutine(_LobbyCoroutine); _LobbyCoroutine = null; } _LobbyCoroutine = StartCoroutine(UpdateLobbyCoroutine()); LobbyPeopleCountText.text = string.Format($"Lobby People Count : {PhotonNetwork.CountOfPlayersOnMaster}"); } private void LobbyOnDisconnected() { if (_LobbyCoroutine != null) StopCoroutine(_LobbyCoroutine); _LobbyCoroutine = null; _LobbyMain.SetActive(false); } private void LobbyOnJoinedRoom() { _RoomJoinPopUp.SetActive(true); _RoomJoinFailText.text = "Attempt to connect . . ."; _PopUpCloseBtn.gameObject.SetActive(false); _LobbyMain.SetActive(false); if (_LobbyCoroutine != null) StopCoroutine(_LobbyCoroutine); _LobbyCoroutine = null; } private void LobbyOnLeftRoom() { _LobbyMain.SetActive(true); _RoomJoinFailText.text = "Attempt to connect . . ."; _PopUpCloseBtn.gameObject.SetActive(false); _SachedRoomList.Clear(); if (_LobbyCoroutine != null) { StopCoroutine(_LobbyCoroutine); _LobbyCoroutine = null; } _LobbyCoroutine = StartCoroutine(UpdateLobbyCoroutine()); UpdateLobbyRoomList(); } private void LobbyOnJoinRoomFailed(short returnCode, string message) { PhotonNetwork.JoinLobby(_CntLobby); _RoomJoinPopUp.SetActive(true); _PopUpCloseBtn.gameObject.SetActive(true); _RoomJoinFailText.text = string.Format($"returnCode : {returnCode} , {message}"); UpdateLobbyRoomList(); } #endregion #region MonoBehaviourPunCallbacks public override void OnRoomListUpdate(List<RoomInfo> roomList) { _CntRoomList = roomList; UpdateLobbyRoomList(); } public override void OnCreatedRoom() { // 카운트 리셋 NetworkManager.Instance.CreateRoomWithNickName(true); } public override void OnCreateRoomFailed(short returnCode, string message) { NetworkManager.Instance.CreateRoomWithNickName(false); } #endregion /// <summary> /// 일정 시간마다 로비에 Room과 있는 로비 인원을 업데이트 /// 목적이 Chatting을 사용 가능할 때까지 대기 /// </summary> private IEnumerator UpdateLobbyCoroutine() { yield return new WaitUntil(() => NetworkManager.Instance._ChatClient != null && NetworkManager.Instance._ChatClient.CanChat); _RoomJoinPopUp.SetActive(false); _PopUpCloseBtn.gameObject.SetActive(true); WaitForSeconds _Delay = new WaitForSeconds(10f); yield return _Delay; while (true) { LobbyPeopleCountText.text = string.Format($"Lobby People Count : {PhotonNetwork.CountOfPlayersOnMaster}"); PhotonNetwork.JoinLobby(_CntLobby); UpdateLobbyRoomList(); yield return _Delay; if (_LobbyMain.activeSelf == false) break; } _LobbyCoroutine = null; } private void UpdateLobbyRoomList() { for (int i = 0; i < _CntRoomList.Count; i++) { RoomInfo info = _CntRoomList[i]; if (info.RemovedFromList) _SachedRoomList.Remove(info.Name); else _SachedRoomList[info.Name] = info; } for (int j = 0; j < _RoomItemList.Count; j++) { _RoomItemList[j].gameObject.SetActive(false); _RoomItemList[j].ChangeRoomInfo("Defalut", 0, 0); } for (int j = 0; j < _CntRoomList.Count; j++) { RoomInfo info = _CntRoomList[j]; if (info.PlayerCount == 0) continue; _RoomItemList[j].gameObject.SetActive(true); _RoomItemList[j].ChangeRoomInfo(info.Name, info.PlayerCount, info.MaxPlayers); } } } }
Chatting.CS
using UnityEngine; using UnityEngine.UI; using TMPro; using Photon.Pun; using Photon.Chat; using ExitGames.Client.Photon; namespace Com.Lycos7560 { public class Chatting : MonoBehaviourPunCallbacks, IChatClientListener { public ChatClient _ChatClient; public GameObject ChattingMain; public Button _CloseChattingMainBtn; public TMP_Text _ServerStateText; public TMP_InputField _ChatInputField; public Button _ChatSendBtn; public TMP_Text _ChattingContentText; public TMP_Text SubscribeUsersText; public ChatChannel _CntChatChannel; #region IChatClientListener CallBacks public void OnGetMessages(string channelName, string[] senders, object[] messages) { for (int i = 0; i < senders.Length; i++) { string sender = senders[i]; string message = messages[i].ToString(); string chatMessage = $"[{sender}]: {message}"; // 채팅 메시지 추가 _ChattingContentText.text += chatMessage + "\n"; } } // 개인 메시지(귓속말) 미구현 public void OnPrivateMessage(string sender, object message, string channelName) { Debug.Log("Private message from [" + sender + "]: " + message); } // 접속한 Room의 이름과 관련된 채널 구독이 성공 CallBack public void OnSubscribed(string[] channels, bool[] results) { _ChattingContentText.text = string.Empty; // 채널을 캐싱해준다. (구독 완료된 시점) if (_ChatClient.TryGetChannel("Room_" + PhotonNetwork.CurrentRoom.Name, out _CntChatChannel)) // 나가기 버튼 활성화 _CloseChattingMainBtn.gameObject.SetActive(true); // 첫 화면 출력 _ChattingContentText.text += " Welcome to Photon Chat. \n"; UpdateSubscribeUsersText(); /* 디버깅 for (int i = 0; i < channels.Length; i++) Debug.Log("Subscribed to Channel: " + channels[i]); */ } // 접속한 Room의 이름과 관련된 채널 구독을 해제 CallBack public void OnUnsubscribed(string[] channels) { // 구독 해제와 동시에 Clear _ChattingContentText.text = string.Empty; _CntChatChannel = null; /* 디버깅 for (int i = 0; i < channels.Length; i++) Debug.Log("Unsubscribed from Channel: " + channels[i]); */ } // 디버그 CallBack 미구현 public void DebugReturn(DebugLevel level, string message) { Debug.Log("Debug: " + level + " - " + message); } // 디버그 CallBack 미구현 public void OnDisconnected() { Debug.Log("Disconnected from Chat Server"); } // 디버그 CallBack 미구현 public void OnChatStateChange(ChatState state) { _ServerStateText.text = string.Format($"Server State : {state}"); } // 디버그 CallBack 미구현 public void OnStatusUpdate(string user, int status, bool gotMessage, object message) { Debug.Log("Status Update - User: " + user + ", Status: " + status); } // 어떤 유저가 해당 채널을 구독 CallBack public void OnUserSubscribed(string channel, string user) { // 메시지 창에 어떤 유저가 구독했는지 출력 string message = $"<color=#06FF00>'{user}' has joined '{channel}'</color>"; _ChattingContentText.text += message + "\n"; UpdateSubscribeUsersText(); } // 어떤 유저가 해당 채널을 구독 해제 CallBack public void OnUserUnsubscribed(string channel, string user) { // 메시지 창에 어떤 유저가 구독을 해제했는지 출력 string message = $"<color=#FF0000> '{user}' has left '{channel}'</color>"; _ChattingContentText.text += message + "\n"; UpdateSubscribeUsersText(); } #endregion #region MonoBehaviour private void Awake() { ChattingMain.SetActive(false); _CloseChattingMainBtn.onClick.AddListener(() => { PhotonNetwork.LeaveRoom(); }); _ChatSendBtn.onClick.AddListener(() => { if (_ChatInputField.text == string.Empty) return; else { SendRoomMessage(_ChatInputField.text); _ChatInputField.text = string.Empty; } }); } private void Start() { // 액션 등록 NetworkManager.Instance.OnJoinedRoomAction += ChattingOnJoinedRoom; NetworkManager.Instance.OnLeftRoomAction += ChattingOnLeftRoom; NetworkManager.Instance.OnJoinedLobbyAction += ChattingOnJoinedLobby; } private void Update() { // !!필수!! // ChatClient의 콜백 처리를 위해 주기적으로 호출 if (_ChatClient != null) _ChatClient.Service(); } #endregion #region ActionFuctions /// <summary> /// 로비에 접속하면 Chatting Server를 활성화 시킨다. /// </summary> private void ChattingOnJoinedLobby() { // 구독 전에 나가는 것을 방지 _CloseChattingMainBtn.gameObject.SetActive(false); if (_ChatClient == null) { // 로비에 접속한 후 ChatClient 초기화 및 연결 _ChatClient = new ChatClient(this); _ChatClient.Connect(PhotonNetwork.PhotonServerSettings.AppSettings.AppIdChat, PhotonNetwork.AppVersion, new Photon.Chat.AuthenticationValues(PhotonNetwork.NickName)); Debug.LogFormat($"Chatting Server 초기화 및 연결 시작"); } } /// <summary> /// Room에 접속하면 실행되는 함수 /// Room의 이름과 관련된 채팅 서버에 접속한다. /// </summary> private void ChattingOnJoinedRoom() { _ChattingContentText.text = string.Empty; ChattingMain.SetActive(true); string _Channel = "Room_" + PhotonNetwork.CurrentRoom.Name; // 가입할 채널 이름 배열 _ChatClient.Subscribe(_Channel, 0, -1, new ChannelCreationOptions { PublishSubscribers = true } ); // 채널에 구독 Debug.LogFormat($"{"Room_" + PhotonNetwork.CurrentRoom.Name} :: 구독"); } /// <summary> /// Room에서 나갈 경우 실행되는 함수 /// </summary> private void ChattingOnLeftRoom() { string[] _Channels = new string[] { _CntChatChannel.Name }; _ChatClient.Unsubscribe(_Channels); _CntChatChannel = null; ChattingMain.SetActive(false); } #endregion /// <summary> /// 구독한 Channel에 메시지를 보냅니다. /// </summary> /// <param name="message"></param> private void SendRoomMessage(string message) { if (_ChatClient != null && _ChatClient.CanChat) { // _ChatClient가 구독한 방에 메시지를 보냅니다. _ChatClient.PublishMessage("Room_" + PhotonNetwork.CurrentRoom.Name, message); } } /// <summary> /// 체널의 있는 유저들의 정보를 갱신합니다. /// </summary> private void UpdateSubscribeUsersText() { if (_ChatClient != null && _CntChatChannel != null) { var _Users = _CntChatChannel.Subscribers; SubscribeUsersText.text = string.Empty; foreach (var _user in _Users) SubscribeUsersText.text += _user + " "; } else SubscribeUsersText.text = "There is a problem with the network."; } } }
NetworkManager.CS
using UnityEngine; using UnityEngine.Events; using Photon.Pun; using Photon.Realtime; using TMPro; using Photon.Chat; namespace Com.Lycos7560 { public class NetworkManager : MonoBehaviourPunCallbacks { public static NetworkManager Instance; public Connection _Connection; public Chatting _Chatting; public Lobby _Lobby; public ChatClient _ChatClient { get { return _Chatting._ChatClient; } } #region Private Serializable Fields #endregion #region Private Fields /// <summary> /// 이 클라이언트의 버전 번호입니다. /// 사용자는 gameVersion에 의해 서로 분리되어 있습니다 /// (이를 통해 변경 사항을 변경할 수 있습니다). /// </summary> string gameVersion = "1"; /// <summary> /// 룸당 최대 플레이어 수입니다. /// 방이 꽉 차면 새로운 플레이어가 참여할 수 없기 때문에 새로운 방이 만들어집니다. /// </summary> [Tooltip("방이 꽉 차면 새로운 플레이어가 참여할 수 없기 때문에 새로운 방이 만들어집니다.")] [SerializeField] private byte maxPlayersPerRoom = 5; /// <summary> /// 현재 NetworkClientState의 상태를 나타냅니다. /// </summary> [Tooltip("현재 NetworkClientState의 상태를 나타냅니다. NetworkClientStateCheck 함수를 통하여 업데이트 됩니다")] [SerializeField] private ClientState _CntNetworkClientState = ClientState.PeerCreated; public ClientState CntNetworkClientState { get => _CntNetworkClientState; set { if (_CntNetworkClientState != value) OnChangeNetworkClientState?.Invoke(value); _CntNetworkClientState = value; } } [Tooltip("방생성을 시도한 횟수입니다.")] [SerializeField] private int _CreateRoomCnt = 1; #endregion #region public Fields #endregion public UnityAction<ClientState> OnChangeNetworkClientState; public UnityAction OnDisconnectedAction; public UnityAction OnJoinedLobbyAction; public UnityAction OnJoinedRoomAction; public UnityAction<short, string> OnJoinRoomFailedAction; public UnityAction OnLeftRoomAction; #region MonoBehaviour CallBacks /// <summary> /// MonoBehavior 메서드는 초기 초기화 단계에서 Unity에 의해 GameObject에 호출되었습니다. /// </summary> void Awake() { Instance = this; // #Critical // 이렇게하면 PhotonNetwork를 사용할 수 있습니다. // 마스터 클라이언트의 LoadLevel()과 동일한 룸에 있는 모든 클라이언트의 LoadLevel이 자동으로 동기화됩니다 PhotonNetwork.AutomaticallySyncScene = true; UpdateCntNetworkClientState(); } /// <summary> /// 마스터 서버에 진입한 후에 바로 실행 /// </summary> public override void OnConnectedToMaster() { UpdateCntNetworkClientState(); PhotonNetwork.JoinLobby(); PhotonNetwork.NickName = PlayerPrefs.GetString("NickName"); } /// <summary> /// 접속이 종료되었을 경우 실행된다. /// </summary> /// <param name="cause"></param> public override void OnDisconnected(DisconnectCause cause) { if (cause == DisconnectCause.DisconnectByServerLogic) { // 로비 입장 실패 Debug.LogFormat($"<color=red> Failed to enter lobby : {cause} </color>"); } else { // 기타 연결 끊김 원인(예: 인터넷 연결 끊김) Debug.Log($"<color=red> Disconnected from server : {cause} </color>"); } UpdateCntNetworkClientState(); OnDisconnectedAction?.Invoke(); } public override void OnJoinedLobby() { UpdateCntNetworkClientState(); OnJoinedLobbyAction?.Invoke(); } public override void OnJoinedRoom() { string userId = PhotonNetwork.LocalPlayer.UserId; Debug.Log("Joined Room - User ID: " + userId); OnJoinedRoomAction?.Invoke(); } public override void OnJoinRoomFailed(short returnCode, string message) { OnJoinRoomFailedAction?.Invoke(returnCode, message); } public override void OnLeftRoom() { OnLeftRoomAction?.Invoke(); } public void CreateRoomWithNickName(bool _CntReset) { if (!_CntReset) { string _RoomStringBase = PlayerPrefs.GetString("NickName"); string _UserGuid = PlayerPrefs.GetString("UserGuid"); if (_UserGuid.Length >= _CreateRoomCnt) PhotonNetwork.CreateRoom(_RoomStringBase + "_#" + _UserGuid[.._CreateRoomCnt++], new RoomOptions { MaxPlayers = maxPlayersPerRoom, CleanupCacheOnLeave = true, PlayerTtl = 1000 }); else Debug.Log("방생성 실패(너무 많은 생성 실패)"); } else _CreateRoomCnt = 1; } #endregion #region Public Methods /// <summary> /// 연결 프로세스를 시작합니다. /// </summary> public void Connect() { // 연결되었다면 로비에 참여 if (PhotonNetwork.IsConnected) PhotonNetwork.JoinLobby(new TypedLobby("MainLobby", LobbyType.Default)); else { PhotonNetwork.GameVersion = gameVersion; PhotonNetwork.ConnectUsingSettings(); } } public void JoinRoom(string _roomName) { PhotonNetwork.JoinRoom(_roomName); } public void DisconnectMainServer() { PhotonNetwork.Disconnect(); } public void UpdateCntNetworkClientState() { CntNetworkClientState = PhotonNetwork.NetworkClientState; } #endregion } }
RoomItem.CS
using UnityEngine; using UnityEngine.UI; using TMPro; namespace Com.Lycos7560 { public class RoomItem : MonoBehaviour { public TMP_Text RoomName; public TMP_Text PeopleNumber; public Button JoinRoomBtn; public void ChangeRoomInfo(string _name, int _cnt, int _max) { RoomName.text = _name; PeopleNumber.text = string.Format($"{_cnt} / {_max}"); } } }
Photon PUN2 + Chat + Voice
Chatting.CS (수정)
using UnityEngine; using UnityEngine.UI; using TMPro; using Photon.Pun; using Photon.Chat; using Photon.Voice.Unity; using ExitGames.Client.Photon; namespace Com.Lycos7560 { public class Chatting : MonoBehaviourPunCallbacks, IChatClientListener { public ChatClient _ChatClient; public GameObject ChattingMain; public Button _CloseChattingMainBtn; public TMP_Text _ServerStateText; public TMP_InputField _ChatInputField; public Button _ChatSendBtn; public TMP_Text _ChattingContentText; public TMP_Text SubscribeUsersText; public ChatChannel _CntChatChannel; public GameObject _PlayerSpeaker; #region IChatClientListener CallBacks public void OnGetMessages(string channelName, string[] senders, object[] messages) { for (int i = 0; i < senders.Length; i++) { string sender = senders[i]; string message = messages[i].ToString(); string chatMessage = $"[{sender}]: {message}"; // 채팅 메시지 추가 _ChattingContentText.text += chatMessage + "\n"; } } // 개인 메시지(귓속말) 미구현 public void OnPrivateMessage(string sender, object message, string channelName) { Debug.Log("Private message from [" + sender + "]: " + message); } // 접속한 Room의 이름과 관련된 채널 구독이 성공 CallBack public void OnSubscribed(string[] channels, bool[] results) { _ChattingContentText.text = string.Empty; // 채널을 캐싱해준다. (구독 완료된 시점) if (_ChatClient.TryGetChannel("Room_" + PhotonNetwork.CurrentRoom.Name, out _CntChatChannel)) //// 나가기 버튼 활성화 //_CloseChattingMainBtn.gameObject.SetActive(true); // 첫 화면 출력 _ChattingContentText.text += " Welcome to Photon Chat. \n"; UpdateSubscribeUsersText(); /* 디버깅 for (int i = 0; i < channels.Length; i++) Debug.Log("Subscribed to Channel: " + channels[i]); */ } // 접속한 Room의 이름과 관련된 채널 구독을 해제 CallBack public void OnUnsubscribed(string[] channels) { // 구독 해제와 동시에 Clear _ChattingContentText.text = string.Empty; /* 디버깅 for (int i = 0; i < channels.Length; i++) Debug.Log("Unsubscribed from Channel: " + channels[i]); */ } // 디버그 CallBack 미구현 public void DebugReturn(DebugLevel level, string message) { Debug.Log("Debug: " + level + " - " + message); } // 디버그 CallBack 미구현 public void OnDisconnected() { Debug.Log("Disconnected from Chat Server"); } // 디버그 CallBack 미구현 public void OnChatStateChange(ChatState state) { _ServerStateText.text = string.Format($"Server State : {state}"); } // 디버그 CallBack 미구현 public void OnStatusUpdate(string user, int status, bool gotMessage, object message) { Debug.Log("Status Update - User: " + user + ", Status: " + status); } // 어떤 유저가 해당 채널을 구독 CallBack public void OnUserSubscribed(string channel, string user) { // 메시지 창에 어떤 유저가 구독했는지 출력 string message = $"<color=#06FF00>'{user}' has joined '{channel}'</color>"; _ChattingContentText.text += message + "\n"; UpdateSubscribeUsersText(); } // 어떤 유저가 해당 채널을 구독 해제 CallBack public void OnUserUnsubscribed(string channel, string user) { // 메시지 창에 어떤 유저가 구독을 해제했는지 출력 string message = $"<color=#FF0000> '{user}' has left '{channel}'</color>"; _ChattingContentText.text += message + "\n"; UpdateSubscribeUsersText(); } #endregion #region MonoBehaviour private void Awake() { ChattingMain.SetActive(false); _CloseChattingMainBtn.onClick.AddListener(() => { // 나가기 버튼 비활성화 _CloseChattingMainBtn.gameObject.SetActive(false); PhotonNetwork.LeaveRoom(); }); _ChatSendBtn.onClick.AddListener(() => { if (_ChatInputField.text == string.Empty) return; else { SendRoomMessage(_ChatInputField.text); _ChatInputField.text = string.Empty; } }); } private void Start() { // 액션 등록 NetworkManager.Instance.OnJoinedRoomAction += ChattingOnJoinedRoom; NetworkManager.Instance.OnLeftRoomAction += ChattingOnLeftRoom; NetworkManager.Instance.OnJoinedLobbyAction += ChattingOnJoinedLobby; } private void Update() { // !!필수!! // ChatClient의 콜백 처리를 위해 주기적으로 호출 if (_ChatClient != null) _ChatClient.Service(); } #endregion #region ActionFuctions /// <summary> /// 로비에 접속하면 Chatting Server를 활성화 시킨다. /// </summary> private void ChattingOnJoinedLobby() { // 구독 전에 나가는 것을 방지 _CloseChattingMainBtn.gameObject.SetActive(false); if (_ChatClient == null) { // 로비에 접속한 후 ChatClient 초기화 및 연결 _ChatClient = new ChatClient(this); _ChatClient.Connect(PhotonNetwork.PhotonServerSettings.AppSettings.AppIdChat, PhotonNetwork.AppVersion, new Photon.Chat.AuthenticationValues(PhotonNetwork.NickName)); Debug.LogFormat($"Chatting Server 초기화 및 연결 시작"); } } /// <summary> /// Room에 접속하면 실행되는 함수 /// Room의 이름과 관련된 채팅 서버에 접속한다. /// </summary> private void ChattingOnJoinedRoom() { _ChattingContentText.text = string.Empty; ChattingMain.SetActive(true); string _Channel = "Room_" + PhotonNetwork.CurrentRoom.Name; // 가입할 채널 이름 배열 _ChatClient.Subscribe(_Channel, 0, -1, new ChannelCreationOptions { PublishSubscribers = true } ); // 채널에 구독 Debug.LogFormat($"{"Room_" + PhotonNetwork.CurrentRoom.Name} :: 구독"); _PlayerSpeaker = PhotonNetwork.Instantiate("PlayerVoiceChatPrefab", Vector3.zero, Quaternion.identity); _PlayerSpeaker.GetComponent<Speaker>().enabled = false; _PlayerSpeaker.GetComponent<AudioSource>().enabled = false; // 나가기 버튼 활성화 _CloseChattingMainBtn.gameObject.SetActive(true); } /// <summary> /// Room에서 나갈 경우 실행되는 함수 /// </summary> private void ChattingOnLeftRoom() { PhotonNetwork.Destroy(_PlayerSpeaker.GetPhotonView()); string[] _Channels = new string[] { _CntChatChannel.Name }; _ChatClient.Unsubscribe(_Channels); _CntChatChannel = null; ChattingMain.SetActive(false); } #endregion /// <summary> /// 구독한 Channel에 메시지를 보냅니다. /// </summary> /// <param name="message"></param> private void SendRoomMessage(string message) { if (_ChatClient != null && _ChatClient.CanChat) { // _ChatClient가 구독한 방에 메시지를 보냅니다. _ChatClient.PublishMessage("Room_" + PhotonNetwork.CurrentRoom.Name, message); } } /// <summary> /// 체널의 있는 유저들의 정보를 갱신합니다. /// </summary> private void UpdateSubscribeUsersText() { if (_ChatClient != null && _CntChatChannel != null) { var _Users = _CntChatChannel.Subscribers; SubscribeUsersText.text = string.Empty; foreach (var _user in _Users) SubscribeUsersText.text += _user + " "; } else SubscribeUsersText.text = "There is a problem with the network."; } } }
APK File
- 마이크 온오프 기능 추가
- 스피커 관련 오류 수정