진행하다 말다를 반복하는 웹빌드에서 웹소켓 사용하는 작업 메모입니다.
이것이 구현된다면 웹 브라우저로 온라인 유니티 게임을 만들 수 있습니다.
웹소켓은 실시간 통신이 된다는 것을 의미합니다.
기술적으로는 가능하며 제대로된 결과물을 이번엔 만들어야겠다고 생각했습니다.
플레이 데모 영상: https://www.youtube.com/watch?v=wHJ6zOM5Zuw
웸브라우저를 두개 띄워 웹소켓 버서와 통신해서 서로 화면이 잘 맞는지 확인하는 영상입니다.
싱크가 잘맞군요 결과물이 좋으니 개발당시 괴로웠던 시간이 잊혀지는군요
(잘가라 괴로워하던 과거의 나)
유니티 참고 문서: https://docs.unity3d.com/kr/2018.4/Manual/webgl-interactingwithbrowserscripting.html
이 문서는 유니티 웹 빌드가 자바스크립트 함수를 호출하는 방식을 설명하고 있습니다.
jslib 파일에 대애 알려주는데 Plugins/아무이름.jslib 에 두시면 됩니다.
개발 환경 매우 까다롭다.
웹 빌드는 빌드 시간이 오래걸립니다.
에디터 상태에서는 jslib 는 동작하지 않기 습니다.
에디터에서는 c# 코드로 websocket-sharp 을 이용해서 통신을 하면 되고 확인도 쉽습니다.
빌드 한다음은 jslib 를 이용해 javascript 로 웹소켓 통신을 하고
결과를 유니티로 전달해야합니다.
또한 빌드가 끝나고 자동으로 뜨는 결과물 페이지에서는
unityInstance 가 없기 때문에 SendMessage 를 이용해 유니티로 결과를 전달할 수가 없습니다.
이 부분때문에 index2.html 같은걸 만들어서
별도의 node.js 서버에서 웹서버를 띄워서 SendMessage 를 사용할 수 있도록 해야합니다.
개발이 불편한 환경입니다.
개발 환경 자세히
c# 에서 websocket 통신을 하려면
github: https://github.com/sta/websocket-sharp
websocket-sharp 이 폴더를 유니티에 붙여 넣으면 됩니다.
이 c# 소스는 node.js 웹서버중 socket.io 랑 맞는게 아니고 ws 랑 맞는 방식입니다.
웹 소켓 서버 / node.js + ws
웹 소켓 서버는 node.js 의 ws 를 이용했습니다.
socket.io 랑은 안맞습니다.
socket.io 쓰겠다면 c# 코드는 별도로 찾아보셔야 합니다.
에니터에서는 개발이 쉽습니다.
웹소켓 서버를 https (wss) 로 띄우는건 난이도가 있었습니다.
몇개의 페이지와 설명에서 잘못된 코드가 상위에 나오는 경우가 있었습니다. 이것 때문에 시간낭비가 꽤 있었습니다.
공유기의 DDNS 와 letsencrypt 를 이용해 인증을 받아 사용했습니다.
https 띄우기전 인증서를 넣을때
privatekey.pem 를 key 넣고
fullchain.pem 을 cert 에 넣어서 성공했습니다.
잘못된 코드들은
ca 를 사용하는 코드
funnchain.pem 이 아니고 chain.pem 을 사용하는 코드들은 동작이 실패했습니다.
정확히 말하자면 https 는 동작했는데
wss 통신이 실패하는 경우 였는데
상황이 복잡해서 많은 시간을 낭비했습니다.
인증 문제는 잘못 되어도 별다른 힌트를 알 수 없었습니다.
서버가 죽는 것도 아니고 건 아니고 응답이 없기 때문 입니다.
.jslib 파일
jslib 파일은 결국은 브라우저에서 동작하는 자바스크립트 코드가 있는 파일입니다.
console.log 로 로그를 찍을 수 있으며
var ws = new WebSocket 을 사용해 웹소켓 클라이언트도 구현할 수 있었습니다.
프라우저에서 F12 눌러서 콘솔듸워 코딩할 수 있는 부분은 다 된다고 보면 된다.
개발 팁 1. jslib 파일을 직접 코딩하지말고 먼저 .js 파일로 동작을 확인한 다음 옮기자
.jslib 에서 문제가 생기면 문제를 찾는것과 고치고 확인하는 과정에서 시간낭비가 심합니다.
vscode 에서 js 파일로 먼저 동작을 확인해보는 것이 좋습니다.
테스트 까지 끝나면 jslib 에 코드를 복사-붙여넣기 하는 식으로만 사용했습니다.
vscode 에서 .js 파일로 코딩하면 문법 확인, 들여쓰기도 자동으로 되서 편합니다.
.jslib 에서 바로 코딩을 시작하면
코드 문법을 검사를 할 수 없고
색깔로도 구분이 안되며
들여쓰기하는 시간도 걸립니다.
이런일들은 시간낭비를 크게 발생시킵니다.
반드시 .js 파일에서 미리 만들어 확인하고
이왕이면 브라우저에서도 확인한 다음에
.jslib 파일을 작성하도록 합시다.
개발 팁 2. .jslib 함수내부에서는 try - catch 로 감싸야합니다.
위에 이미지 보면 try 코드가 보인다 이렇게 전체를 감싸야합니다.
jslib 내부에서 예외가 발생하면 에러를 읽기가 너무 어렵습니다.
개발 팁 3. 거의 모든 줄에 로그를 넣읍시다.
.jslib 파일에서 문제가 발생하면 빌드 시간 때문에 시간낭비가 심합니다.
어디서 에러가 나는지 한번에 알 수 있도록 로그를 빽빽히 넣는 편이 좋습니다.
성공하고나면 지우더라도 개발중엔 이렇게 해야합니다.
빌드시간 너무 깁니다.
개발 팁 4...jslib 의 함수는 리턴을 받을 수 없었다.
자바스크립트 -> 유니티로 데이터를 받으려면 SendMessage 를 이용해 받는 방법밖에 없었습니다.
함수에서 return 을 이용해 값을 받으려고 해도 유니티에서 읽지를 못했습니다.
위에서도 설명했지만 SendMessage 를 사용하려면 unityInstance 가 필요한데
기본빌드에서는 이 전역 변수가 없습니다.
전역 변수를 뽑기위한 과정때문에 또 하나의 절차가 유발 됩니다.
브라우저의 javascript 에서 유니티로 데이터 보내기 SendMessage
함수 리턴은 받을 수 없었고 받을 수 있다고 해도
통신 데이터를 받아야하는데
어차피 즉시 리턴 받는 기능을 쓸일은 없습니다.
그래서 SendMessage 를 이용해 콜백을 해야하는데
window.unityInstance.SendMessage(
"@WebSocket",
"jslib_callback",
"onopen"
);
jslib 에 있는 콜백 코드 입니다.
유니티의 @WebSocket 오브젝트에 jslib_callback("onopen") 을 호출해주는 코드 예제입니다.
접속 되면 콜백 해주는 코드인데 문제는
window.unityInstance
기본 빌드에서는 이 변수가 없습니다.
참고로 window 는 브라우저의 전역변수를 보관하는 변수입니다.
빌드에 나오는 index.html 매번 새로 만들어지기 때문에 여기다 코딩을 하면 날아갑니다.
index2.html 로 파일을 복사해서 거기서 편집하는 것이 좋습니다.
아래의 그림을 확인해서 then 안에 값을
window. unityInstance = unityInstance;
저장해두세요.
유니티 로드가 끝나면 then 이 호출 됩니다.
전역변수로 보관해두고 jslib 에서 사용하면 문제가 없습니다.
이왕이면 유니티 측에서 빌드될때 강제로 좀 window 에 넣어달라고 요청해볼 생각입니다..
그래서 빌드 후 자동으로 뜨는 결과물 창은 쓸모가 없습니다.
결과물을 별도로 돌릴 웹 서버를 node.js 로 짜두면 편할 것입니다.
이전에 작성해둔 유니티 웹 (br 과 gz 을 지원하는) express 서버 띄우기글이 있습니다.
이글은 압축방식을 지워하기 위해 제작하였는데 테스트하는데 많은 도움이 되었습니다.
링크 : https://serverdown.tistory.com/1225
vercel 로 express 서버를 배포해보자 / 유니티 webgl 압축 지원하는 페이지를 배포 가능
몇일전...vercel 불편하다고 징징한 글을 올렸었는데요과거의 징징글: vercel 처음 써보고 분노한 개발자 사용기 / 배포 deploy / setInterval 안됨새로운 가능성 발견투자한 시간이 아까워 더 찾아봤습니
blog.sidnft.com
저는 개발할 당시 웹소켓 서버 따로 유니티웹 띄우는 서버 따로 해서 동시에 두개 돌리고있었는데
지금 생각해보니 한 서버에 둘다 돌려도 되는거 였는데
당시엔 피곤해서 그 생각을 못했었습니다.
이 환경이 갖춰지면 테스트하기엔 충분한 환경이 갖춰졌습니다.
데모 페이지
SIDNFT's Web Game Demo
unity.sidnft.com
이 페이지에 가면 Multiplay Demo 링크가 있습니다..
입장 버튼을 크게 키워야 겠군요. 아주 작네요
사람이 없을테니 브라우저 창을 여러개 띄워서 클릭해보면 통신이 된다는 거을 알 수 있을 것이다.
1. 허공을 찍으면 내 박스가 그곳으로 이동합니다.
2. 내 박스를 클릭하면 숫자가 올라갑니다. 크기도 커집니다.
3. 다른 유저의 박스를 클릭하면 숫자가 내려갑니다. 크기도 작아집니다.
딱 이정도 기능만 구현했습니다.
Unity Play 에서는 ...
혹시나 해서 Unity Play 에서는 어떻게 되어있나 확인해봤습니다.
gameInstance = instance;
얘네들은 문제 점을 인식하고 있었군요.
전역변수에 값을 저장해두고 있었습니다.
이 변수를 사용할 수 있을꺼 같아서
추가 로 작업을 했는데 활용 가능했습니다.
Unity Play Multiplay Demo 링크: https://play.unity.com/en/games/ad4d2c84-f8bb-4c0e-9261-4703c1b9860d/mpdemo
mp_demo on Unity Play
mp_demo
play.unity.com
jslib 에 추가된 코드는 아래와 같습니다.
mergeInto(LibraryManager.library, {
jslib_call_action: function (_v) {
function jslib_callback(val) {
const go_name = "@WebSocket";
const func_name = "jslib_callback";
if (window.unityInstance) {
window.unityInstance.SendMessage(go_name, func_name, val);
} else if (window.gameInstance) {
window.gameInstance.SendMessage(go_name, func_name, val);
} else if (gameInstance) {
gameInstance.SendMessage(go_name, func_name, val);
} else {
console.error(
"SendMessage fail, need instance",
go_name,
func_name,
val
);
}
}
try {
const v = UTF8ToString(_v);
// console.log("jslib_call_action", v);
~~~~ (생략)
코드는 요런식으로 추가되었고
window.gameInstance 가 맞는지
gameInstance 가 맞는지 확신이 없어서 둘다 넣었습니다.
빌드를 두번하긴 싫어서 입니다.
되는지 확인했으니 Unity Play 적극적으로 활용해도 되겠습니다.