기본 콘텐츠로 건너뛰기

RxJS 에서 GameScene 을 구현하고 있다.

결과물은 여기에
http://jsbin.com/wuhega/edit?js,output

Reactive Programming 의 장점+효용이 극대화되는 분야는 아무래도 게임이지 않을까 싶어서 짬을 내어 RxJS로 슈팅게임을 만들고 있다.

  1. 배경+플레이어+총알: https://jsbin.com/worewow/edit?js,output
  2. 1+적+폭파에니메이션: https://jsbin.com/modedic/edit?js,output
  3. 2+coffee+적 움직임등 : https://jsbin.com/yobuka/
대략, 이런 흐름으로 살을 조금씩 붙여가면서 올리고 있는데
어느정도 튜토리얼을 만들려는 생각으로 ECMA6를 가지고 시작을 했지만 {}, (), [] 괄호노동을 통한 손목터널증후근과 코딩하는 시간 만큼 쌍 맞추는 시간이 소모되는 자신을 발견하고 개인의 행복을 위해(...) 다시 coffeescript로 바꿔서 진행하고 있다. 살 것 같다. 아효.

아마도 곧 합쳐질 것 같지만 어느정도 내용물이 만들어졌으니 이것들을 감싸는 껍데기를 어떻게 만들지 코드를 짜면서 고민해보았다.

일단 크게 나누어 보기를
  1. Splash Scene - 한번만 노출. 게임명과 제작자에 대해 알리는 화면
  2. Title Scene - 게임시작을 위해 대기하는 화면
  3. Game Scene - 실제 게임 화면
  4. GameOver Scene - 게임을 더 이상할 수 없는 상황.
이정도로 잡고 1>2>3>4>2>... 의 반복 구조로 설계를 해보기로 했다.

아무런 레퍼런스도 없고 검색해 본 것도 없어서 매우 삽질이 예상되지만 그래도 FRP(Functional Reactive Programming)원칙에 충실하려고 노력했다.

먼저, 화면을 준비한다.
'use strict'
### setup canvas ###
c = document.createElement 'canvas'
document.body.style.margin = 0
document.body.style.height = '100%'
document.body.style.overflow = 'hidden'
document.body.appendChild c
c.width = window.innerWidth
c.height = window.innerHeight
ctx = c.getContext '2d'

캔버스를 만들고 scroll이 되지 않게 margin을 0, 높이를 100%, 그리고 스크롤바도 숨겼다.
캔버스의 크기는 윈도우의 크기만큼 꽉 차게.

두번째로 각각의 Scene을 Observable로 준비한다.
FPS = 16
SplashScene = Rx.Observable.interval FPS
TitleScene = Rx.Observable.interval FPS
GameScene = Rx.Observable.interval FPS
GameOverScene = Rx.Observable.interval FPS
FPS(Frame per Second)를 60frame으로 잡기 위해 1000msec/60frame 한 값이 16.666을 거칠게 16으로 주고 각 Scene 들이 16msec 에 한번씩 canvas 를 그리는 구조를 잡기 위해 위와 같이 설정했다.

이번엔 말 그대로 "껍데기"를 만들 것이므로 최대한 각 Scene들을 단순하게 구현하고자 했다.

시작은 Splash부터
paintSplash = (t)->
  int2hex = (i)-> "0#{i.toString(16)}".toString(16).substr(-2)
  q = int2hex (255-t*3)>0 && 255-t*3 || 0
  ctx.fillStyle = "##{q}#{q}#{q}"
  ctx.fillRect 0, 0, c.width, c.height
  ctx.fillStyle = "#eaeaea"
  ctx.font = "3rem arial"
  ctx.textAlign = "center"
  [x,y]=[c.width/2, c.height/2]
  ctx.fillText "Appsoulute", x, y - 25
  ctx.fillText "games", x, y + 25 if t>150
goSplash = ->
  console.log "enter splash"
  SplashScene
  .takeUntil Rx.Observable.timer 5000
  .subscribe paintSplash, (->), goTitle
goSplash()
각 Scene들은 여기에서 크게 다르지 않은 구조다.
  1. 해당 Scene을 subscribe 하도록 하는 함수를 만들어 호출할 것이며 (goSplash())
  2. subscribe 시 canvas에 요소들을 그릴 것이며 (paintSplash)
  3. 각 Scene을 종료하는 제약조건을 가질 것이며 (takeUntil)
  4. 3의 제약조건으로 종료되고 난 뒤 다음 Scene을 생성할 함수를 호출할 것이다. (goTitle)
이게 전부고 큰 뼈대라고 보면 되겠다.
SplashScene 을 Rx.Observable.interval FPS 로 정의했다.
FPS값, 즉 16msec (0.016초) 마다 subscribe에선 주기적으로 증가하는 값을 받을 것이며 이는 paintSplash 에 산술적으로 증가하는 정수값을 전달할 것이다.
Rx.Observable.interval(100).takeUntil(Rx.Observable.timer(5000)).subscribe(t=>console.log(t))
하고 콘솔에 한 번 입력해보자. 5000msec(5초)동안 0부터 100msec 마다 1씩 값이 증가하면서 출력되는 것을 확인할 수 있다.

paint시리즈의 시작은 canvas 영역만큼 페인트를 확 들이 붓는 것부터 시작하는데 이건 매 frame (정확히 말해 각 Stream의 interval)마다 딱 한번씩만 실행한다.
paintSplash의 인자 t를 기억하자. 애니메이션을 할때 키가 되는 값이다.
  int2hex = (i)-> "0#{i.toString(16)}".toString(16).substr(-2)
  q = int2hex (255-t*3)>0 && 255-t*3 || 0
  ctx.fillStyle = "##{q}#{q}#{q}"
  ctx.fillRect 0, 0, c.width, c.height
배경색을 지정할때 "#000000"식으로 RGB를 표현하는데
255부터 거꾸로 세어서 #ffffff , #fefefe , #fcfcfc .... #020202, #010101, #00000 에 이르도록 배경을 처리하였다.
"#{variable}"은 ECMA6의 `${variable}`과 완전히 같다.
fillStyle 에서 색을 지정하고 fillRect로 0,0 에서 canvas 높이와 너비 만큼 칠한다.

폰트의 경우
  ctx.fillStyle = "#eaeaea"
  ctx.font = "3rem arial"
  ctx.textAlign = "center"
의 세가지 속성을 사용했다.
fillStyle로 글씨에 사용할 색을 변경하고
font로 폰트의 크기(3rem: 해당페이지 기본 폰트에 x3배)와 이름(arial: 기본폰트)을 지정한 뒤
textAlign로 가로 정렬을 잡아준다.
textAlign은

이와 같은 특성을 가지고 있어서 매우 유용하다. start, end, left, center, right 속성을 바꿔가면서 비교해보자.
다시, subscribe 쪽으로 돌아와서 보면
.takeUntil Rx.Observable.timer 5000
takeUntil는 인자로 들어오는 Observable에 Stream이 발생할 때 해당 Observable을 종료하는 Operator이다.
subscribe가 시작되고 5000msec(5초가 지나면) takeUntil이 complete 를 발생시켜 Observable을 중단하고 subscribe에 지정한 complete 콜백을 실행한다.

.subscribe paintSplash, (->), goTitle

subscribe는 세개의 인자를 갖는데 각각 next, error, complete 이다. 세번째 인자인 goTitle 을 지정하여 결과적으로 5초 지나고 난 뒤 해당 함수로 이동한다.
https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/subscribe.md
내용을 참조하여 사용예를 익히자.

Splash가 5초 지나고 Title을 부르면 나머지는 다 비슷한데 시간을 기다리는 것이 아니라 클릭을 하면 다음으로 넘어가도록 구현한다.
goTitle = ->
  TitleStream = Rx.Observable.fromEvent c, "mouseup"
  TitleScene
  .takeUntil TitleStream
  .subscribe paintTitle, (->), goGame
goSplash와 거의 같은 데 제약 조건이 canvas에서 mouseup 이벤트를 fromEvent를 통해 스트림으로 받았다.
그러면 Title Scene은 마우스 클릭이 있기까지 Observable이 유효한 상태이고 마우스를 눌렀다 떼는 순간 complete을 하고 goGame으로 향한다.
goGame = ->
  console.log "enter game"
  GameStream = Rx.Observable.fromEvent document, "keydown"
  .bufferCount 3
  GameScene
  .takeUntil GameStream
  .subscribe paintGame, (->), goGameOver
goGame에선 제약 조건을 다르게 해봤는데 bufferCount 라는 Operator를 사용했다.
bufferCount는 buffer의 일종으로 해당 Observable이 3번 일어날 때 활성화가 되어 takeUntil을 실행한다.
그렇다. 이는 Life 카운트를 구현하기 위한 것으로 적과 충돌 같은 사망 스트림이 3번 발생한 경우로 goGameOver로 향하게 하는 장치라고 보면 되겠다.
구현을 단순화 하기 위해 keydown을 통해 세번 아무 키나 누르면 goGameOver 로 가게 했다.

마지막으로 GameOver를 보자.
사실상 GameOver는 Splash랑 거의 같다.

goGameOver = ->
  console.log "enter gameOver"
  GameOverScene
  .takeUntil Rx.Observable.timer 3500
  .subscribe paintGameOver, (->), goTitle

3500msec(3.5초)만큼 기다리고 그저 goTitle로 향한다.

이와 같이 하나의 루프가 완성되었다.
더 좋은 방법이 있을 법도 하지만 거꾸로 하나하나 Scene을 만드는 것도 꽤 재밌는 작업이었다.
import 나 외부 script 참조를 통해 적절하게 Scene 별로 파일을 나누면 더욱 보기 좋을 것이다.

기실 어떤 프로그램이든 좋은 껍데기 구조는 수익+평판이랑 직결된다. 좀 더 예리하게 다듬어보자.

이 블로그의 인기 게시물

Rinkeby Test Network에 접근하는 간단한 방법.

dApp 개발 시 실제 계정으로 트랜젝션을 보내면 너무나 비싸므로
Rinkeby나 Ropsten 같은 테스트 네트워크에 연결하여 마이닝 없이 faucet을 통해 ether를 받고
그걸로 트랜젝션 테스트를 하면 편리하다.

보통 https://github.com/ethereum/wiki/wiki/Dapp-using-Meteor#create-your-%C3%90app 문서를 보고 시작하는데
geth --rpc --rpccorsdomain "http://localhost:3000" 이렇게 하면 마이닝부터 해야하니 귀찮다.
https://infura.io/#how-to 를 보고 계정을 신청하자. 이런 것도 호스팅이 되다니 좋은 세상이네.
간단한 개인 정보 몇가지를 입력하고 나면 Access Token이 나온다.

가입 후  https://infura.io/register.html 화면

Access Token이 있는 네트워크 주소로 geth를 연결한다.
geth --rpc --rpccorsdomain "https://rinkeby.infura.io/<YOUR_ACCESS_TOKEN>" 이러면 오케이.

meteor project를 만들고
meteor add ethereum:web3 추가한 다음 console에서
web3.eth.getBalance(web3.eth.coinbase, (error,result)=>console.log(
  error, result.toFormat()
)); 자신의 coinbase의 잔액을 구해보자.
6eth가 최소단위인 wei로 보면 6,000,000,000,000,000,000 정도.
https://faucet.rinkeby.io/ 여기에서 받아온 (무료로/마이닝없이) ether가 잘 나온다.
여기서부터 시작하는게 좋아보인다.

ESP32 DevBoard 개봉기

오늘 드디어 손에 넣었다. ESP32 DevBoard!
Adafruit 에서 15개 한정 재입고 트윗을 보고 광속 결제.
그리고 1주일의 기다림. 사랑해요 USPS <3
알리를 이용하다보니 1주일 정도는 광속 배송임.
물론 배송비도 무자비함 -_ㅜ
15개 한정판 adafruit 발 dev board
그놈이 틀림없으렸다.
오오 강려크한 포스
ESP32_Core_board_V2라고 적혀있군요.
ESP32 맞구요. 네네. ESP32-D0WDQ6 라고 써있는데 D → Dual-core 0 → No internal flash W → Wi-Fi D → Dual-mode Bluetooth Q → Quad Flat No-leads (QFN) package 6 → 6 mm × 6 mm package body size 라고 함.
길이는 이정도
모듈크기는 이정도
코어는 6mm밖에 안해! 여기에 전기만 넣으면 BLE+WIFI!
밑에 크고 발 8개 달린 놈은 FM25Q32라고 32Mbit 플래시메모리
ESP8266 DevBoard 동생이랑 비교 크고 아름다운 레귤레이터랑 CP2102 USB Driver가 붙어있음.
ESP8266 DevBoard엔 CH340G 인데 확 작아졌네.
머리를 맞대어 보았음.
모듈크기는 아주 약간 ESP32가 더 큰데 워낙에 핀이 많고 촘촘함. ESP8266인 ESP12는 핀 간격이 2.00mm인데 비해
ESP32는 1.27mm 밖에 안함.
딱봐도 비교가 될 정도.
https://www.sparkfun.com/news/2017 크고 아름다운 Pinouts

ESP8266 보드랑 별로 안달라보인다.
http://www.silabs.com/products/mcu/pages/usbtouartbridgevcpdrivers.aspx#mac
에서 CP2102 드라이버를 설치하고
screen 으로 연결해보자.
내 경우엔 tty.SLAB_USBtoUART 로 잡혔다.
어디서 기본 속도가 115200bps 라고 들은 적이 있어서
screen /dev/tty.SLAB_USBtoUART …

MQTT 접속해제 - LWT(Last will and testament)

통신에서 중요하지만 구현이 까다로운 문제로 "상대방이 예상치 못한 상황으로 인하여 접속이 끊어졌을때"의 처리가 있다.

이것이 까다로운 이유는 상대방이 의도적으로 접속을 종료한 경우는 접속 종료 직전에 자신의 종료 여부를 알리고 나갈 수 있지만 프로그램 오류/네트웍 연결 강제 종료와 같은 의도치 않은 상황에선 자신의 종료를 알릴 수 있는 방법 자체가 없기 때문이다.
그래서 전통적 방식으로는 자신의 생존 여부를 계속 ping을 통해 서버가 물어보고 timeout 시간안에 pong이 안올 경우 서버에서 접속 종료를 인식하는 번거로운 방식을 취하는데

MQTT의 경우 subscribe 시점에서 자신이 접속 종료가 되었을 때 특정 topic으로 지정한 메시지를 보내도록 미리 설정할 수 있다.
이를 LWT(Last will and testament) 라고 한다. 선언을 먼저하고 브로커가 처리하게 하는 방식인 것이다.

Last Will And Testament 라는 말 자체도 흥미롭다.
법률용어인데 http://www.investopedia.com/terms/l/last-will-and-testament.asp
대략 내가 죽으면 뒷산 xx평은 작은 아들에게 물려주고 어쩌고 하는 상속 문서 같은 내용이다.

즉, 내가 죽었을(연결이 끊어졌을) 때에 변호사(MQTT Broker - ex. mosquitto/mosca/rabbitMQ등)로 하여금 나의 유언(메시지)를 상속자(해당 토픽에 가입한 subscriber)에게 전달한다라는 의미가 된다.

MQTT Client 가 있다면 한번 실습해보자.
여러가지가 있겠지만 다른 글에서처럼 https://www.npmjs.com/package/mqtt 을 사용하도록 한다.

npm install mqtt --save 로 설치해도 되고 내 경우는 자주 사용하는 편이어서 npm install -g mqtt 로 전역설치를 했다.

호스트는 무료 제공하고 있는 test.mosquitto.org 를 사용한다.
실 사용시엔 -h 옵션을 …