[번역] Angular.js로 멀티플레이 게임 만들기

NG-NEWSLETTER의
Building multiplayer games with Angular 번역글 입니다.

Angular로 멀티플레이어 게임 만들기

 

 Ari Lerner가 ngEurope에서 이 포스트의 내용으로 강연하였습니다. 강연 영상은 여기서 보실 수 있고, 전체 소스코드는 이 튜토리얼의 마지막에 있습니다. 강연때 사용된 슬라이드를 보시려면 여길 누르세요!

게임을 만들때 선택할 수 있는 방법은 굉장히 많죠.

 

선택할 프레임 워크에 따라 쉽게 만들수 있는 게임의 종류는 다양합니다. tic-tac-toe 부터 매직 더 게더링까지,  Angular로 만들 수 있는 게임도 한 무더기로 있어요. 카드게임, 트라비아 게임, 보드게임, 모바일 숨바꼭질같은 게임을 우리는 AngularJS로 만들 수 있지요.

 

당신이 Applebee’s (미국에 있는 유명한 식당 체인) 에 마지막에 갔을때를 떠올려 보세요, 여러 TV에서 interactive trivia game이 돌아가고 있었을거에요. SPA(Single Page App)를 쓰면, 이런건 반나절이면 만들 수 있죠. CSS3와 HTML5를 쓴다면, 웹 자체만으로 2D나 3D 구현이 가능하기 때문에, 다른 불필요한 애니메이션 라이브러리를 쓸 필요도 없어요.

 

라이브러리를 쓰는 대신 엘리먼트의 3D wipe를 실행하고 CSS3를 거기에 적용시키면 되죠. 그리고 나서, 우리는 단지 CSS Class만 추가해주면 되요.

 

DOM이 많이 있는 게임을 만드는건 어때요? AngularJS가 진짜로 많은 DOM 객체 조작에 쥐약일까요? 그럴지도요. Angular 1.x를 보면, DOM 객체들이 각각의 이벤트 핸들러를 가지고있어서 많은 DOM을 쓰는 게임은 좀 힘들어요. event loop가 많아지기 때문에, DOM 조작기반의 게임을 AngularJS로만 만드는 것은 좋은 방법은 아닌것 같군요.

 

그런데, 게임에는 다양한 요소들이 필요하죠. 예를 들면, 많은 게임에서는 스코어보드가 필요해요. 또는 HUDS나, 채팅창, 이벤트창, 멀티플레이어 정보, 고득점자 정보 창 같은 것들이 필요하죠. 게임 초대나 프리미엄 컨텐츠 접근에 쓰일 결제창은 또 어떻구요!

 

게임 라이브러리의 엘리먼트들은 최적화 되어있지 않거나 어렵고 복잡할 수도 있어요. 반면에, AngularJS같은 웹 어플리케이션 라이브러리는 이런 컴포넌트 만드는데는 제격이죠.

 

게임 라이브러리를 물리효과, 애니메이션, 음향 처리 등에 사용하고, container로는 AngularJS를 써볼게요.

 

오늘의 포스트에서는, 우리는 AngularJS가 사용된 게임을 만들어 볼 겁니다. AngularJS를 Front-End로, node/express를 Back-End로 사용하고,  phase.iosocket.io를 사용해서 back-end와 front-end를 실시간으로 통신할겁니다.

 

게임 진행 흐름은 아래와 같아요 :

 

그래픽적으로 보면.. :

Angular

 

이제 우리 게임의 AngularJS쪽 부분을 작업해봅시다. 게임의 컨테이너를 만들거에요.

 

우선, AngularJS 어플리케이션을 모듈 기반으로 만들어야해요. 그렇게 하면 우리 게임을 독립적으로 만들 수 있고, 공통 컴포넌트를 쉽게 공유할 수 있고, 특정 기능에 대한 테스트를 할 수 있기 때문이죠.

 

우리 어플리케이션을 만들기 위해, client-side package manager인 bower를 써서 angular를 당겨옵시다. (아니면 그냥 angularjs.org 에서 소스를 받으세요 :])

 

모듈 기반의 어플리케이션을 만들기 위해, 우리는 ‘환상적인’ ui-router를 AngularJS랑 같이 사용 할 필요가 있어요. 우선, bower를이용해서 ui-router를 설치합시다. (아니면 github에서 받으세요! :])

 

라이브러리를 다 준비했으면, index.html 에 연결해 줄 차례죠. 가서 index.html을 만듭시다. 아주 간단한 버젼은 다음과 같겠죠 :

 

여기선, APP 만드는데 필요한 기본적인 것들을 처리했어요.

우리는 :

  • ng-app directive를 body의 angular 모듈(우리가 app이라고 부를)이 위치할 곳에넣었어요.
  • angular, angular-ui-router, ng-game.js 파일을 불러왔어요.
  • ui-view directive를 body에 포함시켰어요.

만약 당신이 이런 의존성 설정에 생소하면, angular guide의 getting start를 보고 오세요!

 

index.html을 브라우저로 열면, 빈 페이지랑 콘솔에 뜬 에러만 보일거에요. 우리가 아직 APP 이라는 모듈을 정의하지 않았거든요. 가서 scripts/ 라는 폴더를 만들고 ng-games.js 파일을 넣읍시다.

 

이 파일에서 angular app을 정의해야 합니다. angular에서 제공하는 module() 함수를 써서 정의할게요.

 

그리고 ui-router를 사용하려면, module에 의존성 주입을 해야 합니다. 모듈 정의를 조금 바꾸어 볼게요 :

메뉴 만들기

 

모듈을 설정했으면, 게임 메뉴를 만듭시다. 이 메뉴는 현재 실행중인 게임의 리스트를 보여주고, 게임에 참가 할 수 있게 해줄거에요.

 

게임 메뉴를 만들기 위해, scripts/ 폴더 아래에 menu/ 라는 폴더를 만들고 index.js파일을 넣어 둡시다. 여기에 우리는 게임 메뉴 모듈을 만들거에요 :

 

새 모듈을 만들었으면, route를 만듭시다. 우리가 게임 만들때 ui-router가 필요한 이유를 잠시 설명하자면, 각각의 모듈이 자신만의 route를 쓰게할 것이기 때문이에요. 한 파일에 모든 route를 정의한, global routing table을 만드는 것보다, 각각의 모듈에서 자기네들의 route를 정의해서 쓰는게 더 좋아요.

 

위에서 설명한 장점을 마음속에 담고, 게임 모듈을 만들기 시작해 봅시다. route를 정의하기 위해, 우리는 모듈의 config() 함수를 쓸거구요, 여기에 uiRouter의 $stateProvider를 inject 해줄 겁니다 :

 

config() 함수 안에서, 우리는 첫번째 route를 정의해볼까요?:

 

이제 menu state를 만들었으니, menu state를 main application에 의존성을 갖게 해야되요. 우선, 우리는 index.html에서 game을 불러오게 해야겠지요 (아니면, CommonJS 같은 AMD-library를 사용하던지요!) :

 

그리고 마지막으로, 우리는 app.menu를 module에 의존성 주입합시다.

 

이제 브라우져에서 열어보면,재미없게도,  ‘ there is a menu here ‘ 를 볼 수 있을거에요. 이걸 좀 더 기능적으로 만들어볼까요?

 

menu state에서 template 을 설정하는 대신, 템플릿하고 우리의 첫 컨트롤러를 불러옵시다. scriptes/menu/index.js에서 templateUrl하고 controller를 포함시키게끔 menu state정의를 바꿔봅시다.

 

이제 브라우져에서 불러오면, 우리는 아마 2개의 에러를 볼 수 있을거에요. 첫째는 로딩과 관련된 에러에요. 우리가 얘를 불러올때, 파일에서 불러오면 안되고 서버에서 불러와야되요.

 

역자 주) 다 아시겠지만, Angular.JS는 웹 서버에서 불러와야지 작동합니다!

 

그냥 쓰고싶은 아무 서버나 쓰면 되는데요, 이건 튜토리얼이니까, 우리는 node-static server를 쓸게요. node-static을 쓰기 위해서는, 일단 node.js가 설치 되어 있어야 되구요, 아래의 npm command를 입력해서 node-static을 설치하세요 :

 

node-static이 설치되면, 우리는 index.html이 있는 폴더에서 static만 입력하면 간단히 서버를 실행 시킬 수 있지요. 지금은 이거 말고 아무것도 필요 없어요!

 

나중에는, 이걸 express로 대체할 거에요!

서버를 구동시키면, 우리는 http://loaclhost:8080을 쳐서 접속할 수있어요.

 

다시 돌아가서, 두번째 에러, MenuController라는 Controller를 찾을 수 없다는 에러가 나올거에요. 브라우져는 당연히 얘를 못 찾겠죠, 우리가 아직 안 만들었으니까! MenuController를 만들어 봅시다.

 

scripts/menu/menu.controller.js 파일을 만들고, app.menu 모듈에 Controller를 추가해 봅시다.

 

이제 이 파일을 index.html에다가 붙일게요. 아래처럼 수정합시다!

 

이젠,,,, 아무것도 안 보이지만…. 로딩은 됐으니까, menu를 만들면 되겠네요.

 

인터페이스를 만들기 전에, 어떤 인터페이스를 만들지 한번 생각해봅시다. 우선, 참가 가능한 게임의 목록을 보여줘야겠지요. 그리고, 유저가 게임을 새로 생성할 수 있게 해야겠죠.

 

우리한테 지금 보여줄 게임 (방)이 하나도 없으니까, 당분간은 이렇게 인터페이스를 해 놓읍시다.

 

뷰가 상당히 심플하죠? 우리는 Controller에 있는 rooms (지금은 없는데, 나중엔 생길거에요!) 에서 루프를 돌아서 패널에 그걸 출력할거에요. 또 우리는 Create a game 버튼을 만들었죠. 이 버튼을 누르면 (지금은 없지만 곧 만들) game.play state로 이동하게 될거구요.

 

새로운 state를 만들어야 되니까, game 모듈을 만들어봅시다. 우리는 app.menu 모듈 만들때랑 똑같은 순서대로 이 모듈을 만들거에요.

  1. scripts/ 폴더 밑에 game/ 이라는 폴더를 만들고 Index.js 파일을 넣습니다.
  2. index.html에서 scripts/game/Index.js 파일을 불러옵니다.
  3. main application에 새로운 모듈을 의존성 주입해 줍시다.

 

우선, scripts/games/Index.js 파일을 만들어봅시다.

 

그리고, index.html에서 이 새로운 파일을 불러옵시다.

 

마지막으로, 확실하게 main application에 의존성 주입을 합시다.

 

여러분께서 생각하고 계신지는 모르겠는데,  우리는 하나의 state만 게임 실행을 위해 사용할겁니다. 게임의 다른 부분에서는, 다른 state를 쓸건데, 일단 지금은 이 state에만 집중하자구요.

 

우리가 전에 menu state 만들때 한 것 처럼, config() 함수를 써서 game state를 만듭시다:

 

이제 game.play state를 game state의 자식 state로 만들거에요, 이렇게 계층식으로 (nesting) route를 짜면 좋은 점이 몇 개 있거든요. 일단 부모 state랑 template를 만듭시다.

 

우리가 templateUrl이 아니라 template을 사용한걸 주의 깊게 보세요. template가 너무 작기때문에, route에서 template을 썼어요. (진짜 중요하진 않은데,  small optimization 한거에요.)

 

어쨌든, game.play state를 game state밑에다가 만들거에요. 게임을 플레이 할때, 몇몇 중요한 이유때문에 그 게임 자체가 route를 가질 필요가 있어요. :

  • 다른 참가 가능한 게임과 나누고 싶을거고
  • 다른 플레이어를 초대할 수 있게끔 하고 싶을 거에요.

 

얘네들이 뜻하는건, url에 게임ID가 포함되어야 한다는 거죠. 우리의 기본적인 route는 이런 모양을 할겁니다.

 

기본적인 route는 template, url, controller로 구성됩니다. 일단 가서 GameController를 만듭시다. GameController는 우리 게임을 제어하는데 쓰일겁니다. /scripts/game/game_controller.js 를 만듭시다. 이 파일에서, controller() 함수를 써서 Controller를 만들겁니다.

 

당연히, 나중에 GameController안에 다른 기능들을 넣어야겠죠. 어쨌든, 얘를 우리 index.html에 확실하게 포함시키도록 합니다. :

 

브라우져에서 우리 앱을 불러오면, (못생긴) 게임 생성 버튼이 있을거에요. 눌러보면 아무것도 없는 텅 빈 화면으로 이동하게 될겁니다. 왜냐하면 game state안에 우리가 아무짓도 하지 않았기 때문이죠.

 

이제, 어떻게 우리가 게임을 생성할지 이야기 해봅시다.

 

게임 만들기

 

앞에서 얘기했던 것 처럼, 이건 실제로 게임을 만드는 거랑은 조금 차이가 있어요. DOM으로만 이루어진 게임을 만들때, 우리는 다른 JS library를 같이 쓰지 않아요. 하지만, 우리는 엄청난 pixel단위 조작을 해야하는 게임거니까, 조금 다른 방향으로 접근하려고 해요.

 

엄청난 게임을 만들기 위해 쓸 수 있는 방법은, 툭 까놓고 말하면 <canvas>를 쓰는거죠. 그러면 이미지도 그대로 불러올 수 있고, 스프라이트나, 애니메이션이나, 우리가 직접 만든 이벤트 루프도… 다 사용할 수 있죠.

 

아니면, 우리는 웹 게임 제작을 위해 만들어진 게임 라이브러리를 쓸 수도 있어요. 게임 만드는데 필요한 여러가지 귀찮은 일들을 하는것보다는, Phaser라는 오픈소스 게임 라이브러리를 사용하는게 더 낫죠.

 

Phaser는 이런저런 귀찮은 일들도 해주고, 각종 엘리먼트들과 상호작용하기 위해 DSL(domain-specific language)도 사용할 수 있게 해주죠. 우리가 <canvas> 객체 외부에서 내부로 바로 상호작용 할 수 있게도 해줘요! (쓸 일은 없지만요)

 

Pharse는 이런 지루한 일도 커버칠 수 있어요 :

  • 게임에 사용될 물리효과
  • 에셋 프리로딩
  • 스프라이트 제어
  • 애니메이션
  • 키보드/터치 입력
  • 사운드 제어
  • 게임 크기 조절
  • 그 외 이것저것….

 

DOM 엘리먼트로 인터페이스를 직접 만들기보다는, Phaser에다가 맡겨놓고 우리는 게임 만드는데만 포커스를 맞춰보죠.

 

“진짜 간단한” Phaser 소개

 

웹에 Phaser에 관한 뛰어난 문서들이 많기 때문에, 여기서 Phaser를 엄청 자세하게 설명하진 않을거에요. 그래도, 아예 모르면 힘드니까 여기서 기본 지식을 조금 설명해보죠.

 

게임 만들기

 

게임을 만드려면 일단, 우리는 게임 화면을 페이지 어딘가에 배치해야겠죠? 몇가지 방법이 있어요.  Phaser로 게임 만드는 가장 간단한 방법은 게임을 생성할때, 미리 만들어 놓은 게임 객체를 넘겨주는겁니다. 보세요 :

 

코드에서 보면, 우리는 게임을 몇가지 정의만으로 생성하고 있죠. 처음 두개는 우리가 작업할 canvas의 width와 height이구요, 세번째는 <canvas> 엘리먼트의 type을 정의하고 있네요. 만약 우리가 Phaser.AUTO로 정의하면,  phaser는 게임이 로딩되는 동안 브라우저에 알맞는 엘리먼트를 세심하게 작성할거에요.

 

거의 항상 Pharse.AUTO를 사용할겁니다. 역시 기본 설정은 좋아요.

 

네번째는 <canvus>가 append 될 DOM 엘리먼트의 id에요. 우리가 얘를 작성하지 않으면, phaser는 body에 붙어버릴거에요.

 

그리고, 마지막 요소는 게임 object입니다. 이 object는 Phaser가 게임을 실행하는데 필요한 모든 게임 메소드들을 포함합니다. 여기에는 중요한 게임 메소드들만 몇 개 적어놨어요.

 

이 메소드들은 진짜 간단한 메소드에요. 이건 작은 게임이나 샘플 게임 만들때 적합하죠. 다른 한편으로는, 뭘 더 할수록 더 복잡해지기 때문에, 우리는 게임 만드는 방법을 좀 달리 해야되는 것 처럼 보이네요.

 

State object를 통해서 게임 만들기

 

Phaser에서는하나의 게임 Object가 아니라 state object를 통해 state 단위로 게임 개발을 가능하게 해 줍니다. State objet-based game을 만들기 위해, 우리는 마지막 argument를 지우고, phaser game에 state를 붙일겁니다 :

 

이렇게 하면, 우리 게임에 서로 다른 state 들을 설정 할 수 있어요. 이게 복잡한 게임 만들때 훨신 좋은 방법입니다.

MainMenu state를 만들어볼게요. 아래처럼 보이게 할거에요:

ng-invader
역자) 원 글 페이지에 가시면 실제 구동 화면을 보실 수 있습니다. ;[

게임을 위한 함수 작성

 

preload() 함수는 게임에 쓸 asset을 불러오기 위한 함수에요. 게임을 진행하려면 이미지, 스프라이트, 사운드 등을 불러와야 겠죠. preload() 함수를 써서 불러옵시다. state에서 두번째로 호출되는 함수랍니다! (init()  뒤에 호출되지요!:])

 

이미지를 불러오기위해, 우리는 이름과 url을 정의할 거에요 :

 

스프라이트 시트를 불러올게요 :

 

혹시 스프라이트 시트가 뭔지 잘 모르시나요? 스프라이트 시트는 하나의 이미지에 애니메이션의 각각 다른 부분의 이미지를 넣어 둔 거에요. 더 나가자면, 에셋의 수를 적게 만들때 좋은 방법입니다.

 

밑의 그림은 128*128 크기의 16가지의 다른 이미지들을 저장한 explosion 스프라이트 시트 (phaser game에 등록된 asset임!) 입니다.

이렇게 생겼어요 :

explode.png 는 phaser example repo에서 다운받으실 수 있으세요!

 

오디오도 불러오구요:

 

비트맵 폰트도 불러오구여 :

 

스크립트 태그도 불러와 볼까요? :

create() 함수

 

create() 함수는 우리가 실제로 페이지에 게임을 넣을때 쓰는 함수에요. 에셋을 설정하고, 스프라이트를 배치하며, 애니메이션을 불러오는 등등.. 한번 MainMenu state에 회전하는 배경화면을 넣어보죠.

 

메뉴 배경화면은 이런 우주 모습이 보일거에요

ng-invader2
역자) 원 글 페이지에 가시면 실제 구동 화면을
보실 수 있습니다. ;[

Phaser에서는 이런 스크롤 효과를 autoScroll설정을 통해 엄청 쉽게 넣을 수 있어요. 우선, 우리는 화면에 메뉴를 배치해야되요. 요래요래 해봅시다 :

 

일단 우리는 menu_background 를 뼈대가 될 tileSprite로 넣었어요. tileSprite를 여기에서 쓰는 이유는 배경화면을 화면 전체에 채우고 싶기 때문이에요. (바둑판식 배열!)

ng-invader3
역자) 원 글 페이지에 가시면 실제 구동 화면을
보실 수 있습니다. ;[

autoscrolling을 설정하기 위해, 우리는 phaser의 autoScroll()함수를 background에 넣고, x축과 y축이 움직일 길이를 파라미터로 넣어줍시다.

 

배경화면 작업이 끝나면, 로고를 넣읍시다. 스프라이트를 화면에 넣고 배치하는것도 이래 쉬워요.

 

마지막으로, 메뉴버튼 하나 넣어봅시다. 이것도 쉬워요. phaser가 만들어주는 text 스프라이트를 넣읍시다. 우리가 만든 스프라이트 넣는거보다 이 편이 더 멋진 text를 표현할 수 있어요.

 

Phaser에서는 텍스트 버튼을 다양하게 커스터마이징 할 수도 있습니다 (여기를 확인하세요). 음… 폰트랑 색을 설정하고, 가운데 정렬합시다 :

 

우리는 얘가 input을 받을 수 있을지 없을지를 정해줘야겠죠, 다른말로 말하면, 상호작용이 가능한 오브젝트인지 Phaser에게 알려줘야 해요. inputEnabled 를 사용해서 표시합시다:

 

이제, 이 text박스는 상호작용이 가능합니다. 이제 핸들러를 설정해줘야 해요. 예를 들면, 얘를 클릭하거나 터치했을때 어떤 일이 일어나게끔 연결해줘야 한다는 거죠. 마우스가 위로 지나가거나 스크롤 할 때도 마찮가지에요. 우리는 Phaser에게 이 엘리먼트에 어떤 이벤트가 연결되어야 하는지 알려줘야해요:

 

마지막으로, newGameBtn에 마우스가 올라가면 나올 애니메이션을 만들어줍시다. tween이라고 불리는 걸 사용할거에요.

 

tween은 오브젝트를 (부드럽게) 변형시켜주는 이펙트에요. 시작점과 끝점, 소요시간을 파라미터로 갖고, 적당하게 파라미터를 설정해주면 부드럽게 변형되게끔 할 수 있죠.

 

Phaser에서 tween 설정하는건 정말 쉬워요. overNewgame 함수안에서, newGameBtn에 twwen을 설정할거에요. 이 함수는 버튼에 마우스가 올라오면 실행 될 겁니다:

 

이 tween에서, 우리는 newGameBtn object의 크기를 조작했어요. 최초 크기는 (1,1) 이죠. 마지막에 1.3배 커지도록 조정했어요. 300밀리초 (세번째 파라미터 – 기간) 에 걸쳐서 커질거구요.

 

다섯번째 파라미터는 easing 사용하고 관련된거에요. easing에 대해서는 여길 확인하세요. 이 튜토리얼에서는 설명 안 할 거니까요.

 

마지막 파라미터는 자동실행 파라미터에요. 얘를 true로 해 주시면, tween이 자동적으로 실행되요. 만약 이걸 설정 안하면, 우리가 tween을 따로 실행시켜주지 않는 이상 실행되지 않아요.

 

마지막으로, 메뉴에 마우스가 올라가면 소리가 나게 하기 위해서, 우리는 audio element를 실행할 거에요:

 

update() 함수

 

update함수는 게임 루프 함수와 같아요. 게임이 업데이트될 필요가 있을때마다 실행되죠. MainMenu state에는 필요 없지만, game loop를 위해서는 필요해요.

MainMenu state 전체를 보시려면, 여기를 확인하세요 : http://j.mp/13y6H2z

 

AngularJS에 게임 넣기!

 

앞에서 체크했듯이, 게임은 <canvas> DOM 요소에 들어갑니다. 우리가 angular app을 만들고 있음을 생각해 보면, directive를 만들어야 하는걸 알 수 있겠죠.

 

gameCanvas directive를 만들어볼까요?. /scripts/game/game_canvas.js 파일을 만듭시다. app.game module에 directive 만들거에요.

 

디렉티브가 단순할거에요. 기본이 될 directive를 만들어봅시다:

 

directive 자체는 명확하죠. 우리는 isolate scope (우리가 항상 AngularJS directive를 만들때 해야 하는 것 처럼요) 를 만들었지요, 현재 플레이어와 맵 아이디를 받을거에요. 여기서 players는 사용하지 않는데, mapId는 사용 할거에요. template에 id 어트리뷰트를 가진 DOM 엘리먼트를 하나 넣어놨어요. 마지막으로, 우리는 디렉티브에 기능을 추가하기 위해서 link 함수를 넣었지요.

 

만약 당신이 directive를 잘 모르시면, building custom directive 포스트를 확인하세요!

 

linkFn을 봅시다. directive가 페이지에 만들어질때, link function이 실행 될 거에요. 우리는 이 link function 안에 Phaser game을 넣을겁니다.

 

게임을 /scripts/game/main.js 에 만듭시다. 이 파일 안에, 우리가 원하는 곳에, 함수 하나를 만들어 봅시다. 전역 window 객체에 이것 저것 막 넣지 않기 위해서, 우리는 CommonJs, AMD, 아니면 ES6같은걸 사용해서 함수를 export 할 수 있을거에요( github의 전체 소스를 확인하시면, ES6을 사용해서 JS파일들을 불러왔어요!:] ). 간단하게 하기 위해서, 여기선 window 객체에다가 넣는 것으로 퉁칩시다.

 

이 함수에서, 우리는 4개의 파라미터를 넣어줘야 해요:

  • directive의 scope. (꼭 필요하지는 않겠지만..)
  • isolate directive definition상의 players 변수
  • isolate directive definition상의 mapId 변수
  • angualr $injector

 

$injector를 함수로 넘겨줌으로써, 게임 내부에서 angular나 angular service가 필요할 때 사용할 수 있지요.

 

일단, 이 파일을 index.html 에서 불러오고, directive내에서 함수를 호출해 봅시다.

먼저, index.html에 script를 넣어봅시다:

 

두번째로, 디렉티브의 link function에서 함수를 호출합시다:

 

이러면, 게임이 불러와 지죠. 이제, phaser object를 사용해서 우리는 아무 게임이나 만들 수 있어요. component 형식으로 phaser game을 만들면 이득인 부분중에 하나는, 우리가 directive의 id를 안다는 거죠.  template을 보시면, gameCanvas라고 id를 설정했어요. 이제 phaser game에 뭐라도 하고싶을땐,  페이지에 있는 이 엘리먼트만 참조하면 되겠죠:

 

디렉티브에서 css height와 width를 조절해봅시다. 동적으로 height와 width를 수정할 수 있어요. 이런 식으로 게임을 만들면 되는데, 여기서 전부 다 만들지는 않을거에요. github에 전체 소스를 둘테니까, 한번 보세요. 참고하시라고 main.js만 살짝 보여드릴게요 :

정리하기

 

directvie를 쓰기때문에, <canvas> element를 angular app에서 손 볼수 있게 되었어요. 게임하고 상관없는 방향으로 나가고 싶을땐, 단순히 메모리에서 게임 부분만 지워주면 되는거죠. 게임이 실행중이지 않을때, 우리가 여기에 메모리를 투자할 이유도 없구요.

 

directive의 scope 덕분에, 우리는 scope의 $destory 이벤트를 게임 초기화 할 준비를 하는데 사용할 수 있어요. Angular가 scope를 비울때, $destory 이벤트를 내뿜거든요. 우리는 단순히 destory()를 phaser game object에서 불러오고, phaser가 메모리와 관련 object들을 관리하게끔 하면 되는거죠.

$injector 여?…

 

게임과 AngularJS가 통신하는 방법중에 하나는 scope events system을 사용하는 겁니다. 예를들어, 우리가 angular app의 angular button을 통해 음악을 토글하고 싶을때, 우리는 angular에서의 이벤트를 게임에서 받아줘야하겠죠.

 

이런식으로 말이에요:

 

추가적으로, 우리는 game에서 Angular로 이벤트를 쏠 수도 있지요. 만약 게임에 새로운 플레이어가 참가하거나 이벤트가 생겨서, Angular 쪽으로 알림을 줘야한다면, scope object에서 $emit 함수를 쓸 수 있어요!

Server-side (멀티플레이 게임)

 

이제 멀티플레이를 위한 부분으로 가봅시다. 우린 node랑 express를 사용할거에요. 멀티플레이 서버를 돌리기 위해, 몇가지 dependence가 필요합니다. npm(the node package manager)를 써서 설치해봅시다:

 

이건 node 튜토리얼이 아니니까, 여기 server.js 파일을 보세요. 중요한 부분이 두개 있습니다 :

  1. express.static 을 써서 angular app을 제공할거에요.
  2. (routing 제어를 위해) socket.io route를 통해 파일을 불러올거에요.

 

/routes/io.js 파일을 보면, 서버와 클라이언트간의 상호작용을 위해 몇가지 이벤트를 설정해놨어요. 새로운 사람이 접속할때마다, 새로운 예비 플레이어라고 추측하죠. 무슨 말이냐면, 우리는 접속하는 모든 사람이 게임을 하는중인지 아닌지 추적할거에요.

 

io.js 파일을 보실까요 :

 

만약, 클라이언트가 접속하면, 우리는 새로운 Player object를 생성하고, 거기에 ID를 저장할거에요.

Player 클래스는 깃허브에서 확인하세요.

 

이제 플레이어도 있으니, socket에서 확인하고 제어할수 있는 이벤트들을 설정합시다:

 

필수적으로, 우리는 새로운 플레이어를 player 배열에 추가해주는 newPlayer 핸들러를 만들어야 해요. 이 이벤트가 실행되려면,  클라이언트가 서버에 새로운 플레이어가 있다고 얘기하게끔 해야겠죠.

 

게임이 실행되면, 우리는 클라이언트에 newPlayer 이벤트를 보냅니다. :

 

다시 서버로, 서버에서 해야 하는 일들이 더 있죠:

  1. 맵이 존재하는지 아닌지 확인합니다. (없으면 만들어야 하구요!)
  2. 현재 맵의플레이어 배열에 플레이어를 추가합니다.
  3. 접속중인 플레이어들한테 신규 플레이어가 들어왔다고 말해줍니다.
  4. 신규 플레이어한테 접속중인 플레이어들에 대해서 말해줍니다.

복잡하게 보일 수도 있겠는데, 사실은 간단해요.

한번에 하나씩 해치워봅시다. 우리는 맵이 존재하는지 이렇게 확인할 수 있어요:

 

 

다음으로, 맵에 플레이어를 추가해줍시다:

 

이렇게 하면 현재 접속중인 플레이어들한테 신규 플레이어에 대해서 말해줄 수 있구요:

 

신규 플레이어한테 지금 접속중인 플레이어에 대해서 이야기 해 줍시다:

client-side 멀티플레이어

 

socket.io를 제어하기 전에, 먼저 클라이언트 app에다가 socek.io client-side 파일을 로딩하라고 이야기 해 줘야 합니다. angluar의 의존성 주입 시스템을 우린 정말 좋아하니까, d3 포스팅에서 쓴 패턴을 그대로 따서 쓰도록 하죠.

 

우리는 두개의 서비스를 만들거에요. 첫번쨰 서비스는 socket.io를 불러오는 서비스에요. service가 초기화될때, 얘가 동적으로 body에 script tag를 붙여서 브라우저가 스크립트를 로딩하게끔 할 거에요. 어렵지 않은데, 조금 길죠…:

 

너무 자세하게 들어가진 않을거에요, 이 서비스는 done()이라는 promise를 return하는 하나의 함수로 이루어져 있어요. 이 promise가 resolve되면, window.io(의 로딩이 끝났기 때문에,)를  사용할 수 있어요. window.io사용을 위해 resolve 되었는지 확인하는 코드를 볼까요? :

 

다음은 socket handler를 만들기 위해 필요한 서비스입니다. socket.io를 쓰기전에 해결해야할 것들이 엄청 많기 때문에, 우리는 Brian Ford의 멋진 socket library를 사용할게요.

먼저, 설치해야 되니까, github에서 긁어오시거나 bower를 사용하세요.

 

다음엔, socket 연결을 랩핑하는, promise를 return 하는 서비스를 만들거에요. 얘는 간단하면서 방금 전 서비스보다 짧기까지 합니다.

 

이 서비스를 쓰면, 다른 angular object에 쉽게 의존성을 주입할 수 있고, promise가 완료되면 socket과 연결되서 쉽게 서버와 상호작용 할 수 있어요. :

 

여기까지이구요, 고맙습니다. 멋진 게임 만들수 있기를 바랄게요!

이 글에 쓰인 모든 소스는 여기서 확인하세요!

댓글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.