→💡 해당블로그는 "Unity엔진과 Chat gpt를 활용한 멀티플레이 환경 구성 과정"에서 학습한 내용을3달이 지난 지금에서야복습 겸 스파르타 유니티 과제에 적용시키는 과정을 담았습니다.
1. 멀티플레이 환경
2. 포톤 기본 세팅
3. 포톤 연결 / 로비와 방 생성
4. 플레이어 생성
5. Agora를 사용한 실시간 카메라 연결
해당 포스터는 5번: Agora를 사용한 실시간 카메라 연결부터 작성되었습니다. 전 내용이 궁금하신 분들은 이전 블로그를 확인해 주세요.
이전 글 보러 가기 >
2025.02.19 - [Unity] - [Unity][멀티플레이] #1. Photon을 유니티에 연동해 보자
[Unity][멀티플레이] #1. Photon을 유니티에 연동해보자
💡 해당블로그는 "Unity엔진과 Chat gpt를 활용한 멀티플레이 환경 구성 과정"에서 학습한 내용을3달이 지난 지금에서야 복습 겸 스파르타 유니티 과제에 적용시키는 과정을 담았습니다. 1.
youcheachae.tistory.com
2025.02.20 - [Unity] - [Unity][멀티플레이] #2. 플레이어 생성, 동기화
[Unity][멀티플레이] #2. 플레이어 생성, 동기화
💡 해당블로그는 "Unity엔진과 Chat gpt를 활용한 멀티플레이 환경 구성 과정"에서 학습한 내용을3달이 지난 지금에서야 복습 겸 스파르타 유니티 과제에 적용시키는 과정을 담았습니다. 1. 멀
youcheachae.tistory.com
🎨5. Agora를 사용한 카메라 연결
- Agora란?
□ RTC(Real Time Communication)의 플랫폼
□ 음성통화/화상통화/실시간 방송 등 실시간 대화형 기능을 웹, 모바일, 데스크톱에서 쉽게 사용할 수 있게 해 준다
- 패키지 다운로드
https://docs.agora.io/en/sdks?platform=unity
SDKs Download | Agora Docs
Description will go into a meta tag in <head />
docs.agora.io
□ 해당 링크에서 Unity 선택 후 Video Sdk를 다운로드한다.
□ 버전은 4.4.0을 사용하였다.
□ 압축을 푼 후 파일을 드래그해서 Asset 폴더에 넣은 후 Import 한다.
- App Id 받아오기
□ Agora 홈페이지에서 로그인 후 Projects → Create New
□ 프로젝트의 App Id를 복사한다.
- VideoChatManger.cs
public class VideoChatManger : MonoBehaviour
{
[Header("===Data===")]
[SerializeField] private string _appID = "85636c045ba54ceda8cee9c505f35e5b";
[SerializeField] private string _token = "";
[SerializeField] private string _currentChannelName;
[Header("===오브젝트===")]
static public GameObject _videoChatObject; // raw 이미지 프리팹
static public Transform _videoChatLayout; // 프리팹 담길 상위 부모
[Header("===아고라 인스턴스===")]
static public IRtcEngine rtcEngine;
}
□ 복사한 Agora 프로젝트의 Id를 변수로 할당한다.
private void SetUpVideoRtcEngine()
{
// 아고라 인스턴스 생성
rtcEngine = Agora.Rtc.RtcEngine.CreateAgoraRtcEngine();
// 세팅
RtcEngineContext context = new RtcEngineContext();
context.appId = _appID;
context.channelProfile = CHANNEL_PROFILE_TYPE.CHANNEL_PROFILE_LIVE_BROADCASTING;
context.audioScenario = AUDIO_SCENARIO_TYPE.AUDIO_SCENARIO_DEFAULT;
context.areaCode = AREA_CODE.AREA_CODE_GLOB;
// rtc 엔진 초기화
rtcEngine.Initialize(context);
Debug.Log("아고라 엔진 셋업");
}
□ AppId를 통해서 RTC 엔진을 초기화 세팅한다.
private void SetVideoConfiguration()
{
rtcEngine.EnableAudio();
rtcEngine.EnableVideo();
VideoEncoderConfiguration config = new VideoEncoderConfiguration();
// 화면 영역
config.dimensions = new VideoDimensions(200, 200);
config.frameRate = 15;
config.bitrate = 1000;
rtcEngine.SetVideoEncoderConfiguration(config);
rtcEngine.SetChannelProfile(CHANNEL_PROFILE_TYPE.CHANNEL_PROFILE_COMMUNICATION);
rtcEngine.SetClientRole(CLIENT_ROLE_TYPE.CLIENT_ROLE_BROADCASTER);
Debug.Log("아고라 비디오 셋업");
}
□ 비디오 세팅 메서드
□ EnableVideo();
■ 비디오 모듈을 활성화시키는 함수
■ 채널에 가입하기 전 or 채널에 있는 동안 호출할 수 있다
private void InitEventHandler()
{
UserEventHandler handler = new UserEventHandler(this);
rtcEngine.InitEventHandler(handler);
Debug.Log("아고라 유저 이벤트 핸들러 셋업");
}
internal class UserEventHandler : IRtcEngineEventHandler
{
readonly VideoChatManger _chatManger;
internal UserEventHandler(VideoChatManger chatManger)
{
_chatManger = chatManger;
}
// 채널에 접속 성공시 출력 오버라이딩
public override void OnJoinChannelSuccess(RtcConnection connection, int elapsed)
{
Debug.Log($"You Joined Channel : " + connection.channelId);
}
public override void OnUserOffline(RtcConnection connection, uint remoteUid, USER_OFFLINE_REASON_TYPE reason)
{
// 함수 추가 예정
}
public override void OnUserJoined(RtcConnection connection, uint remoteUid, int elapsed)
{
// 함수 추가 예정
}
// 로컬 비디오 스트림 상태 변경 콜백
public override void OnLocalVideoStateChanged(VIDEO_SOURCE_TYPE source, LOCAL_VIDEO_STREAM_STATE state, LOCAL_VIDEO_STREAM_REASON errorCode)
{
Debug.Log($"로컬 비디오 상태: {state}, 에러 코드: {errorCode}");
switch (state)
{
case LOCAL_VIDEO_STREAM_STATE.LOCAL_VIDEO_STREAM_STATE_CAPTURING:
Debug.Log("카메라 캡처 중 - 카메라가 정상적으로 활성화되었습니다.");
break;
case LOCAL_VIDEO_STREAM_STATE.LOCAL_VIDEO_STREAM_STATE_STOPPED:
Debug.Log("카메라 캡처가 중지되었습니다.");
break;
case LOCAL_VIDEO_STREAM_STATE.LOCAL_VIDEO_STREAM_STATE_FAILED:
// 오류 코드별 상세 처리
if (errorCode == LOCAL_VIDEO_STREAM_REASON.LOCAL_VIDEO_STREAM_REASON_DEVICE_BUSY)
{
Debug.LogError("카메라 장치가 이미 사용 중입니다.");
}
else if (errorCode == LOCAL_VIDEO_STREAM_REASON.LOCAL_VIDEO_STREAM_REASON_DEVICE_NO_PERMISSION)
{
Debug.LogError("카메라 접근 권한이 없습니다.");
}
else
{
Debug.LogError($"카메라 캡처 실패! 상세 오류 코드: {errorCode}");
}
break;
}
}
}
□ 이벤트핸들러 작성
□ IRtcEngineEventHandler를 상속받아야 한다
□ 채널에 접속 성공 시 / 유저가 채널에서 떠날 / 유저가 채널에 입장했을 때 / 로컬 비디오 스트림 상태가 변화 콜백시 출력
- Start()
private void Start()
{
// rtc 엔진 생성, 초기화
SetUpVideoRtcEngine();
// 비디오 셋업
SetVideoConfiguration();
// 이벤트 핸들러 셋업
InitEventHandler();
}
: 각 초기화 함수를 Start에서 실행해 준다
📝 채널 입장
// 해당 채널에 들어오면
public void Join(string channelName)
{
// 현재 채널 이름 저장
_currentChannelName = channelName;
rtcEngine.JoinChannel(_token, channelName, 0, null);
rtcEngine.EnableVideo();
// 채널에 뷰 생성
string _cameraName = PlayerManager.Instnace.nowPlayer.PlayerName;
MakeVideoView(0, channelName);
Debug.Log($"{channelName} 의 채널에 조인 ");
}
□ 채널이름으로 해당 채널에 입장
□ 카메라 뷰 생성
// 뷰 생성
static public void MakeVideoView(uint uid, string channelId = "")
{
VideoSurface videoSurface = MakeImageSurface(uid.ToString());
// null이면 return
if (ReferenceEquals(videoSurface, null))
return;
// 로컬 , 리모트 유저 뷰 설정
if (uid != 0)
{
videoSurface.SetForUser(uid, channelId);
}
else
{
// 로컬일때
videoSurface.SetForUser(uid, channelId, VIDEO_SOURCE_TYPE.VIDEO_SOURCE_CAMERA_PRIMARY);
}
videoSurface.SetEnable(true);
}
// 비디오 서페이스 생성
static public VideoSurface MakeImageSurface(string name)
{
// raw 이미지 오브젝트 생성
GameObject videochat = Instantiate(_videoChatObject);
if (videochat == null)
return null;
// 오브젝트 이름 설정
videochat.name = name;
if (_videoChatLayout != null)
{
// 비디오 레이아웃 밑에 넣기
videochat.transform.SetParent(_videoChatLayout, false);
}
else
{
Debug.Log("Layout is null");
}
videochat.transform.localPosition = Vector3.zero;
var videoSurface = videochat.AddComponent<VideoSurface>();
return videoSurface;
}
□ MakeVideoView()
■ 로컬 사용자일 때(uid == 0)
□ 기본적으로 카메라를 사용하게 설정
□ MakeImageSurface()
■ raw 이미지 오브젝트를 생성 후
■ VideoSurface추가
📝 채널 퇴장
// 해당 채널에서 나가면
public void Leave()
{
rtcEngine.DisableVideo();
rtcEngine.LeaveChannel();
// 생성된 뷰 삭제
DestoryAll();
Debug.Log($"{_currentChannelName} 의 채널에서 퇴장 ");
}
□ 채널에서 나가면 현재 생성되어 있는 뷰를 삭제
static public void DestoryAll()
{
List<uint> uids = new List<uint>();
for (int i = 0; i < _videoChatLayout.transform.childCount; i++)
{
uids.Add(uint.Parse(_videoChatLayout.transform.GetChild(i).name));
}
for (int i = 0; i < uids.Count; i++)
{
DestoryVideoView(uids[i]);
}
}
static public void DestoryVideoView(uint id)
{
var obj = FindChild(UiManager.Instnace._canvas.transform, id.ToString()).gameObject;
if (!ReferenceEquals(obj, null))
Destroy(obj);
}
static public Transform FindChild(Transform parent, string name)
{
Transform result = parent.Find(name);
if (result != null)
return result;
// 자식 오브젝트 모두 검색
foreach(Transform child in parent)
{
result = FindChild(child, name);
if (result != null) return result;
}
return null;
}
□ 카메라 layoutGroup하위에 있는 카메라들의 이름을 저장 한 뒤, canvas 하위에서 해당 오브젝트를 찾아 Destory()
📝 특정 채널에 입장했을 때
2025.02.20 - [Unity] - [Unity][멀티플레이] 게임에서 충돌 시 동기화 문제
[Unity][멀티플레이] 게임에서 충돌 시 동기화 문제
1. 구현 동작2. 문제점3. 해결과정 □ 어디서 문제가 발생하는가? □ GetHashCode()로 해결가능4. 해결 방법 □ PhonView의 IsMine □ 최종 코드5. 결론 📝 1. 구현 중인 동작 □ zep처럼 특
youcheachae.tistory.com
: 해당글의 ConferenceBox.cs
■OnTriggerEnter2 D
□ VideoChatManger.cs의 Join() 함수 실행
■ OnTriggerExit2D
□ VideoChatManger.cs의 Leave() 함수 실행
📝 현재까지의 문제점
: Join() 함수 내부의 MakeVideo() 함수에서 uid를 항상 0으로 넘기고 있기 때문에 , 생성되는 Videosurface 오브젝트의 이름은 항상 0이다
: DestoryAll에서 오브젝트의 이름을 가지고 Destroy()하는 부분에서 오류가 생긴다.
: 다른 유저가 채널에 입장했을 때, 즉 리모트 유저가 채널에 입장했을 때는
그 유저의 고유 아이디를 가지고 VideoView를 만들어야 한다.
- 해결방안
: UserEventHandler 클래스의 UserEventHandler에서 오버라이딩 한 메서드에서 MakeView()를 추가해 준다.
public override void OnUserOffline(RtcConnection connection, uint remoteUid, USER_OFFLINE_REASON_TYPE reason)
{
// 코드 추가
DestoryVideoView(remoteUid);
}
public override void OnUserJoined(RtcConnection connection, uint remoteUid, int elapsed)
{
// 코드 추가
MakeVideoView(remoteUid, _chatManger._currentChannelName);
}
: 이렇게 하게 되면 리모트 유저가 채널에 입장했을 때, 리모트 유저의 고유 아이디로 View를 만들게 된다.
📝 최종 영상
: 현재 카메라는 동기화가 되지 않고 있다.
: 이 부분은 추후 추가해 보겠다.
- Agora 공식 문서
https://docs.agora.io/en/video-calling/get-started/get-started-sdk?platform=unity
Video Calling Quickstart | Agora Docs
Rapidly develop and easily enhance your social, work, and educational apps with face-to-face interaction.
docs.agora.io
- Agora의 메서드
https://api-ref.agora.io/en/video-sdk/unity/4.x/API/class_irtcengine.html#api_irtcengine_enablevideo
IRtcEngine
Preloads a channel with token, channelId, and userAccount. public abstract int PreloadChannel(string token, string channelId, string userAccount); When audience members need to switch between different channels frequently, calling the method can help short
api-ref.agora.io
깃허브
https://github.com/kimYouChae/Sparta_Metaverse
GitHub - kimYouChae/Sparta_Metaverse
Contribute to kimYouChae/Sparta_Metaverse development by creating an account on GitHub.
github.com
'Unity' 카테고리의 다른 글
[Unity] Audio Clip, Source, Mixer / 오디오 볼륨 조절 슬라이더 (0) | 2025.02.26 |
---|---|
[Unity] Csv To Json 변환 툴 (0) | 2025.02.25 |
[Unity][멀티플레이] #2. 플레이어 생성, 동기화 (0) | 2025.02.20 |
[Unity][멀티플레이] 게임에서 충돌 시 동기화 문제 (0) | 2025.02.20 |
[Unity][멀티플레이] #1. Photon을 유니티에 연동해보자 (0) | 2025.02.19 |