목차
- 채팅 글 길면 개행 할 수 있게 하는 법
- 코드로 텍스트 개행해주기
- Text 박스 Height 문제 해결법
- 스크롤 바 맨 밑에 있을 때 채팅치면 계속 맨 밑에 있게 하는 법
- 스크롤 바 계속 내리기 코드로 구현
- Photon Voice 맛보기
채팅 글 길면 개행 할 수 있게 하는 법
채팅을 개행 할 수 있게 해보자.
Text 프리팹
여기서, Vertical 을 OverFlow 하면 개행이 알아서 된다.
근데 이렇게 하게 되면, 텍스트가 개행이 되긴 하는데,
다음 채팅을 쳤을 때, 개행 된곳에 나오게 된다.
Text 프리팹 자체의 Heigt 높이가 변하진 않았기 때문
그래서 이 Text 프리팹 에 Content Size Fitter 을 넣으면 알아서 되긴하는데 뭔가 안좋음
노란색 느낌표도 나고, 이게 끊기는 어떤 문제가 있다고 하심!
그렇기 때문에 코드로 개행을 시켜보자
코드로 텍스트 개행해주기
ChatItem.cs 스크립트를 하나 만든다!
이거는 Text 프리팹에 넣을 거임ㅎ
chatText 변수와
RectTransform 변수를 생성하고,
GetComponenet 로 받아오자.
그리고 텍스트를 셋팅 해서 Height 를 맞춰주는 함수를 만들어보자!
렉트트렌스폼에 x값은 그대로 놔두고
지금 높이가 문제니까(개행 할꺼니까), chatText 에 preferredHeight 라는걸 써서,
개행해도 맞춰주게 할거임!
[ Text 프리팹의 RectTransform 의 y값 자체를 개행 한 만큼 넓혀 주려고 하는거다! ]
일단 이렇게 함수를 생성한 후
ChatManager.cs 로 이동해보자----->
우리가 만들었었던 RpcAddChat 함수이다.
여기서
GameObject item = Instantiate(chatI~~~, trContent)
이 줄이 보이시나요?
여기서 만들어진 item 이 우리가 만든 Text Prefabs. 즉, ChatItem 이다!
이게 뭔데?
채팅 치면 생성되는 Text
아시죠?
처음에 위에서 ChatItem prefab 에 넣을 ChatItem.cs 를 만들었으니까, 여기서 가져올거임!
item (ChatItem) 에서 ChatItem 컴포넌트를 가져와서, 아까만든 SetText() 함수를 실행시킨다
=> 이 뜻은, 인풋필드에 글을 쓰고, Enter 를 누르면 ChatItem 프리팹이 생성되고 Content 안에 들어가면서
SetText 함수를 실행시켜 인풋필드에 글을 chatItem 에 넣고 heigt 를 설정해준거임!
이제 실행을 해보면, 될거같은데 Null 오류가 뜬다..??
왜 그럴까
ChatItem.cs 로 가보자------>
이걸 아까 Start 함수에 넣었었는데,
Awake 로 바꾸자!
왜 Null 오류가 떴었냐면,
ChatManager.cs 에서 ChatItem 을 Instantiate 한 후,
GetComponent 해서 SetText 함수를 호출한다고 했잖아?
근데,
ChatItem 안에 들어있는 ChatItem.cs 에 들어있는 Start가 실행되기도 전에 (= Text , RectTransform 컴포넌트 받아오기도 전에)
SetText 함수 호출을 ChatManager.cs 에서 불러버린거임.
그래서 SetText 함수 에서는 chatText 변수와 rt 변수가 Null 인거야
즉,ChatManager.cs 에서 ChatItem 에 있는 SetText 함수를 호출한게
ChatItem.cs 에 있는 Start 함수가 실행 된거보다 빠르게 호출을 한거야
디지몬이 진화하고있는데 공격하라고 시킨 느낌?
그래서, Start 가 아닌 Awake 로 바꿔서 진화를 더 빠르게 시킨거지!
힘들게 여기까지 이해해서 왔다.
저렇게 까지 다 하고 실행해봤다!
2줄짜리 글(개행 성공) 해서 나왔다!
근데 또 문제가..
근데 그걸 담는 넓이가 뭔가 넓게 나옴..!
근데 우리가 원하는건
이렇게되는 이유:
지금 Text 는 150 으로 맞춰놔서 , 글을 많이 쓰면 3줄이 된다고 치자.
근데 Content 에서 자식의 width 를 강제로 190 으로 끌어올리기 때문에,
실제로 나오는건 2줄이지만 3줄짜리의 크기의 넓이로 나올 수가 있어
즉, 150 짜리에서 320 길이의 글을 쓰면,
150,150,20 으로 3줄이 나올거임
하지만
190 짜리에서는 190,130 이렇게 2줄만 생김.
190짜리는 Content 에서 강제로 늘려준거고, 150 짜리는 원래 Text 의 크기였음
그래서 Text 의 넓이는 3줄짜리라고 유니티님 한테 "3줄로 시켰으니까, 3줄 크기 맞춰주세요~" 말했는데
글자의 길이는 2줄만 나온 현상이 일어남
그러니까, Text Prefab 을 강제로 Content width 에 맞추는걸 빼버리면 되기는 해
2줄짜리가 2줄로 나오긴 해
그런데
width 가 Content 에 딱 맞게 되지는 않는다!
우리는 이런거에 만족하지 않는다
완벽을 위해 달려나가자
다시 Width 를 체크하는 위험에 무릎쓴다.
고통을 알지만 우리는 달려나간다.
그럼 이렇게 하면 어떻게 할까?
도대체 뭐가 문제길래 안될까?
Text 프리팹의 크기가 150 으로 설정되어있었는데,
Content 에 저 Control Child Size 에 Width 가
Text 프리팹의 크기를 자신의 사이즈에 맞게 하는 과정이
SetText 함수가 호출되는것 보다 느리게 나온다.
즉, ChatManger에서 -> rpc 를 호출을 하면 (= InputField 에 채팅을 입력하고 Enter 를 치면)
Text 프리팹을 소환 하고, SetText 함수(Height 를 맞추는 기능)를 호출하는것 까지 한바퀴임
이 한바퀴가 돌고 나서, 소환된 Text Prefab 의 Width 를 Content Width 에 맞추기 때문에
이미 150으로 설정 된 width 로 Unity 가 개행을 시킨다.
이미 개행이 되어있기 때문에 Content 사이즈인 190 으로 Text 를 만들어도 이미 개행이 다 되어있음
한 프레임만 늦게 실행되도 Content Width 에 맞게 된 다음에, 잘 개행이 될텐데.
여튼 한 프레임 사이에 순서가 달라져서 그렇다
Text 의 width 를 극단적으로 20 정도로 두면 Height 가 엄청 늘어난다.
채팅 2개밖에 안쳤는데 저렇게 많은 개행이 들어간다!
width 를 20으로 두고 해서 저렇게 많이 쓰면
엄청 많은 개행이 된거임.
Unity 가 "아 width 가 20 인데 저렇게 많이 썼으면 한 10줄은 띄어야 겠는데요?"
이렇게 해버린거임
근데 그 다음프레임에
강제적으로 width 를 Content 까지 늘려서 글씨는 저렇게 나오긴 함
애초에 Text 의 width 크기를 그러면 190 으로 하면 되지 않냐? 라는 생각을 할 수도 있다.
근데 Content의 크기는 달라질 수도 있다. 크기에 따라서,
동적으로 다 해주고싶어서 이렇게 하는거임!
Text 박스 Height 문제 해결법
우리가 SetText 에서 썼던
rt.sizeDelta = new Vector2(rt.sizeDelta.x, chatText.preferredHeight);
이거를 Update 에 넣어주어서, 계속 새로 고침을 해주면 된다
ChatItem.cs------>
Update 에 저렇게 써주자!
변경이 되면, height 를 맞춰는 코드를 작성한다!
스크롤 바 맨 밑에 있을 때 채팅치면 계속 맨 밑에 있게 하는 법
이 상태일 때, Content 의 좌표값은 (0,0) 이다.
지금은 채팅을 계속치면 이렇게 채팅은 늘어나는데,
스크롤바는 계속 위에있다.
하지만 우리가 채팅을 할 때, 스크롤바는 아래로 내려가게 된다.
여기서 집중해야 한다
Content 클릭해놨는데 왼쪽 사진(스크롤 바가 제일 밑에 있는 상태)은 지금 Content 가 제일 위로 이동한거 보이나요?
스크롤 바가 맨 밑에 있을 때는, Content 의 Y 좌표가 최대인것이다.
Content 가 제일 위로 올라간거니까!
검은 박스 => Scroll View,
빨간 박스 => Content
중요한것 .
저 두개의 뺀 값이 Content 의 y값이다!!
그러면 h2 랑 h1 이랑 뺀 값보다 y 가 크다면 스크롤 바가 제일 아래에 있다고 볼 수 있다
위 그림에서 알 수 있는 점은 현재 채팅을 저정도로 쳤을 때, Content 의 Height 는 280 이다.(더 많이 치면 더 늘어날 것이다)
그리고 ScrollView 의 Height 는 200 이다.
두개의 Height 를 빼면 80 이 나온다.
즉 y 가 80 이면 제일 아래에 있다고 볼 수 있는것이다.
근데 소수점 그런것 때문에
, y => h2 - h1 이면 바닥에 있다고 할 수 있는 것이다.
위에서 말 했듯이
여기서 채팅을 하나쓰면, Content 가 채팅 Height 만큼 늘어난다. 그 Height 를 H3 이라고 하자.
그러면 y 값에 H3 을 더 더해주면 채팅 Heigth 만큼 올라가는거다!
스크롤바가 밑으로 계속 내려가는것 처럼 보일 것이다
하지만,
채팅을 추가하기 전에 스크롤 바가 바닥에 있었는지를 알아야한다!!
스크롤 바가 중간에 있으면 채팅을 쳐도 스크롤 바가 중간에 있다. (보통 채팅이 그렇다. 카톡은 아닌가,,?)
여튼 스크롤바가 바닥에 있을 때만, 스크롤 바를 계속 내리는걸로 해보자
그럼
엔터를 딱 눌렀을 때,
if( y => H2 - H1)
이 상태면
바닥에 있었구나 상태인거임 (이 부분 이해 안되면 위에 좀 더 읽고 오면 됩니다!)
여기서는 H3 를 Text 하나 더 추가 됬을 때의 전체 값이라고 하자 (위에서 쓴 H3 의 정의랑 다름!)
그럼 H3 = H2 + Text.y 이고
COntent. Y 를 이렇게 바꿔주면 됨
스크롤 바 계속 내리기 코드로 구현
ChatManger.cs ------>
RpcAddChat () 에서
4,5 번 추가해야함
위에 변수 추가
public RectTransform rtScrollView;
// 이전 Content 의 H값
float prevContentH;
[PunRPC]
void RpcAddChat(string chat)
{
// 이전 content의 H값을 저장하자
prevContentH = trContent.sizeDelta.y;
//1. 글을 쓰다가 엔터를 치면
//2. ChatItem 을 하나 만든다.
//(부모를 ScrollView - Content)
GameObject item = Instantiate(chatItemFactory, trContent);
//3. text 컴포넌트 가져와서 inputField 의 내용을 세팅
ChatItem chatItem = item.GetComponent<ChatItem>();
chatItem.SetText(chat);
//4. 이전에 바닥에 닿아있었다면
// == (Content.y >= 변경되기 전 content Hight 값 - ScrollView 의 Hight 값)
if(trContent.anchoredPosition.y >= prevContentH - rtScrollView.sizeDelta.y)
{
//5. 추가된 높이만큼 content y값을 변경하겠다. (지금은 이미 추가 된 상태인거임)
trContent.anchoredPosition = new Vector2(0, trContent.sizeDelta.y - rtScrollView.sizeDelta.y);
}
}
TrContent 에는 Content 를
RtScrollView 에는 ScrollView 를 넣는다!
근데 이렇게 하면, 잘 안됨
변경이 안된 상태인듯.. 위에서 일어났던 그런 문제들 때문에..
그래서 한박자 늦게 실행 시켜보자 (코루틴으로!)
public RectTransform rtScrollView;
// 이전 Content 의 H값
float prevContentH;
[PunRPC]
void RpcAddChat(string chat)
{
// 이전 content의 H값을 저장하자
prevContentH = trContent.sizeDelta.y;
//1. 글을 쓰다가 엔터를 치면
//2. ChatItem 을 하나 만든다.
//(부모를 ScrollView - Content)
GameObject item = Instantiate(chatItemFactory, trContent);
//3. text 컴포넌트 가져와서 inputField 의 내용을 세팅
ChatItem chatItem = item.GetComponent<ChatItem>();
chatItem.SetText(chat);
//4. 이전에 바닥에 닿아있었다면
StartCoroutine(AutoScrollBottom());
}
IEnumerator AutoScrollBottom()
{
yield return null;
// == (Content.y >= 변경되기 전 content Hight 값 - ScrollView 의 Hight 값)
if (trContent.anchoredPosition.y >= prevContentH - rtScrollView.sizeDelta.y)
{
//5. 추가된 높이만큼 content y값을 변경하겠다. (지금은 이미 추가 된 상태인거임)
trContent.anchoredPosition = new Vector2(0, trContent.sizeDelta.y - rtScrollView.sizeDelta.y);
}
}
근데 지금 ScrollView 가 중간에 있어도 이게 계속 실행 되려고 한다
그래서
이 상태에서는 content 값을 변경 안하게 해야한다!
이건 ScrollView의 heigh값이 Content 의 heigt 값 보다 큰 상황이다!
그러니까 , content 의 height 값 > scrollView 의 height 값 일 때만 실행 시키면 된다!
trContent.sizeDelta.y => 그냥 자체 height값
trContent.anchoredPosition.y => 지금 Inspector 창에 나와있는 y값 (H2 - H1)값
추가 안했을 때는 H2 -H1 이 Content의 y값이어야 스크롤 바가 제일 아래쪽에 위치 한것이었다
Text 가 하나 더 추가 됬을때는 H3-H1 이 Content 의 y값이어야 스크롤 바가 제일 아래쪽에 위치 한것이겠지?
왼쪽이 prevContentH(H2) 이고, 오른쪽이 Text 를 하나 더 생성한 Content(H3) 이다.
Text 를 하나 더 생성하든 뭐하든 오른쪽의 Content.y 값은 왼쪽이랑 다를바가 없다.
근데 소수점 뭐 그런거 때문에
텍스트를 하나 더 만들었을 때 오른쪽의 y 값이 왼쪽의 H2-H1값보다 같거나 크다면,
"원래 바닥에 있었구나"
라고 간주 하고, y값을 최신화 해주는거다. (H3 - H1)로
원래 스크롤바가 바닥에 있는게 아니었다면
오른쪽에 있는 y값이 왼쪽에 있는 y값 보다 작겠지?
이게 헷갈리는게 y값 은 스크롤 바에 따라 변하는 값이고
Height 간의 뺄셈은 스크롤바와 상관없이 변하지 않는 값이다
ChatManager.cs , ChatItem.cs 완성본
1. ChatManager.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using Photon.Pun;
using UnityEngine.EventSystems;
public class ChatManager : MonoBehaviourPun
{
// ChatItem 공장
public GameObject chatItemFactory;
//InputChat
public InputField inputChat;
//Scrollview 의 Content transfrom
public RectTransform trContent;
Color nickColor;
// Start is called before the first frame update
void Start()
{
// InputChat 에서 엔터를 눌렀을 때 호출 되는 함수 등록
inputChat.onSubmit.AddListener(OnSubmit);
//커서를 안보이게!
Cursor.visible = false;
nickColor = new Color(Random.Range(0.0f, 1.0f), Random.Range(0.0f, 1.0f), Random.Range(0.0f, 1.0f));
}
// Update is called once per frame
void Update()
{
//esc 키를 누르면 커서를 활성화!
if (Input.GetKeyDown(KeyCode.Escape))
{
Cursor.visible = true;
}
// 클릭 했을 때
if (Input.GetMouseButtonDown(0))
{
//만약에 커서가 UI에 없다면
if (EventSystem.current.IsPointerOverGameObject() == false)
{
// 커서 안보이게
Cursor.visible = false;
}
}
// 근데, InputField 누르면 false가 됨,
//모바일 땐
//if (EventSystem.current.IsPointerOverGameObject(Input.GetTouch(0).fingerId) == false)
//{
//}
}
// InputChat 에서 엔터를 눌렀을 때 호출 되는 함수
void OnSubmit(string s)
{
//<color = #FF0000> 닉네임 </color>
string chatText = "<color=#" + ColorUtility.ToHtmlStringRGB(nickColor) + ">" + PhotonNetwork.NickName + "</color>"+ " : " + s;
photonView.RPC("RpcAddChat", RpcTarget.All, chatText);
//4. inputChat 의 내용을 초기화
inputChat.text = "";
//5. inputChat 이 선택되도록 한다.
inputChat.ActivateInputField();
}
public RectTransform rtScrollView;
// 이전 Content 의 H값
float prevContentH;
[PunRPC]
void RpcAddChat(string chat)
{
// 이전 content의 H값을 저장하자
prevContentH = trContent.sizeDelta.y;
//1. 글을 쓰다가 엔터를 치면
//2. ChatItem 을 하나 만든다.
//(부모를 ScrollView - Content)
GameObject item = Instantiate(chatItemFactory, trContent);
//3. text 컴포넌트 가져와서 inputField 의 내용을 세팅
ChatItem chatItem = item.GetComponent<ChatItem>();
chatItem.SetText(chat);
//4. 이전에 바닥에 닿아있었다면
StartCoroutine(AutoScrollBottom());
}
IEnumerator AutoScrollBottom()
{
yield return null;
// 스크롤뷰 H보다 Content H값이 클 때만(스크롤이 가능한 상태라면)
// 스크롤바가 생기는 시점 (채팅을 어느정도 쳐서, Content 가 Scroll View 를 넘어간 시점)
if (trContent.sizeDelta.y > rtScrollView.sizeDelta.y)
{
// == (Content.y >= 변경되기 전 content Hight 값 - ScrollView 의 Hight 값)
// 스크롤 바가 바닥에 있는거라면
if (trContent.anchoredPosition.y >= prevContentH - rtScrollView.sizeDelta.y)
{
//5. 추가된 높이만큼 content y값을 변경하겠다. (지금은 이미 추가 된 상태인거임)
trContent.anchoredPosition = new Vector2(0, trContent.sizeDelta.y - rtScrollView.sizeDelta.y);
}
}
}
}
2. ChatItem.cs 완성본
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class ChatItem : MonoBehaviour
{
//Text
Text chatText;
//RectTransform
RectTransform rt;
// Start is called before the first frame update
void Awake()
{
chatText = GetComponent<Text>();
rt = GetComponent<RectTransform>();
}
// Update is called once per frame
void Update()
{
//preferredHeight 가 변경이 되면
if (rt.sizeDelta.y != chatText.preferredHeight)
{
//height 맞춰주자
rt.sizeDelta = new Vector2(rt.sizeDelta.x, chatText.preferredHeight);
}
}
public void SetText(string chat)
{
//텍스트 셋팅
chatText.text = chat;
//rt.sizeDelta = new Vector2(rt.sizeDelta.x, chatText.preferredHeight);
}
}
Photon Voice 맛보기
포톤 홈페이지로 가서, 어플리케이션을 하나 더 만들자
Photon 종류를 Voice 로 하고,.
이름 => XR_B
작성 (Create) 고고!
그리고 이제 에셋 스토어 ㄱㄱ
이거 다운 받고 Import 시키자!
그러면 이제
Photon -> PhotonUnityNetworking -> Resources -> Photon Server Settings 에
여기 이제 App Id Voice 도 들어갔을 거임
여기에 아까 만든거 AppID 넣자!
Dashboard 누르면 그 홈페이지도 들어가짐!
PhotonVoice -> Demos ->DemoVoicePun-Scene 으로 들어가면
귀여운동물들이 나오는 씬이 있다.
여기 들어가서 보면, 저 캐릭터 에게 붙어져있는 Audio Sourcee 에 의해서 보이게 됨
여기서 보면, Use Primary Recorder 체크 되어있으면 자동으로 Recorder 되는거고
그리고 밑에 Voice 오브젝트 에 보면,
옆에 Transmit Enable 이 있는데,
배그같이 키 눌렀을 때만 들리게 하고싶으면
키 눌렀을 때만 저게 체크 되게 하면 됨.
또한 밑에 내려가보면 위에 나오는 사진 처럼, Microphone Type 에서 Unity 로 하면
Unity 상에서 목소리가 살짝 변조(?) 되서 나옴
'Photon' 카테고리의 다른 글
Photon 마지막 날 (Photon Voice, Photon 소유권 넘기기, Map Editor(맵 툴 제작)) (2) | 2022.09.23 |
---|---|
Photon 아홉째 날 (포톤채팅구현, 채팅 동기화 후 나오는 문제 해결, 닉네임 색 바꿔주기) (1) | 2022.09.20 |
Photon 여덟째 날(Password 생성, SpawnPoint 생성, 턴 형식 만들기) (2) | 2022.09.16 |
Photon 일곱째 날 (void CreateRoomListUI(), 방정보 Custom 하기, Map Thumbnail 만들기, 상황별 정리) (0) | 2022.09.15 |
Photon 여섯 째날 (LobbyScene 꾸미기, RemoveRoomListUI(),UpdateRoomCache()) (1) | 2022.09.14 |