Category Archives: JAVASCRIPT

Cocos2d-X 웹게임 개발하기 – Mac에서 개발환경 구축하기

웹기반 게임을 개발하기 위해서는 다양한 게임 엔진을 사용하는것을 고려해 볼 수 있습니다. 이 문서에서는 Cocos2d-X 를 이용하여 게임을 WEBGL 또는 CANVAS 환경에서 동작하는 게임을 만드는 과정을 설명하려 합니다. 먼저 개발 환경을 설정 할 것이며 Homebrew를 사용하는 맥 환경을 가정할 것입니다. 또한 개발 툴로는 IntelliJ를 사용할 것입니다.

1. JAVA 설치

우리는 Cocos2d-X를 사용하여 웹게임만을 개발할 생각이지만 JSB(Javascript Binding)이라는 기술을 사용하여 JS로 만들어진 게임 코드를 이용하여 iOS/Android 등과 같은 네이티브 게임을 빌드할 수 있습니다. 이러한 준비를 위하여 필요한 모든 것을 설치해 보도록 하겠습니다.

Homebrew를 사용하면 최신버전의 Java를 손쉽게 설치할 수 있습니다.

$ brew cask install java

위와 같은 명령을 사용하여 최신 버전의 Java를 설치할 수 있으며 설치 후 .bash_profile에 JAVA_HOME 환경 변수를 추가해주도록 합시다.

$ echo "export JAVA_HOME=`/usr/libexec/java_home`" >> ~/.bash_profile

2. Android SDK/NDK 설치

기존에 Android Studio를 활용중이라면 사용중인 SDK를 그대로 사용하셔도 무방합니다만 여기서는 Homebrew를 이용하여 SDK와 NDK를 설치하는 방법을 적어보겠습니다.

$ brew install android-sdk
$ brew install android-ndk

설치가 완료되면 다음의 명령을 사용하여 Android SDK를 업데이트할 수 있습니다.

$ android

이후에 뜨는 화면에서 자신이 빌드시에 사용하고자 하는 필요한 것들을 선택하여 설치를 진행하도록 합시다.

3. Python 및 Ant 설치

마찬가지로 Homebrew를 사용하여 Python과 Ant를 설치해보겠습니다.

$ brew install python
$ brew install ant

다음의 명령을 사용하여 Ant의 환경 변수를 추가해주도록 하겠습니다.

$ echo "export ANT_ROOT="/usr/local/opt/ant/bin"" >> ~/.bash_profile

4. Cocos2d-X 다운로드 및 설치

[Cocos2d-X 공식홈페이지]에 방문하면 최신버전의 Cocos2d-X 안정화 버전을 다운로드 받을 수 있습니다.

이 글을 작성할 시점에는 3.14.1 버전이 최신버전이네요. 다운로드 받도록 하겠습니다.

다운로드가 완료되었다면 압축을 풀고 적절히 원하는 위치에 옮겨놓도록 하겠습니다. 저는 홈디렉토리 밑에 cocos2d-x 디렉토리에 옮겨두었습니다. 이후에 셋업 명령을 실행합니다.

$ ./setup.py 

Setting up cocos2d-x...
->Check environment variable COCOS_CONSOLE_ROOT
  ->Search for environment variable COCOS_CONSOLE_ROOT...
    ->COCOS_CONSOLE_ROOT not found

  -> Add COCOS_CONSOLE_ROOT environment variable...
    ->Added COCOS_CONSOLE_ROOT=/Users/dennis/cocos2d-x/tools/cocos2d-console/bin

->Check environment variable COCOS_X_ROOT
  ->Search for environment variable COCOS_X_ROOT...
    ->COCOS_X_ROOT not found

  -> Add COCOS_X_ROOT environment variable...
    ->Added COCOS_X_ROOT=/Users/dennis

->Check environment variable COCOS_TEMPLATES_ROOT
  ->Search for environment variable COCOS_TEMPLATES_ROOT...
    ->COCOS_TEMPLATES_ROOT not found

  -> Add COCOS_TEMPLATES_ROOT environment variable...
    ->Added COCOS_TEMPLATES_ROOT=/Users/dennis/cocos2d-x/templates

->Configuration for Android platform only, you can also skip and manually edit "/Users/dennis/.bash_profile"

->Check environment variable NDK_ROOT
  ->Search for environment variable NDK_ROOT...
    ->NDK_ROOT not found

  ->Search for command ndk-build in system...
    ->Path /usr/local/Cellar/android-ndk/r13b was found

  -> Add NDK_ROOT environment variable...
    ->Added NDK_ROOT=/usr/local/Cellar/android-ndk/r13b

->Check environment variable ANDROID_SDK_ROOT
  ->Search for environment variable ANDROID_SDK_ROOT...
    ->ANDROID_SDK_ROOT not found

  ->Search for command android in system...
    ->Path /usr/local/Cellar/android-sdk/24.4.1_1 was found

  -> Add ANDROID_SDK_ROOT environment variable...
    ->Added ANDROID_SDK_ROOT=/usr/local/Cellar/android-sdk/24.4.1_1

->Check environment variable ANT_ROOT
  ->Search for environment variable ANT_ROOT...
    ->ANT_ROOT is found : /usr/local/opt/ant/bin


A backup file "/Users/dennis/.bash_profile.backup2" is created for "/Users/dennis/.bash_profile".

Please execute command: "source /Users/dennis/.bash_profile" to make added system variables take effect

셋업과정은 별고 없고 필요한 툴들을 찾아서 환경변수 혹은 PATH에 등록해주는것이 주요 목적입니다. 저의 경우에는 .bash_profile에 다음과 같은 환경변수가 등록되었습니다.

# Add environment variable COCOS_CONSOLE_ROOT for cocos2d-x
export COCOS_CONSOLE_ROOT=/Users/dennis/cocos2d-x/tools/cocos2d-console/bin
export PATH=$COCOS_CONSOLE_ROOT:$PATH

# Add environment variable COCOS_X_ROOT for cocos2d-x
export COCOS_X_ROOT=/Users/dennis
export PATH=$COCOS_X_ROOT:$PATH

# Add environment variable COCOS_TEMPLATES_ROOT for cocos2d-x
export COCOS_TEMPLATES_ROOT=/Users/dennis/cocos2d-x/templates
export PATH=$COCOS_TEMPLATES_ROOT:$PATH

# Add environment variable NDK_ROOT for cocos2d-x
export NDK_ROOT=/usr/local/Cellar/android-ndk/r13b
export PATH=$NDK_ROOT:$PATH

# Add environment variable ANDROID_SDK_ROOT for cocos2d-x
export ANDROID_SDK_ROOT=/usr/local/Cellar/android-sdk/24.4.1_1
export PATH=$ANDROID_SDK_ROOT:$PATH
export PATH=$ANDROID_SDK_ROOT/tools:$ANDROID_SDK_ROOT/platform-tools:$PATH

이후에 저기서 말해준대로 source 명령을 사용하여 추가된 환경변수를 적용 하도록 합시다.

$ source ~/.bash_profile

이제 cocos 명령이 정상 동작하는지 테스트를 해봅시다. 최초 실행시에 사용 정보를 자기네들 서버에 전송하는것을 허용할지 물어오는데 저는 No를 선택하였습니다. 어쨌든 문제없이 명령이 실행된다면 준비는 완료된 것입니다.

$ cocos 
Cocos collects data when the command-line tools are used for development. This data is examined in the aggregate only and is used to continually innovate and improve Cocos products. This data is anonymous and includes, but is not limited to, a unique hardware identifier, version number our software and information on which tools and/or services in Cocos products are being used and how they are being used. We take your privacy seriously and we do not share or sell any of this data. You can opt-out from sharing this data with us, but by sharing you help contribute to growth of Cocos.

Do you agree to sent the data? [Y]es, [N]o
N

/Users/dennis/cocos2d-x/tools/cocos2d-console/bin/cocos.py 2.2 - cocos console: A command line tool for Cocos2d-x.

Available commands:
	run              Compiles, deploy and run project on the target.
	gen-libs         Generate prebuilt libs of engine. The libs will be placed in 'prebuilt' folder of the engine root path.
	luacompile       Encrypt and/or compile lua files.
	deploy           Compile and deploy a project to a device/simulator.
	package          Manage package for cocos.
	compile          Compile projects to binary.
	gen-simulator    Generate Cocos Simulator.
	new              Creates a new project.
	jscompile        Compile and/or compress js files.
	gen-templates    Generate templates for Cocos Framework.

Available arguments:
	-h, --help			Show this help information.
	-v, --version			Show the version of this command tool.
	--ol ['en', 'zh', 'zh_tr']	Specify the language of output messages.

Example:
	cocos new --help
	cocos run --help

4. 프로젝트 생성

이제 터미널에서 cocos 명령을 사용하여 MyGame이라는 이름의 새로운 프로젝트를 생성해보도록 하겠습니다. 언어(-l)은 js로 패키지명(-p)는 kr.pe.theeye.mygame 으로 설정하였습니다.

$ cocos new MyGame -l js -p kr.pr.theeye.mygame
> Copy template into /Users/dennis/Documents/Project/MyGame
> Copying directory from cocos root directory...
> Copying files from template directory...
> Copying Cocos2d-x files...
> Rename project name from 'HelloJavascript' to 'MyGame'
> Replace the project name from 'HelloJavascript' to 'MyGame'
> Replace the project package name from 'org.cocos2dx.hellojavascript' to 'kr.pe.theeye.mygame'
> Replace the Mac bundle id from 'org.cocos2dx.hellojavascript' to 'kr.pe.theeye.mygame'
> Replace the iOS bundle id from 'org.cocos2dx.hellojavascript' to 'kr.pe.theeye.mygame'

이렇게 하면 프로젝트가 생성되고 프로젝트 디렉토리 내부로 들어가서 다음의 명령을 통해 바로 웹브라우저로 실행해볼 수 있습니다.

$ cocos run -p web
Building mode: debug
Deploying mode: debug
Starting application.
Try start server on 127.0.0.1:8000
Serving HTTP on 127.0.0.1, port 8000 ...

문제 없이 프로젝트가 생성되었다면 다음과 같은 화면을 볼 수 있습니다.

5. IntelliJ를 통해 프로젝트 관리하기

IntelliJ를 이용하여 프로젝트를 관리한다는건 사실 별다른게 있지는 않습니다. 그냥 IntelliJ로 프로젝트를 열면 알아서 자동완성을 포함한 프로젝트 구동에 필요한 모든것이 갖춰지게 됩니다.

Import Porject를 선택해도 되지만 그냥 Open으로 기존에 생성했던 MyGame 프로젝트를 열어보겠습니다.

MyGame을 찾아서 OK를 누르게 되면 프로젝트가 열리고 Indexing 과정을 거쳐 개발을 할 수 있는 환경이 자동으로 구축됩니다.

프로젝트 루트의 index.html을 마우스 우클릭하여 Run을 실행하게 되면 IntelliJ가 임의의 포트를 사용하여 프로젝트를 실행해 주게 됩니다.

참고 : http://blog.revivalx.com/2014/06/21/setting-up-your-cocos2d-js-environment/

AngularJS PhoneCat 튜토리얼 앱 – 컴포넌트

angularjs-large

이전 단계에서 우리는 컨트롤러와 템플릿이 어떻게 정적인 HTML 페이지를 동적인 뷰로 변화시키는지를 보았습니다. 이것은 단일 페이지 어플리케이션에서 (특히 Angular 어플리케이션들에서) 매우 일반적으로 사용되는 패턴입니다.

이 템플릿(바인딩과 프레젠테이션 로직을 포함하고 있는 뷰의 일부)은 우리의 데이터가 어떻게 정리되고 유저에게 보여지는지를 결정하는 설계도와 같은 역할을 합니다. 그리고 컨트롤러는 바인딩에 의해서 해석되거나 행동이나 로직을 우리의 템플릿에 적용되는 컨텍스트를 제공합니다.

우리는 아직 몇군데를 좀 더 개선할 수 있습니다 :

  1. 우리의 어플리케이션의 다른 부분에서 같은 기능을 하는 부분을 재사용 하고 싶다면 어떻게 해야 할까요? 컨트롤러를 포함한 전체 템플릿을 복제할 수 있습니다. 하지만 이것은 에러를 야기하고 코드의 보수를 어렵게 만들것입니다.
  2. 우리의 컨트롤러와 템플릿을 동적 뷰로 변환시키는데 본드 역할을 하는 스코프는 페이지의 다른 파트들간에 격리되지 않습니다. 이것이 의미하는 것은 페이지의 다른 부분에 있는 예상하지 못한 변경이 (프로퍼티 이름 충돌등) 우리의 뷰에 예상치 못한 디버깅이 어려운 문제를 가져올 수 있습니다. (물론 현재 진행하고 우리의 튜토리얼은 작은 프로젝트이기에 문제가 되지 않겠지만 실 서비스의 큰 규모의 어플리케이션에서는 문제가 될 수 있습니다)

이번에 진행할 튜토리얼을 위해 프로젝트를 단계 3으로 초기화 하겠습니다.

$ git checkout -f step-3

컴포넌트로 구조하자!

템플릿과 컨트롤러 콤비네이션은 일반적이고 순환적인 패턴을 가지기 때문에 Angular는 간단한 방법으로 이것들을 재사용가능하고 격리된 엔티티로 통합해줍니다. 이것을 컴포넌트라고 합니다. 추가로 Angular는 각각의 컴포넌트 인스턴스마다 격리된 스코프를 생성해주며 이는 스코프간의 상속이 발생하지 않고 어플리케이션의 다른 부분의 같은 컴포넌트를 사용하고 있는 부분들끼리 발생할 수 있는 리스크가 없음을 의미합니다.

이 문서는 소개 수준의 튜토리얼이기 때문에 컴포넌트의 모든 기능에 대해서 심도 있는 설명을 하지 못합니다. 만약 당신이 컴포넌트에 대해 더 알아보고 사용 패턴에 대해 공부해보고 싶다면 개발자 가이드의 Components 섹션을 읽어보시기 바랍니다.

사실, 사람들은 컴포넌트를 그들의 더 복잡하고 장확한 (하지만 강력한) 지시자의 자기 중심적이고 경량화된 버전으로 생각할 수 있습니다. 개발자 가이드의 Directives 섹션에서 이부분에 대한 모든것을 확인할 수 있습니다. 지시자는 고급 주제입니다. 이 튜토리얼을 이용해서 공부를 끝낸 뒤에 기초를 마스터하기 위해 한번 읽어보시길 권장합니다.

컴포넌트를 생성하기 위해서 우리는 Angular 모듈의 .component() 를 사용해야 합니다. 우리는 여기서 컴포넌트의 이름과 컴포넌트 정의 오브젝트(CDO – Component Definition Object)를 제공해야 합니다.

컴포넌트 역시 지시자이므로 컴포넌트의 이름은 카멜 케이스(camelCase) 로 표기하도록 합며 HTML에서는 케밥 케이스(kebab-case)로 사용할 것입니다. 우선 간단한 형태로 우리의 CDO는 하나의 템플릿과 하나의 컨트롤러를 포함할 것입니다. (우리는 사실 컨트롤러를 생략할수도 있으며 이경우 Angular는 더미 컨트롤러를 생성하여 제공할 것입니다) 이것은 템플릿에 어떠한 동작도 추가하지 않은 간단하지만 유용한 형태의 프레젠테이션 컴포넌트입니다.

다음의 예제를 봅시다.

angular.
  module('myApp').
  component('greetUser', {
    template: 'Hello, {{$ctrl.user}}!',
    controller: function GreetUserController() {
      this.user = 'world';
    }
  });

이제 우리의 뷰에 <greet-user></greet-user> 를 추가할때마다 Angular는 템플릿을 제공하고 특정 컨트롤러의 인스턴스를 관리함으로써 DOM 하위 트리를 확장합니다.

하지만 여기서 $ctrl 은 어디서 왔고 무엇을 하는것일까요? 이미 이전에 설명 했듯이 이것은 스코프를 직접 접근하는것을 피하는 좋은 예시입니다. 여기서 우리는 우리의 컨트롤러 인스턴스를 사용할 수 있고 사용해야만 합니다. 예로 우리의 컨트롤러에 프로퍼티로 할당할 데이터나 메소드는 컨트롤러 생성자 안에서 스코프에 직접 접근하는것이 아닌 “this”로 접근하여 할당합니다.

템플릿에서 컨트롤러 인스턴스를 별칭을 사용하여 접근할 수 있습니다. 이 방법은 우리의 표현식을 해석할때의 컨텍스트가 훨씬 더 명확해 집니다. 기본적으로 컴포넌트는 $ctrl 을 컨트롤러의 별칭으로 사용합니다. 하지만 필요할 경우 이것을 덮어쓰기 할 수 있습니다.

여기에 사용할 수 있는 옵션은 더 많은것이 있습니다. 우리의 어플리케이션에 .component() 를 사용하기 전에 API 문서를 확인해보시기를 권장합니다.

컴포넌트 사용하기

이제 우리는 컴포넌트를 어떻게 만드는지 알았습니다. 그럼 HTML 페이지를 우리가 새로 배운 스킬을 사용하여 리팩토링 해보겠습니다.

<html ng-app="phonecatApp">
<head>
  ...
  <script src="bower_components/angular/angular.js"></script>
  <script src="app.js"></script>
  <script src="phone-list.component.js"></script>
</head>
<body>

  <!-- Use a custom component to render a list of phones -->
  <phone-list></phone-list>

</body>
</html>
// Define the `phonecatApp` module
angular.module('phonecatApp', []);
// Register `phoneList` component, along with its associated controller and template
angular.
  module('phonecatApp').
  component('phoneList', {
    template:
        '<ul>' +
          '<li ng-repeat="phone in $ctrl.phones">' +
            '<span>{{phone.name}}</span>' +
            '<p>{{phone.snippet}}</p>' +
          '</li>' +
        '</ul>',
    controller: function PhoneListController() {
      this.phones = [
        {
          name: 'Nexus S',
          snippet: 'Fast just got faster with Nexus S.'
        }, {
          name: 'Motorola XOOM™ with Wi-Fi',
          snippet: 'The Next, Next Generation tablet.'
        }, {
          name: 'MOTOROLA XOOM™',
          snippet: 'The Next, Next Generation tablet.'
        }
      ];
    }
  });

이제 결과물을 실행해보면 결과물은 보기에는 같아보입니다. 하지만 우리가 무엇을 추가했는지 봐보겠습니다.

  • 우리의 휴대폰 리스트는 재사용 가능해졌습니다. 단지 <phone-list></phone-list> 를 페이지 어디든지 넣어주기하면 하면 휴대폰 리스트를 출력할 수 있습니다.
  • 우리의 메인 뷰 (index.html) 가 깔끔해지고 더 명확해졌습니다. 그냥 훑어보기만 해도 어디에 휴대폰 리스트가 출력될지 알 수 있습니다. 더이상 세부적인 구현에 대해서 신경쓰지 않아도 됩니다.
  • 우리의 컴포넌트는 격리되었고 외부의 영향으로부터 안전합니다. 마찬가지로, 어플리케이션의 다른 부분에 의해서 무언가 사고가 발생할 수 있다는 걱정을 할 필요가 없습니다. 컴포넌트 내부에서 무엇이 발생하면 그것은 그 컴포넌트 내부에서 유지됩니다.
  • 격리된 환경에서 우리의 컴포넌트를 테스트하는것이 더 쉽습니다.

angularjs-phonecat-tutorial-03

각기 다른 타입의 엔티티들은 다른 접미사를 사용함으로써 명확하게 구별되도록 하는것이 좋습니다. 이 튜토리얼에서는 .component 접미사를 컴포넌트에 사용하였습니다. someComponent 컴포넌트의 파일명은 some-component.component.js가 될 것입니다.

테스트

우리는 우리의 컨트롤러와 템플릿을 컴포넌트로 통합하였지만 우리는 여전히 어플리케이션 로직과 데이터가 존재하는 컨트롤러를 개별적으로 테스트 할 수 있고 그래야 합니다. 컴포넌트의 컨트롤러를 인스턴스화 하고 가져오기 위해서는 Angular는 $componentController 서비스를 제공합니다.

우리는 기존에 .controller() 메소드를 통해 등록된 컨트롤러의 이름으로 컨트롤러를 인스턴스화 하는 메소드로 $controller 서비스를 사용했었습니다. 만약 원한다면 우리는 또한 이러한 방식으로 우리의 컴포넌트 컨트롤러를 등록할 수 있습니다. 하지만 대신에 우리는 이것을 CDO 내부에 인라인으로 선언하여 지역화를 유지할 수 있도록 하였습니다. 어느쪽이던지 잘 동작합니다.

describe('phoneList', function() {

  // Load the module that contains the `phoneList` component before each test
  beforeEach(module('phonecatApp'));

  // Test the controller
  describe('PhoneListController', function() {

    it('should create a `phones` model with 3 phones', inject(function($componentController) {
      var ctrl = $componentController('phoneList');

      expect(ctrl.phones.length).toBe(3);
    }));

  });

});

이 테스트는 컨트롤러와 연관된 phoneList 컴포넌트를 가져오고 인스턴스화 한 뒤 휴대폰 배열 프로퍼티가 3개의 레코드를 가지고 있는지 검증합니다. 여기서는 scope가 아닌 컨트롤러 인스턴스 자신이 데이터를 가지고 있는것을 확인할 수 있습니다.

실행 테스트

이전과 동일하게 npm test 를 통해 테스트를 진행하고 파일의 변화를 즉시 추적하도록 할 수 있습니다.

실험

  • 이전 단계로부터 이번에는 phoneList 컴포넌트에 대한 실험을 도전해 봅시다.
  • index.html 에 단지 <phone-list></phone-list> 를 더 추가함으로써 한 페이지 안에 두개 이상의 휴대폰 리스트를 출력하도록 해봅시다. 이제 새로운 바인딩을 phoneList 컴포넌트의 템플릿에 추가해 봅시다.
template:
    '<p>Total number of phones: {{$ctrl.phones.length}}</p>' +
    '<ul>' +
    ...

페이지를 새로고침하고 새로운 기능이 모든 휴대폰 리스트에 퍼져나가는것을 확인해 봅시다. 실제 어플리케이션에서는 이러한 휴대폰 리스트가 다른 여러 페이지에서 보여질 수 있습니다. 또한 어떤 한 장소에서 값이 변경되거나 추가될 수 있으며 이 변화가 어플리케이션 전체에 퍼쳐나가게 됩니다.

참고 : https://docs.angularjs.org/tutorial/step_03