Kengo's blog

Technical articles about original projects, JVM, Static Analysis and TypeScript.

The Art of Enbugging

I've read The Art of Enbugging (PDF) by Andy Hunt and Dave Thomas.

This is little long, but good article to share your topic with teammate. If you're trying to improve maintainability of your product, it will help your communication.

I feel that "The Paperboy and the Wallet" is good example, we can feel what is wrong and understand how to solve. I agree that Law of Demeter for Functions is good to keep software easy to maintain.

And, about "Shy". I know that a lot of developers are shy, this should become good metaphor to tell for beginners.

When/How to use goog.ui.Component#makeId()

goog.ui.Component#makeId() is a helper function to generate ID for DOM elements. Let's start discussion based on sample component which contains following DOM:

<form>
  <div>
    <input type="text">
  </div>
  <button type="submit">
</form>

To implement createDom() method, we may need to decide ID for each element. And of course each of them should be unique in the document.

How you'll solve this problem? Use random value as ID? Or create counter to use sequential value? It looks troublesome.
So it's time to try makeId() method! It will use goog.ui.IdGenerator as counter to generate sequential value, and generated ID starts with ':' so it will not conflict other hand-made value so easily.

/** @override */
jp.skypencil.Component.prototype.createDom = function() {
  var domHelper = this.getDomHelper();

  var $input = domHelper.createDom(goog.dom.TagName.INPUT, {
    type : 'text',
    id: this.makeId('input') // will return a string like ':1.input'
  });
  var $div = domHelper.createDom(goog.dom.TagName.DIV, {
    id : this.makeId('div') // will return a string like ':1.div'
  }, $input);
  var $button = domHelper.createDom(goog.dom.TagName.BUTTON, {
    type : 'submit',
    id : this.makeId('button') // will return a string like ':1.button'
  });

  var $form = domHelper.createDom(goog.dom.TagName.FORM, {
    id : this.makeId('form')  // will return a string like ':1.form'
  }, $div, $button);
  this.setElementInternal($form);
}

getElementByFragment() helps you to get Element easily

Here is another merit to use makeId(); you can get Element instance simply like below:

var $button = this.getElementByFragment('button');

Stay away from setId() and getId()

goog.ui.Component also has setId() and getId(), but this is not for handling id attribute of DOM element. This is for handling Component-ID will be used to maintain component tree.

It is not necessary to keep Component-ID and DOM element ID same. I recommend you to stay away from these 2 methods, you do not have to care about component tree handling (it's responsibility of closure-library!).

Reference

goog.Promise の使い方

日本語情報少ないので。

Promiseとは?

こちらをご覧ください。

Promiseオブジェクトを作る

newを使ってコンストラクタを呼ぶだけです。このとき、引数に関数をひとつ渡してやる必要があります。この関数はresolve関数とreject関数を受け取るので、関数内で

  • 処理成功時は resolve
  • 処理失敗時は reject

を呼んでやる必要があります。それぞれ引数に値をひとつ渡すことができ、他の処理に結果を受け渡すことができます。

var promise = new goog.Promise(function (resolve, reject) {
  if (...) {
    resolve(result);
  } else {
    reject(err);
  }
});

コンストラクタ引数は無名関数ではなく、メソッドあるいはローカルな関数として定義しても良いですね。公式ドキュメントではresolverと命名されています。

function resolver(resolve, reject) {
  if (...) {
    resolve(result);
  } else {
    reject(err);
  }
}

var promise = new goog.Promise(resolver);

成功時の処理を書く

var promise = new goog.Promise(resolver);
promise.then(function(result) {
  // resolve() の引数に渡した値をここで使える
  console.log(result);
});

失敗時の処理を書く

var promise = new goog.Promise(resolver);
promise.thenCatch(function(err) {
  // reject() の引数に渡した値をここで使える
  console.log(err);
});

必ず実行したい処理を書く

var promise = new goog.Promise(resolver);
promise.thenAlways(function() {
  // リソース解放など
});

合わせ技

then などのメソッドは自分自身を戻り値として返すので、メソッドチェーンが可能です。

var promise = new goog.Promise(resolver)
  .then(displayDialog)
  .thenCatch(displayAlert)
  .thenAlways(unlockSubmitButton);

Promiseの合成

静的メソッドとして、以下3種類が用意されています。

  • goog.Promise.all(promises)
  • goog.Promise.firstFulfilled(promises)
  • goog.Promise.rase(promises)

allはすべてresolveされた時のみresolveされるものです。thenにはすべてのPromiseの結果が配列に入れられて返ります。必要なリソースを並列で用意するときに利用できるでしょう。ES6のやつと同じです。

goog.Promise.all(assetLoader, dataLoader, calculator)
  .then(function(result) {
    var asserts = result[0];
    var data = result[1];
    var computed = result[2];
    doFoo(asserts, data, computed);
  });

firstFullfilledは、最初にresolveされた結果を返します。ひとつでもresolveされたら合成後のPromiseはresolvedになります。すべてrejectされたときのみrejectとなり、thenCatchが呼び出されます。同じ資源を複数の経路で準備できる際など、ひとつでも成功すれば良い・最も早く取得できた結果を使えれば良い時に利用できるでしょう。

goog.Promise.firstFullfilled(loadDataFromCDN, loadDataFromServer)
  .then(function(data) {
    doFoo(data);
  });

raseは最初にresolve/rejectされたPromiseの結果を返します。ES6のやつと同じです。

goog.resultとgoog.async.Deferred

これらは非推奨と明記されています。新規の利用は控えましょう。

  • NOTE: goog.result is soft deprecated - we expect to replace this and
  • {@link goog.async.Deferred} with {@link goog.Promise}.

Tips to accelerate your original game

To make your game more interesting, you should pay much attention to keep high performance. This is UX problem, but it is not designer who can solve. Only developer has solution for this problem, so we should learn about when we face problem and how many approaches we have.

Reducing I/O

Cache

This is popular tip for background-image, map-chip and character-chip.

Load picture and draw it onto cache on RAM, then you do not have to access to storage. For instance, your STG may load background-image when player decided the stage to play. By this cache, your STG does not have to read storage for background image during this stage.

Lazy-loading

In previous case, your STG needs to lock screen while it loads resources onto RAM*1. It's better to shorten this time, or player will feel that your game is boring.

To reduce locking, lazy-loading can be solution. At initialising phase, your STG does not have to load background-image of boss-stage. Postpone loading, then you can shorten the lock.

Pre-loading

Here is another and opposite solution. While player is choosing stage to play, your STG can load resources onto RAM. When player decides stage, necessary data is already loaded so you can start game immediately for player.

To use this tip, programmer should understand player's need and how they control your game. For instance, when player enters stage-select page, your STG may load SE (Sound Effect) of shooting action because you knows that player will choose stage so your STG should load such resources later. I think this is similar to implementing recommendation engine.

Are you implementing HTML5 game? If so, you may try pre-fetch API.

Reuse drawn picture

During user plays your STG, you should draw background-image and scroll it from right to left. So in each frame you should draw large picture, it costs resource of player's computer.

To reduce this cost, you can reuse background-image which is already drawn. You shift background-image some pixels, and draw only the right part. You can reuse almost all of drawn picture, and focus on drawing new parts.

Here is another example: even though you are implementing 3D game, you do not have to compute 3D models in each frame, because you can store computed images to RAM and use it instead.

Put multiple resource into one file

This is popular tip for map-chip and character-chip. And this is also very famous for web developer as CSS Sprites. Data URI also can be solution.

By this tip, you can reduce number of resource to load. So it's effective especially when resource is far from player. Learn latency for detail.

Compress data

You may like raw data format like BMP format, but when you publish your product it's better to use compressed format. Then you can reduce data size to load, so it will reduce time to load. Learn Throughput for detail.

To compress data, your may use ZIP or other format. It's OK but please remember that decompression costs machine resource.

Reducing computing cost

divide main-loop and loop-to-render

When you implement graphical game, you should have main-loop like below:

function main(){
  compute();
  render();
  setTimeout(main, 15); // about 60 FPS
}
main();

It works, but it has one problem.

This code tries to render image 66.6 times per second, but in my environment 6 of them has no meaning because we render twice or more between 2 display refresh. You can try it at this page.

It means that we render needless picture 6 times per second... it should be stopped.

var previousTimeToRender = 0;
function tryToRender(){
  var now = Date.now();
  if (now - previousTimeToRender > 1000/60) {
    previousTimeToRender = now;
    draw();
  }
  requestAnimationFrame(tryToRender);
};

function main(){
  compute();
  setTimeout(main, 1000/60);
}

main();
requestAnimationFrame(tryToRender);

Looks too complex? Then capsule it:

function Renderer(fps) {
  this.fps_ = fps;
  this.previousTimeToRender_ = 0;
  this.callback_ = null;
};
Renderer.prototype.tick = function(){
  var now = Date.now();
  if (now - this.previousTimeToRender_ > 1000/this.fps_) {
    this.previousTimeToRender_ = now;
    this.callback_();
  }
  requestAnimationFrame(this.tick.bind(this));
};
Renderer.prototype.register = function(callback){
  this.callback_ = callback;
  this.tick();
};

// then you can code like below:
function main(){
  compute();
  setTimeout(main, 1000/60);
}
main();
var renderer = new Renderer(60);
renderer.register(render);

And if we can reduce FPS to draw without UX problem, it's also good way to improve performance.

var renderer = new Renderer(30); // draw 30 times per seconds. we can keep computing 60 times per second.

Algorithm

Algorithm is very important to implement game. Routing, Z-sort, collision detection, AI... there are a lot of algorithm used in game. Brute-force is acceptable during development, but before you publish your game, it's better to consider to replace it with efficiently implementation.

This is too large topic for this article, so I just list up sample codes:

Now I'm interested in Simulated Annealing, I'll try to code a simple one.

*1:you may know that some PSP games display 'LOADING...' again and again

Intel Edisonを購入した

Intel Edison Breakout Board Kitを淘宝网にて515人民元で購入。当然ながら技適マークは無いです。

まずは以下を試験実施。

wget, python, node が動くことを確認。

翌日起動したところ *** Ready to receive application *** から進まなくなった(U-Bootが起動しなくなった)のでここを参考に再インストール。

$ brew install dfu-util coreutils gnu-getopt
$ cd edison-image-rel1-maint-rel1-ww42-14
$ ./flashall.sh
Using U-Boot target: edison-blank
Now waiting for dfu device 8087:0a99
Please plug and reboot the board
Timed out while waiting for dfu device 8087:0a99
DEBUG: lsusb
./flashall.sh: line 77: lsusb: command not found
DEBUG: dfu-util -l
dfu-util 0.8

Copyright 2005-2009 Weston Schmidt, Harald Welte and OpenMoko Inc.
Copyright 2010-2014 Tormod Volden and Stefan Schmidt
This program is Free Software and has ABSOLUTELY NO WARRANTY
Please report bugs to dfu-util@lists.gnumonks.org

Did you plug and reboot your board?
If yes, please try a recovery by calling this script with the --recovery option

DFUデバイスとして認識されていない。--recoveryはOSXで使えないので対応を考える。

Windowsマシンに切り替えて再接続すると、特に苦労なく2つのLEDが点灯し起動完了。OSXとの相性問題?flashall.shのサポート状況を見ても、開発にはLinuxWindowsを選択したほうが無難だろう。

と、思っていたらWindowsが起動しなくなった。ドライバ入れたから?OS再インストールが必要そう。OSXでは相変わらず。

Windowsは(MacBook ProのBootcampで動いているので)パラメータRAMのリセットを行うことでOSXが起動するように。念の為OSXを最新にあげて再挑戦したところ、とりあえずWindowsは起動した。USB接続で2つのLEDが点灯することも確認済み。

goog.ui.Componentの継承で気をつけるべきこと一覧

基本

インスタンスの状態

  • クラスフィールド(static変数)にインスタンスの状態を入れない。
  • インスタンスフィールドはprivateにする。
  • 親クラスのフィールドには触れない。

DOM要素の操作

  • DOM要素を指定する際にはIDではなくclassやカスタムdata属性などを使う。
    • IDは、コンポーネントが画面に常に一つしか存在しないことを保証できる時のみ利用する(1つの画面に2つ以上同じIDを置けないので)。
    • Closure Libraryはclassによる要素検索メソッドを多数提供しているので、classを使うことが望ましい。
  • 自分自身のDOM要素に含まれるDOM要素のみ触ることが望ましい。
    • goog.dom.getElementByClass() などgoog.dom名前空間にあるメソッドやDomHelperのメソッドよりも、 this.getElementsByClass() などgoog.ui.Componentに用意されたメソッドを使うことが望ましい。

各メソッドで気をつけること

コンストラクタ

  • 必ずgoog.base(this, opt_domHelper)する。DomHelperは省略して良いが、引数で受け取る癖をつけて統一感を持たせる。
  • この時点ではDocumentに入っていない状態かつ自分自身のDOM要素がない状態。DOMの更新は控える。
  • コンポーネントインスタンスは極力この時点で作成する。あるいはコンストラクタ引数として受け取る。
  • this.addChild(childComponent)は親子でライフサイクルを統一するためにも効果的。これによって自身がdisposeされたタイミングで自動的に子コンポーネントもdisposeされるようになる。何らかの理由によりthis.addChild()できない場合はthis.registerDisposable(anotherComponent)しておくと良い。
/**
 * @constructor
 * @extends {goog.ui.Component}
 * @param {goog.dom.DomHelper=} opt_domHelper
 */
my.ui.Component = function(opt_domHelper) {
  goog.base(this, opt_domHelper);
  this.addChild(new my.ui.ChildComponent(this.getDomHelper()));
};
goog.inherits(my.ui.Component, goog.ui.Component);

createDom()

  • 必ずthis.setElementInternal(element)する。
/** @override */
my.ui.Component.prototype.createDom = function() {
  var element = this.getDomHelper().createDom(goog.dom.TagName.DIV, { 'data-foo': 'bar' });
  this.setElementInternal(element);
};

canDecorate()

  • decorateできるDOM要素に制限がある場合は、ここでそのチェックを行う。
  • falseを返した時のエラーメッセージ(goog.ui.Component.Error.DECORATE_INVALID)は汎用的なものなので、decorateに適さない理由をデバッグログに出しておくと良い。
/** @override */
my.ui.Component.prototype.createDom = function(element) {
  return element.tagName === goog.dom.TagName.DIV;
};

decorateInternal()

  • 必ずthis.setElementInternal(element)する。またはgoog.base(this, 'decotateInternal', element)でも良いが、特に親クラス実装を呼び出す必要性はない。
/** @override */
my.ui.Component.prototype.decorateInternal = function(element) {
  this.setElementInternal(element);
};

enterDocument()

  • まずgoog.base(this, 'enterDocument')してから、自クラス固有の処理を行う。
  • goog.base(this, 'enterDocument')は必ず呼び出すこと。
  • イベントのlistenはgoog.events.listen()ではなくthis.getHandler().listen()によって行う。これによってexitDocument時に手動でunlistenをする必要がなくなる。
/** @override */
my.ui.Component.prototype.enterDocument = function() {
  goog.base(this, 'enterDocument');
  this.getHandler().listen(this.getElement(), goog.events.EventType.CLICK, function(event) { ... });
};

exitDocument()

  • まず自クラス固有の処理を行ってから、goog.base(this, 'exitDocument')を呼び出す。
  • goog.base(this, 'exitDocument')は必ず呼び出すこと。
  • enterDocument時に自身や他のオブジェクトに加えられた変更があれば、これを巻き戻す。ただしthis.getHandler().listen()で作成されたイベントハンドラは親クラス実装で自動的にunlistenされるので気にする必要はない。
/** @override */
my.ui.Component.prototype.exitDocument = function() {
  // do some operation if we need

  goog.base(this, 'exitDocument');
};

disposeInternal()

  • まず自クラス固有のクリーンアップ処理を行ってから、goog.base(this, 'disposeInternal') する。
    • 自クラス固有の処理を先に行うとdispose()実行時に、自クラス固有のdisposeInstance→自クラス固有のexitDocument→親クラスのexitDocument→親クラスのdisposeInstanceの順に時に処理が行われることになり、exitDocumentがdisposeInstanceに遅れて実行されてしまう。
    • 公式に推奨されている。
  • goog.base(this, 'disposeInternal')は必ず呼び出すこと。
  • インスタンスフィールドにある他のオブジェクトへの参照をnull代入あるいはdelete演算子により断ち切ること。
  • this.isDisposed()が真かどうかは気にしなくて良い(偽であることを前提にして良い)。dispose()がthis.isDisposed()が偽の時のみdisposeInternal()を呼び出してくれる。
/** @override */
my.ui.Component.prototype.disposeInternal = function() {
  goog.base(this, 'disposeInternal');

  // do some operation if we need
};

getContentElement()

  • コンポーネントを追加するためのDOM要素がthis.getElement()と異なる場合のみ、このメソッドをoverrideする。
/** @override */
my.ui.Component.prototype.getContentElement = function() {
  return this.getRequiredElementByClass('container');
};

継承してはいけないメソッド

  • setElementInternal()
    • JSDocに Considered protected and final と明記されている
  • dispose()
    • 代わりに disposeInternal() を継承する
  • decorate()
    • 代わりに decorateInternal() を継承する
  • render(), renderBefore()
    • 代わりに createDom() を継承する
  • その他、継承する必要がないもの(ドキュメントには明記されていないが、これら標準実装に手を加える必要性はないものと思われる)
    • getElement()
    • getElementBy...()
    • getParent()
    • getRequiredElementByClass()
    • wasDecorated()
    • コンポーネントを扱うメソッド全般

参考資料

  1. goog.ui.Component のはぐれかた
  2. http://docs.closure-library.googlecode.com/git/class_goog_ui_Component.html
  3. GitHub - google/closure-library: Google's common JavaScript library

memo: RabbitMQ

TODO

  • 2回以上実行される可能性(結果報告前にConsumerが落ちる,タイムアウト etc.)と、そのために必要な設計(INSERT処理のみにする=更新・インクリメント処理は避ける etc.)
  • キューを作り分ける意味とその狙い。例えば動的スケールするべきキュー(即実行すべきJob)と処理が遅延してもよいキュー(永続性を優先すべきJob)に分ける?

Reference