2015년 1월 20일 화요일

[UniRx] Reactive Extension - 2 IObservable의 형 변환

개요
IObserable<T>의 운용에 있어서 가장 기초적인 이해를 위해 T가 의미하는 바와 그 운용방법을 정리하고자 한다.

본론
IObservable<T>는, Rx에 의해 정의된 데이터 스트림의 인터페이스이다.
앞의 포스팅의 기본 정의를 보면 이벤트가 총 3개가 있는것을 알 수가 있다.

OnNext(data);
OnError(exception);
OnCompleted();

OnCompleted에 대해서는 이렇다할 데이터가 넘어오지 않는다.
이는 어디까지나 데이터의 흐름이 끊겼음을 알려주는 신호일 뿐이다.

단순하게 Coroutine의 대용으로 생각해 버리기에는 Rx의 탄생배경과 그 근간이 조금 다르다.
Coroutine은 비동기의 흐름을 하나의 thread에서 수행하기 위해 본래 1개의 callstack을 임의로 분할해(yield에 의해) 운용하는 것으로, 비동기 처리에 초점이 맞추어진것이라 한다면.

Rx의 경우 어떤 데이터가 지속적으로 변하는것을 전제로 생각되어진, 그 변화에 반응하는 반응형 프로그래밍 기법이라 할 수 있을것 같다.

이러한 반응성 프로그래밍을 하기 위해서 사용되고 있는 대표적인 기법이 Observable pattern이고, IObservable<T>는 그 인터페이스라 할 수 있다.

IObservable<T>는 T가 지속적으로 변화할 데이터이며, 이를 감시하겠다는 것을 뜻한다.

따라서 이 Observer를 subscribe한다는 말은 어떤 감시 룰을 실제로 운용하겠다는 것을 의미한다. 이 Observe라는 행위를 통해, IObservable<T>는 운용이 된다.

기본적으로 generic인 이 인터페이스를 운용하려 할때, 원하는 데이터가 변화할때가 있다. 예를들면 JSON Parsing라던가가 그 예다. 이미 generic에 의해 타입이 지정되어 있는 이 type의 observable에 대해, 다른 타입으로 어떻게 변환하는가에 대한 의문을 가질 수 있을듯 하다.

예를 들어서 ObservableWWW.Get()에 의해 웹서버에 있는 json파일을 가져와 이를 parse한다 생각해보자.

uFrame에서 공식적으로 채택되어 있는 JSON parser는 simpleJSON이므로, simpleJSON를 기준으로 예를 들어볼까 한다. 다른 implementation을 사용한다고 하더라도, 이해하는데 큰 차이는 없을거라 생각된다.

var owww = ObservableWWW.Get(url).Select(src=> JSON.Parse(src));

위와 같은 Chaining으로 변환이 가능하다.

이후에 owww를 subscribe하면 간단하게 parsing이 완료된 데이터를 얻을 수 있다.

ObservableWWW.Get(url)은, IObservable<string>이다.
여기서 Select라는 LINQ구문의 확장을 사용하면 src에 대해 가공을 실시할 수 있고, 그 결과 IObservable<JSONNode>가 반환된다.

[UniRx] Reactive Extension - 1 : Observable 직접 작성하기

개요

UniRx를 사용함에 앞서 가장 기본이 되는 Observable작성방법을 생각보다 찾기 힘들었던 관계로 글로 남겨두고자 한다.

본론

Rx는 Promise++ specs를 따르고 있다고 한다.
https://gist.github.com/staltz/868e7e9bc2a7b8c1f754

Promise/A+사양인 bluebird의 경우, 아래와 같은 형태의 Promise를 정의하는 것이 가능하다.

// Definition

var Promise = require("bluebird");
var example = new Promise(function(resolve,reject) {
    try {
    // something do
    var some_result_value = ...;
    // then
    resolve(some_result_value);
    } catch(ex) {
    reject(ex);
    }
});

// Usage

example
.then(function(some_result_value) {
// some_result_value is coming.
}).catch(function(ex) {
// ex is coming.
});

Rx의 경우 Promise의 일종이기때문에 비슷하게 정의를 할 수가있는데
위와 같은 정의를 하자면

// Definition

var example = Observable.Create<T>(observer=> {

    try {
       // something do
       T some_result_value = ...;
       observer.OnNext(some_result_value);
    } catch(ex) {
        observer.OnError(ex);
    }
    observer.OnCompleted();

    return Disposable.Create(()=> {
         // something dispose here if required
    });

});

// Usage

example.Subscribe(some_result_value=> {
      // some_result_value is coming
},ex=> {
      // ex is coming
},()=> {
      // OnCompleted calling
});

위와 같이 사용할 수 있다.

ex를 알기 쉽게 하기 위해 try catch를 넣었으나, 비동기 코드를 작성하려 할 경우 일반적인 비동기 운용에 기반한 코드를 작성하고, 필요한 시점에 위에 적힌것과 같은 이벤트 호출을 실시하면 된다.

혹은 간단하게 Coroutine용 IEnumerator function을 작성한 후, Observable.FromCoroutine()을 사용하는 방법도 있다.

2015년 1월 18일 일요일

[uFrame 1.5] Diagram에서 일일히 connect하지 않고 직접 transition하기

개요

사실 다이어그램을 사용해서 플로우를 정리하는 것을 전제로하는 프레임워크에서 이런 설정은 사용에 따라 독이 될수도 있지만, 어떤 구조에서는 이 일일히 연결하는 작업이 오히려 구성에 있어서 중첩구현, 구조의 복잡화 등을 야기할 수 있다고 생각이 된다.

따라서 diagram에서 scene transition 설정을 일일히 설정하지 않고 암묵적으로 고정값으로 transition을 수행하고 싶은 경우 아래와 같은 실행코드를 command의 실행내용에 첨부하자.

수순

전체에서 공유할 1개의 subsystem과 그 기본 element를 각각 작성후, element에 대해서 공유할 커맨드를 추가하고, 각 subsystem에서 이 element름 참조하여 inheritance base로서 상속하여 Execute{COMMAND}를 각 view에서 호출 가능하게 한것을 전제로 한다.
그냥 다른 subsystem에서 view를 구성하여, 연결하지 않은 상태의 scene에서 공통적인 view를 호출하면 그만인 문제였다. 단, 각 Command는 사용하기 위해서 Registered Instance에 등록 될 필요가 있다. 상속하게 되는경우 base만큼의 메모리가 중첩 사용되어서 메모리 사용에 있어서 좋지 않다.

다이어그램에서 이 COMMAND는 따로 transition설정치 않고, COMMAND의 실행 구문에 아래의 코드를 추가하여 별다른 설정없이 특정 scene으로 transition하게끔 설정한다.

{SceneManager} = 작성한 scene manager의 이름
{LevelName} = scene file name
            GameManager.TransitionLevel<{SceneManager}> (  
                (container) =>   
                     {   
                          container._{SceneManager}Settings = new {SceneManager}Settings ();   
                          // something do on finished.  
                          Debug.Log("transition done");  
                     },  
                     new string[]{"{LevelName}"}  
           );  
상기의 기본 코드를 기반으로 작성한 generic function
 
 using System;

 /// <summary>  
 /// Transition uFrame Scene.  
 ///   
 /// Recommend place this function on singleton class or statically
 ///   
 /// Synopsys  
 /// TransitionScene<TitleScene> ("TitleScene");  
 ///   
 /// Compitable with uFrame 1.5.1 rc2  
 /// @author Donghyun You  
 /// @since 2015. 1. 18.  
 /// </summary>  
 /// <param name="sceneName">Scene name that registered on builtin levels</param>  
 /// <param name="onFinished">On finished.</param>  
 /// <typeparam name="T">The 1st type parameter.</typeparam>  
 public void TransitionScene<T>(string sceneName,Action onFinished=null) where T : SceneManager {  
      if (string.IsNullOrEmpty (sceneName))   
      {  
           throw new ArgumentNullException("sceneName");  
      }  
      Type type      = typeof(T);  
      onFinished      = onFinished ?? delegate() {};  
      GameManager.TransitionLevel<T> (  
           (container) =>   
           {   
                var settingParam = container.GetType ().GetField("_"+type.Name+"Settings");  
                var settingInstance = Activator.CreateInstance(Type.GetType (type.Name+"Settings"));  
                settingParam.SetValue(container,settingInstance);  
                // something do on finished.  
                onFinished();  
           },  
           new string[]{sceneName}  
      );  
 }  

2015년 1월 16일 금요일

[uFrame 1.5] 개발규약

 개요
uFrame을 통한 개발에 있어서 일부 알아두어야할 사전 규약, 구조중 별도 문서화 하기 애매한 기능들을 이하에 정리.

상시 수정함.

본문

1. RenderSettings의 설정
    uFrame에서는 Scene단위로 RenderSetting이 가능하도록 RenderSettings와 같은 내용이
   GameManager에 준비되어 있다. 해당 Scene으로 이동했을때 마다 GameManager>RenderSettings에 설정된 내용대로 다시 설정된다.

   일단 기존의 RenderSettings에서 설정후, GameManager의 LoadFromScene버튼으로 바로 불러오는것 또한 가능하다.

[uFrame 1.5] Scene Transition

개요
uFrame에서 scene transition을 가능하게 하기까지의 수순을 정리
생각보다 복잡하다.

수순

1. SceneManager를 작성
2. SceneManager에 대해 1개의 subsystem을 작성. 이를 connect한다.

subsystem은 scene을 transition할 command를 가지는 1개의 element를 필요로 하며, view또한 필요로 한다.

3. 1개의 SceneManager용으로 작성된 Subsystem에 대해 root element1개를 작성
4. root element에 대해 view를 1개 작성하고 이를 root element에 연결한다.
5. root element에 scene transition용 command를 작성한다.
6. scene간의 이동이므로 적어도 상기 2개의 scene을 구성한다.
7. 각 SceneManager에 대해 각각 transition을 작성, 이동할 scene과 연결한다.
8. Save and compile
9. SceneManager에 대해 실제 Scene을 작성한다(SceneManager의 context menu)
10. 작성된 scene에 4에서 작성한 view를 각각 화면상의 특정 오브젝트에 attach하거나 create함.
11. 각 scene의 SceneManager의 instance에서,<COMMAND> Transition -> Scene의 목록을 늘려 이동할 scene파일의 이름을 지정한다.
12. 작성된 scene파일을 build target에 추가한다
13. Loading이라는 특수 scene또한 build target에 추가한다.
14. 게임을 실행
15. 게임을 실행하면 view component의 inspector는 그 내용이 달라져있다. 제일 아래의 command에서  transition용 command를 실행.

위에서는 정적인 scene에 대해 이동하게 설정하나, string으로 scene파일을 지정하게끔 설정하는것 또한 가능하다. command에 대해 string을 받게끔 설정하고, SceneManager 소스코드에서public override IEnumerable<string> GetToTitleSceneScenes (string arg)를 override하여 return []{arg};와 같이 설정하여 SceneManager insepector에서 설정한것이 아닌 Command의 호출시의 인수에 의거하여 Scene을 이동하게끔 설정한다 (공식 video tutorial에 나와있는 내용)

http://invertgamestudios.com/uFrameAPI/Default/webframe.html#Scene%20Transitions.html

2015년 1월 15일 목요일

[uFrame 1.5] uFrame에서 특정 타입을 diagram에서 이용 가능하게함

개요
uFrame의 plugin을 작성하여, uFrame에서 특정 타입이 이용 가능하게 함.

수순
uFrame의 기본적인 플러그인 소스코드가 아래 패스에 배치되어 있음.
이를 참고로, 동일한 플러그인을 작성하여 Editor폴더 이하에 배치하면 로드됨.

Editor는 굳이 uFrameComplete폴더 이하일 필요는 없음

[Unity Project/Assets]/uFrameComplete/uFrame/uFramePlugins/CorePlugin/Editor/UFrameEditorPlugin.cs

namespace dependancies

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using Invert.StateMachine;
using Invert.uFrame;
using Invert.uFrame.Code.Bindings;
using Invert.uFrame.Editor;
using Invert.uFrame.Editor.ElementDesigner;
using Invert.uFrame.Editor.ElementDesigner.Commands;
using UniRx;
using UnityEngine;


Inheritance

public class UFrameModelTypesPlugin : DiagramPlugin


Required Overrides
 public override decimal LoadPriority  
   {  
     get { return 1; }  
   }  
   public override bool Enabled  
   {  
     get { return true; }  
     set {}  
   }  
   public override void Initialize (uFrameContainer container)  
   {  
     var typeContainer = uFrameEditor.TypesContainer;  

     // Color는 기본 설정에 포함되어있지 않음.
     typeContainer.RegisterInstance (new ElementItemType () {  
       Type = typeof(Color),  
       Group = "",  
       Label = "Color",  
       IsPrimitive = true,  
       IsUnityEngine = true,  
     },"Color");  

   }  

현재 1.5rc2버전을 기준으로
Namespace이하에 배치된 타입에 관해서는 자동 생성에서 하위 namespace route가 없어져버리는 버그가 발생하고 있다. 다른 해결방법을 찾지 못한다면 자동생성후 매번 using namespace ...를 해줘야하는 불편함이 있다.

2015년 1월 4일 일요일

[Sails.js] Sailsjs에서 crontab

개요
sails.js에서 내부 service, model등이 이용 가능한 상태로 구동중인 sails.js app에서 crontab실행을 실시

참고자료
https://github.com/balderdashy/sails/issues/2092#issuecomment-56043637

수순
상기 url에 나와있는대로 sailsjs용 hook을 작성해, config에서 job의 내용을 설정. 실행함.
sails.js의 hook의 스펙이 알기 어려운 경우 sails-generate-hook을 도입해서 sails generate hook [hookname]함

2015년 1월 2일 금요일

[Sails.js] Sailsjs에서 Unit Testing

개요
nodejs, 정확히는 javascript로 상용프로젝트상에서 작업을 진행하기에 있어서, TDD를 적용한 프로젝트진행이 필요하다는 이야기가 들려서 한번 Unit Testing기능을 확인해보고자 한다.

Javascript는 여타 언어와는 다르게 실행되기 전까지는 코드의 문제 조차 확인되지 않는 인터프리터의 일종이므로, 각 기능에 대한 단위별 테스트 기능, 이른바 유닛 테스트의 자동화를 실시할 필요가 있다.

통상적으로 중장기 프로젝트가 아니면 TDD의 도입 자체는 다시한번 생각해봐야 할 부분이긴 하나, 상기 이유때문에 테스트 자동화가 필요하다면 일단 한번은 짚고 넘어가야되지 않는가 하는 생각이 든다.

TDD의 도입을 꺼려하는 이유중의 하나로, 하나의 기능을 만듦으로 인해서 추가적으로 작성되어야할 테스트 코드와, 기능의 사양변화가 빈번한 이 업계에 있어, 이런 변화에 대한 TDD를 위한 유닛 테스트 코드의 추가적인 작성, 변동이 들어지는데 sails.js에서도 이러한 커스텀 코드는 작성 하지 않으면 안되지만, 그래도 어느정도 구조가 잡혀있기 때문에, 비교적 간편하게 테스트 코드를 작성하는 것이 가능하다.

sails.js에서는 mocha라고 하는 테스트 모듈을 공식적인 테스트 모듈로 채택하고 있고, 다른 복잡한 방법을 사용하지 않는다면, 거의 테스트를 위한 코드이 작성 만으로 작업을 마칠 수 있다.

수순
http://sailsjs.org/#/documentation/concepts/Testing
기본적으로 상기 문서의 수순만으로 거의 모든 테스트 환경을 구축할 수 있다.
중간중간 필요로 해지는 모듈은 npm을 통해서 도입하도록 하자.

단, mocha.opts의 설명이 "그냥 이 페이지에 나와있는 설명을 참고로 작성하시오"
라 되어있는데, 이 부분에서 test를 위한 파일이 로드 되게끔 설정할 필요가 있다.
다른 옵션들이 추가된다고 하더라도, mocha.opts의 후미에 하기옵션을 붙여놓도록 하자.

test/**/*.test.js IRC채널에서 힌트를 던져준 da_wunder씨에게 감사의 말씀을 전한다.
하기는 당시 참고로 던져준 샘플이다.
https://github.com/tarlepp/angular-sailsjs-boilerplate/blob/master/backend/test/mocha.opts