LIFF(LINE Front-end Framework)에서 TIC-80을 작동시켜보자

안녕하세요. LINE Fukuoka에서 LINE 메신저 앱 개발을 담당하고 있는 안드로이드 엔지니어 Seisuke입니다. 지난달(2018년 11월)에 열린 사내 해커톤에서 Fantasy Console이라는 다소 낯선 장르의 제품인 ‘TIC-80‘을 LIFF에서 작동시켜 보았는데요. 여기서 얻은 지식을 혼자 알고 있기는 아까워 블로그를 작성하게 되었습니다. 

LINE Fukuoka에서는 매년 해커톤을 실시하고 있습니다(2017년도 해커톤 실시 후기(일본어)). 기본적으로 몇몇 발표자가 개발 기획을 내놓으면 그 기획에 흥미를 느낀 4~5명이 팀을 구성해 이틀에 걸쳐 개발을 진행하는 형식입니다. 해커톤에서는 평상시 업무에서는 잘 사용하지 않는 기술을 시도해 보는 팀도 많이 있는데요. 저는 WebAssembly를 사용해 LIFF에서 TIC-80을 작동시켜보는 기획을 진행하게 되었습니다. 

TIC-80, 그리고 Fantasy Console이란?

TIC-80는 C 언어로 만든 Fantasy Console제품이며, Fantasy Console은 번역하면 ‘가상의 게임기’라는 뜻입니다. 얼마 전에 Python으로 만들어진 pyxel이 화제가 된 바 있지요.  
우선, 왜 ‘가상’이라고 하는지부터 살펴볼까요? 여기서 ‘실제’에 해당하는 것은 MSX나 MS-DOS 같은 싱글태스킹 OS를 탑재한 가정용 컴퓨터입니다. 제 세대의 이야기는 아니지만 이런 컴퓨터에서 BASIC 같은 것으로 게임 만들다가 프로그래밍 세계에 입문한 분도 꽤 되지 않을까 싶네요. Fantasy Console은 실제 컴퓨터를 에뮬레이션하는 것이 목적이 아닙니다. 게임 제작에 집중할 수 있는 ‘환경’ 구축을 목적으로 개발되었습니다. 따라서 옛날 컴퓨터처럼 해상도, 표시색 수, 음원 수 등이 제한된 환경과 함께 그 제약에 맞는 API, 프로그래밍 코드 편집기, 도트 찍기 도구, 음원 제작 도구가 제공됩니다. 또 게임 플레이 환경과 제작 환경이 하나로 묶여서 제공되기 때문에 내가 하는 게임이 어떤 코드와 소재로 만들어졌는지 그 자리에서 바로 확인할 수 있습니다. TIC-80에서는 보통 프로그래밍에 Lua를 사용하는데요. JavaScript나 Python 등 다른 여러 가지 언어로도 개발할 수 있습니다. 

다음은 TIC-80의 사양 중 일부를 발췌한 것입니다. 보시는 바와 같이 최소한의 사양입니다. 

  • 화면 사이즈 : 240×136픽셀
  • 스프라이트 : 16색 8×8픽셀의 전면 스프라이트와 배경 스프라이트 각각 256개
  • 음원 : 4채널 16개 파형의 조합 
  • 코드 사이즈 : 64KB까지

위와 같은 사양이기 때문에 ‘cart’라고 불리는 게임 자체의 사이즈도 아주 작은 편입니다. 따라서 LIFF에서 게임을 플레이할 수도 있고 직접 만든 cart를 LINE 친구들이 플레이할 수 있도록 공유하는 것도 쉽습니다. 이러한 점이 Fantasy Console의 목적에 잘 부합한다고 판단하여 해커톤 테마로 삼게 되었습니다.

서비스의 개요

Sinatra로 만든 간단한 웹앱을 Heroku로 운영했습니다. TIC-80의 콘솔에서 cart를 업로드할 수 있습니다. 업로드가 끝나면 채팅 화면에 다운로드 URL이 표시되는데요. 이를 친구에게 공유하면 그 친구는 LINE의 LIFF에서 게임을 플레이할 수 있습니다. 

LIFF에서 TIC-80 작동

LIFF는 LINE 메신저 앱에서 작동하는 웹앱 플랫폼으로, 채팅 화면에서 LINE 사용자 ID 등을 연동할 수 있습니다. TIC-80는 원래 WebAssembly로 빌드할 수 있도록 되어 있어 일단 브라우저에서 작동시킬 수는 있습니다. 하지만 모바일 브라우저까지 고려된 것은 아닙니다. 그리고 게임을 플레이할 때는 실제 게임 패드나 키보드를 연결해서 하는 편이 좋습니다. 따라서 LIFF에서 TIC-80를 편하게 조작하려면 다음 몇 가지 사항에 주의해야 합니다. 

화면 폭

WebAssembly에서는 실행 시 화면이 지정된 캔버스에 그려지는데요. 이 캔버스의 크기는 기존 바이너리(binary) 디스플레이 크기가 자동으로 적용되는 모양입니다. TIC-80은 설정에서 도트(dot) 하나의 크기를 변경할 수 있는데요. 가로 폭이 512픽셀(240×2픽셀 + 좌우 여백 16×2픽셀)이 되도록 설정하고 viewport를 다음과 같이 512px로 설정했습니다.

<meta name="viewport" content="width=512, user-scalable=0">

사실 TIC-80에서 아예 여백 자체를 없애고 모든 디스플레이 설정을 CSS로 하는 것이 맞을 것 같습니다.

게임 패드 구현

TIC-80에 가상 게임 패드 기능이 있는 것 같기는 하나, WebAssembly에서는 작동되지 않도록 설정되어 있습니다. 이 부분은 깊이 파고 들어갈 것 없이 JavaScript로 가상 게임 패드를 구현해서 LIFF에서 게임을 실행할 수 있게 만들었습니다. 

window.dispatchEvent(new KeyboardEvent(name,{'keyCode':code}));

위와 같이 일반적인 KeyboardEvent를 실행해도 키 입력이 TIC-80에 잘 전달됩니다. 다만 게임에서는 버튼 길게 누르기, 동시 누르기 등의 기능이 확실히 처리돼야 하므로 구현할 때 조금 신경쓸 필요가 있습니다. 정말 쾌적하게 게임을 즐기려면 비스듬히 누르기도 할 수 있어야 하지만 이번에는 그냥 넘어가기로 했습니다.

키보드 입력

TIC-80에서는 다른 많은 오픈소스 멀티플랫폼 게임 프로그래밍처럼 SDL2를 이용해서 키보드 입력을 처리합니다. 앞서 적은 게임 패드도 마찬가지입니다. TIC-80에서 키보드 입력은 별도 프레임으로 키보드 숨기기, 키보드 보이기를 받는다는 전제로 코딩되어 있습니다. 하지만 iPhone 등의 가상 키보드는 숨기기와 보이기가 순간적으로 발생하기 때문에 키보드 입력을 정상적으로 받을 수 없었습니다. 어쩌다 프레임 사이에 걸쳐 있을 때만 키보드 입력을 받을 수 있었습니다. 이 부분은 TIC-80 자체를 수정해서 해결했습니다. 

스페이스키 처리

스페이스키 입력이 들어오면 LIFF가 자체적으로 스크롤해 버리기 때문에 입력되지 않도록 JavaScript로 차단해야 합니다.

WebAssembly와 JavaScript 연결

WebAssembly에서는 C와 JavaScript에서 서로 함수를 실행해 연결할 수 있습니다(참고). 이번에는 C에서 JavaScript를 호출하는 것만으로도 충분했기 때문에 C 안에서 Inline JavaScript 작성이 가능한 EM_JS를 사용했습니다. LIFF의 웹앱에서 글로벌한 JavaScript 함수를 준비해 두면 C 언어에서 이를 손쉽게 호출할 수 있습니다. 

Cart를 업로드하는 기능은 TIC−80 명령어로 실행하는데요. 그 내용을 보면 사실 해커톤에서 작성한 서버 API를 JavaScript로 호출한 것이 전부입니다. 일반적인 XMLHttpRequest를 사용하며 API 호출에 성공하면 업로드한 게임을 다운로드하는 URL을 반환합니다. 또한 Callback 함수로 LIFF의 기능을 사용하여 대화 상에 메시지를 표시하는데요. Callback 함수를 호출하는 데까지는 WebAssembly의 Inline JavaScript로 구현하고 LIFF의 메시지 표시 기능은 웹앱에 구현했습니다.

앞으로의 전망

주어진 시간은 이틀 뿐. 그 안에 어떻게든 최소한의 기능을 구현해내야 하는 상황 속에 해커톤을 마쳤습니다. 이걸 조금 더 손보면 재미있는 작품이 만들어지지 않을까 하는 생각이 들더군요. 이번에는 선보이지 못했지만 ‘LINE 사용자 ID를 이용한 득점 업로드 기능’이나 ‘순위 매기기 기능’ 같은 아이디어도 있었습니다.  LIFF로 수집한 LINE 사용자 ID를 TIC-80을 실행할 때 커맨드라인 인자로 전달할 수 있는데요. 그런 다음 TIC-80의 API로 제공하고 게임 제작자가 필요한 파라미터를 전송, 실행하면 서버에 순위 데이터가 축적되는 방식입니다.

제가 생각하기에 Fantasy Console은 프로그래밍에 흥미를 느낀 아이들이 Scratch 같은 비주얼 프로그래밍을 체험한 뒤 그 다음 단계로 도전해 볼 만 한 것 같습니다. 이를 위해서는 다국어 지원이나 기능 간소화 등이 뒷받침되어야 하겠지요. 저도 TIC-80에 일본어 도트 폰트 등 여러 가지 기능을 추가해 보고 있는데요. 어느 정도의 기능은 구현할 수 있지만 기본 UI 특성상 사후에 추가하기에는 어려운 부분도 많네요. 이번에 해커톤 참가를 고려하면서 ‘Kotlin/Native와 WebAssembly로 뭐 해볼 만한 것 없을까’하고 알아보다가 TIC-80으로 결정하게 되었던 건데요. 다음에는 Kotlin/Native로 이상적인 Fantasy Console에 도전해 보는 것도 재미있겠다는 생각이 듭니다.