fc2ブログ

Numberの整数化タイミングについて考える

JavaScript囲碁ライブラリを修正した。
左が修正前、右が修正後
囲碁ライブラリ修正前
囲碁ライブラリ修正後
線の間隔を等間隔にしていたが、Canvasサイズによっては余白が大きくなる。
そのため、線の間隔を算出する部分を修正した。

<修正前>
線の間隔を求めた際に整数化

<修正後>
線の座標をセットする際に整数化
  var sepx = (this.cvwidth_ - coormarg) / (rownum - 1 + this.banmarg_); // 線の間隔
  this.xArr_ = []; // x座標格納配列
  this.xArr_[0] = banmarg + coormarg;
  for (var i = 1; i < rownum; i++) {
    this.xArr_[i] = Math.round(this.xArr_[0] + sep * i);
  }


負荷を減らすため、Canvasに設定する座標やサイズを整数にしている。
整数化するタイミングや方法がバラバラなので整理することにした。

・整数が期待される引数
ローカル変数にセットする際に~~で整数化。
/**
 * 何路盤かセット
 * @param {number} inrownum 何路盤か。5以上19以下の整数を設定
 */
migolib.Goban.prototype.setrownum = function(inrownum) {
  var rownum = ~~inrownum;

・算出頻度が低く、他の計算に使用されない値
セットする際にMath.round()で整数化。切り捨てが必要な場合のみ~~で整数化。
  /* canvas中心座標 */
  this.centpos = [Math.round(this.cvwidth_ / 2), Math.round(this.cvheight_ / 2)];

・算出頻度が高く、他の計算に使用されない値
セットする際に~~で整数化。

・間隔の長さなど、他の計算に使用される値
整数化しない。
クラスのプロパティなどに値を保持する場合は、コメントに整数でない旨明記。
  /**
   * 碁盤の目の間隔(px)。not integer
   * @type {number}
   * @private
   */
  this.eyesep_ = 0;

ループで加算する際など、他の計算に使用されることを見過ごして誤差を積み上げてしまうことがあるので注意。
  for (var i = 1; i < rownum; i++) {
    // this.xArr_[i - 1]は整数化されているため、丸め誤差が含まれる
    this.xArr_[i] = Math.round(this.xArr_[i - 1] + sep);
  }

とりあえずこんな感じでやってみるか。

<関連エントリ>
[JavaScript]小数を整数に変換する望ましい方法は?
スポンサーサイト



JSゲームエンジンPropulsionJSのソースを読む

引き続き、読めそうなサイズのJavaScriptゲームエンジンを探して読んでいる。
Canvasを使っているエンジンを探して見つけたのがPropulsionJS
ファイルは propulsion.jsのみ。非圧縮で36KB。

ドキュメントを見ると、アクションゲームに必要な機能がそろっているのがわかる。
<機能について気づいたこと>
・PP.Alarmで「指定したフレーム数経過後に指定した関数を起動」を設定できる。
・シーン管理機能は無いが、PP.loop.roomに関数をセットすることで画面を切り替えることができる。オブジェクトの入れ替えなどを行う関数をユーザーが作成する必要がある。
・レイヤー管理機能は無い。PP.draw.depthでオブジェクトごとに管理。
・CSS Spriteには対応していない。同一オブジェクトのアクション画像だけは、等幅画像を横一列につなげて1枚の画像ファイルを作成する必要がある。

ソースを読んでわかったことは以下のとおり。
1.衝突判定がかなり気合入っている
・任意の多角形同士の衝突判定ができる。ただし、デフォルトではキャラクター画像サイズの長方形で判定。
・衝突した結果跳ね返った位置を算出できる。衝突した2つのオブジェクトに対して、跳ね返りの割合をパラメータで設定できる。
・衝突位置を算出できる。
SATアルゴリズムで判定しているとソースのコメントにある。
各多角形の辺と、それぞれの垂直ベクトルとの内積を使っているが、何でそれで衝突判定できるのかわからない。
後でちゃんと調べてみるかな。

2.accessor propertyを活用
ECMAScript 5th editionで導入されたaccessor propertyを使いこなしている。
accessor propertyとは、「プロパティに対してgetやsetが発生したときに呼び出す関数を設定できる」プロパティ。
PropulsionJSでは、次の3種類のやり方で実装している。
get演算子set演算子を使用
 日本語のリファレンスは見つけられなかった。
・Object.definePropertyメソッドを使用
 definePropertyメソッドについては ECMA-262 5th edition で導入された Object.defineProperty を使い、属性を指定してプロパティを定義する に詳しい解説がある。
Object.watchメソッドを使用
 setしか設定できない。PropulsionJSでは、watchメソッドがサポートされていない環境用に、Object.definePropertyで実装したwatchメソッドを提供している。

accessor propertyの利用方法に興味がある人には、PropulsionJSのソースを見る価値があると思う。
代入文を記述するだけで関数が実行されるため、コードがすっきりする。
ただし、コードの一部を読んだだけでは関数が実行されていることに気づかない。


ゲームエンジンよりも、accessor propertyについて勉強になった。
accessor property関連はIE8以前では全く動かないが、そもそもCanvasが動かないからPropulsionJSでは考慮していないのだろう。

JS RPGゲームエンジンingenioJSのソースを読む

ingenioJSは、JavaScriptで記述されたRPGエンジン。
ingenioはスペイン語らしいが、作者はドイツ人のようだ。

ingenioJSはフィールド画面とメッセージの表示機能を提供している。
戦闘画面は提供していないが、フィールドで戦闘するタイプのRPGならエンジンの機能だけでできそうだ。
なお、ajaxを使用しているためサンプルゲームもjsdocもWEBサーバ経由でないと表示できない。
また、getElementsByClassName()などを使っているので、IEでは動かない。

ソースファイルは14ファイルで、非圧縮で計80.6KB。(ただしAudioEngineは含まず)
今のところRPGを作る予定は無いのだが、十分読めるサイズなのでソースコードを読むことにした。

キャラクターデータ、マップデータ、クエストデータをjsonファイルとして作成すると、エンジンが読み込んでゲーム画面を生成する。
マップデータには、プレイヤーから攻撃されたり話しかけられたりした際のアクションも含まれる。
jsonファイルは、ブラウザからajaxで読み込まれる。
マップデータのjsonファイルを作成するエディタも提供されている。
ingenioJSeditor

キャラクターの表示はDOMのCSSで制御している。Canvasは使用していない。
メインループはingenioJS.rendererのコンストラクタでwindow.setInterval()で実装している。
だが、clearIntervalしているところが見当たらない。ループを止めるためのidも取得していない。

メインループをingenioJS.engineで行っていないところは疑問だが、クラスの役割分担が綺麗だと思った。
外部ファイルを読み込んで画面を生成する部分は参考になるかなと思うが、自分にとってはマップ生成エディタのソースが一番参考になりそうな気がする。

JSゲームエンジンMibbuのソースを読む

簡単なアクションゲームを作ろうかと思ったのだが、その前にJavaScriptゲームエンジンについて調べることにした。

ソースが読める程度の小規模エンジンを探して、見つけたのがMibbu
Mibbu, HTML5 javascript game framework, canvas, DOM
圧縮後のファイルサイズは5.59KB。圧縮前でも25.8KB。
作者はポーランドの人みたいだ。

提供している機能は、メインループ、キャラクター画像、背景画像の管理。
HTML5 Canvasを使うか、DOMを使うかを設定できる。DOMを使う場合はCSS3アニメーションをキャラクターに設定できる。
キャラクターの移動のほかに、背景画像の移動も設定できる。
キャラクターの衝突判定機能もある。

小規模アクションゲームとしては十分かなと思ったが、登録したキャラクターの削除はできない。
パラメータを変えて非表示にすればいいだけだが、オブジェクトをうまく再利用する必要がありそう。

ソースコードを読んで参考になったこと
・return this でメソッドチェーンを可能に
・単項プラス演算子で数値変換
  var newTime = +(new Date());
これでDateをNumberに変換している。
・背景画像の360°任意方向へのスクロール
CSSのbackground-positionを算出しているだけだが、自分はやったことが無かったので参考になった。
・CSSアニメーションを動的に設定する方法

ほかにも細かいところでいろいろと参考になった。
他のゲームエンジンも調べてみるかな。

[JavaScript]ダウンロードデータ格納オブジェクトから古いデータを削除する

先日リリースした囲碁定石勉強用サイト「囲碁定石トレーニング」に、定石データを追加した。
ダウンロードした定石データはキャッシュ用オブジェクトに格納するが、定石データが増えたのでキャッシュに上限を設定した。
上限に達した場合、一番古いデータがキャッシュオブジェクトからdeleteされる。

実装は以下のようにした。
・「一番古いデータ」を得るために、キャッシュのメンバ名格納配列を作成
  /**
   * 問題データキャッシュ。メンバ名は問題選択radioのval。
   * メンバの値はダウンロードしたgetdata
   * @type {Object}
   */
  this.pdatacache = {};
  /**
   * 問題データキャッシュのメンバ名格納配列
   * @type {Array.<string>}
   */
  this.pdatacname = [];

・キャッシュを格納するメソッド
/**
 * 問題データキャッシュセット
 * @param {string} probval 問題選択radioのval
 * @param {Object} pdata セットする問題データ
 */
mijoseki.Action_.prototype.setpCache = function(probval, pdata) {
  if (this.pdatacache[probval]) {
    // migoprb.copy()は、オブジェクトをコピーする関数
    this.pdatacache[probval] = migoprb.copy(pdata);
  } else {
    if (this.pdatacname.length >= mijoseki.con.num.climit) {
      // キャッシュ数が上限のため、古いキャッシュを削除
      var delval = this.pdatacname.shift();
      delete this.pdatacache[delval];
    }
    this.pdatacname.push(probval);
    this.pdatacache[probval] = migoprb.copy(pdata);
  }
};

ブログに書くほどのことじゃない気もするが、備忘録として。

JavaScript囲碁ライブラリを修正

プログラミングする時間が全然無かったので、久しぶりのブログエントリだ。

囲碁定石勉強用プログラム作成中、JavaScript囲碁ライブラリにいくつか機能追加をした。
また、いくつかのバグを修正したので、githubに公開しているソースを更新した。
mimami24i/jsIGO - GitHub

盤面をクリアする箇所にバグがあり、バグを修正したときにメソッド名も変更した。
互換性を考えると、プライベートじゃないプロパティやメソッド名は変えない方がいいのだろうけど、使ってる人いないと思って変えた。
変更履歴とかをちゃんと作ったほうがいいのかな。ver1.0になったら考えよう。

本来作ろうとしていた囲碁定石勉強用プログラムができるのはいつの日か。

JavaScriptでSGF構文解析プログラムを作る

チマチマと囲碁定石勉強用プログラムを作っているのだが、定石データを何らかの形式で管理しなければならない。
囲碁の棋譜として広く使われているSGF(Smart Game Format)を使ってみよう。

SGFで記述された定石データを解析するため、parser(構文解析プログラム)を作る必要がある。
既存のプログラムを探したところ、SGFを扱っているプログラムの一覧を見つけた。
Sensei's Library: Smart Game Format
JGo Board がJavaScriptで書かれている。ライセンスは Creative Commons(CC BY-NC 3.0) だ。

ソースコードを読んだが今回の用途とは微妙に合わないので、これを参考にしてSGF parserを作ることにした。
サンプルデータを解析できるぐらいのものはできたが、まだ公開できる状態ではない。

作ってみてわかったが、SGF parserはあまり軽くない。定石データは、parser解析結果のデータで管理しようかと思っている。
今回作ったSGF parserは、定石登録画面で使うことにしよう。

最低限の機能を提供するJavaScript囲碁ライブラリを公開

囲碁定石勉強用プログラムを作ろうとしたのだが、それ以前に碁盤を表示したり碁石を置くプログラムを作らなきゃいけない。
そこで、以下の機能を提供する囲碁ライブラリをJavaScriptで作成した。
・画像を使わず、HTML5 Canvasで碁盤と碁石を描画。碁盤は5路盤から19路盤までをサポート。
・碁盤に石を置く。
・取られた石を消去。
・着手禁止点判定、コウ判定。

囲碁ライブラリの使用サンプルも作って公開した。
JavaScript囲碁サンプル | 無職のHTML5 Canvas
囲碁ライブラリの使用サンプル
excanvas.js を使うと動作が遅いので、IEではFlashCanvasを使って描画している。FlashCanvasだと碁石が歪んでいるように見えるが気にしない。

ソースコードはgithubで公開したが、需要あるのだろうか。
mimami24i/jsIGO - GitHub

[Closure Compiler]型定義チェックでコードの品質を向上する

前のエントリで紹介した、Google製JavaScriptコード圧縮・最適化ツールClosure Compilerだが、コメントで型定義を行うことで強力な警告機能を利用できる。

コメントの構文はJsDocの記法に従う。基本的な書き方は JsDoc Toolkitを使う! - ドックコメントの書き方 を参照。
型定義の記法は、Closure Compilerを使う! - アノテーションによる型定義 を参照。
タグについては、とりあえず以下を押さえておけばいいと思う。
・クラスには @constructor をつける。
・プロパティの型は @type で記述。
・引数の型は @param、戻り値の型は @return で記述。
・複雑な型の別名は @typedef で宣言。
大事なのは型表現なので、ここはひと通り目を通すことをお勧めする。
指定できる型は、Google JavaScript Style Guide - JavaScript Types の Types in JavaScript に一覧がある。

それでは、Closure Compilerの利用方法を説明する。
<テスト対称コード sample.js>
/**
 * @namespace
 */
var ccsample = {};

/** @typedef {{x: number, y: number, z: number}} */
ccsample.Vector;

/**
 * 図形クラス。四角形または三角形を管理。
 * @param {ccsample.Vector} i0 頂点
 * @param {ccsample.Vector} i1 頂点
 * @param {ccsample.Vector} i2 頂点
 * @param {?ccsample.Vector} i3 頂点
 * @constructor
 */
ccsample.Quad = function(i0, i1, i2, i3) {
  /** @type {ccsample.Vector} */
  this.i0 = ccsample.copyVec(i0);
  /** @type {ccsample.Vector} */
  this.i1 = ccsample.copyVec(i1);
  /** @type {ccsample.Vector} */
  this.i2 = ccsample.copyVec(i2);
  /** @type {?ccsample.Vector} */
  this.i3 = ccsample.copyVec(i3);
};

/**
 * Vectorをコピー
 * @param {ccsample.Vector} vec コピー元Vector
 * @return {ccsample.Vector} コピー結果
 */
ccsample.copyVec = function(vec) {
  return {x: vec.x, y: vec.y, z: vec.z};
};

型定義チェックを行うには、--warning_level VERBOSE をつける。
windowsのコマンドプロンプトで実行する場合、実行コマンドは以下のようになる。
java -jar compiler.jar --warning_level VERBOSE --js sample.js --js_output_file sampleout.js > gccout.txt 2>&1
標準出力をgccout.txtにリダイレクトしている。エラーや警告は標準エラー出力に出力されるため、2>&1で標準エラー出力もリダイレクトしている。
コマンド実行結果は以下のとおり。
sample.js:25: WARNING - actual parameter 1 of ccsample.copyVec does not match formal parameter
found : (null|{x: number, y: number, z: number})
required: {x: number, y: number, z: number}
  this.i3 = ccsample.copyVec(i3);
ccsample.copyVecはnullを許容しないのに、上記の箇所でnullが入力される可能性があると警告してくれる。

Closure Compilerのすごいところは、単に型チェックをするだけではなく、コードの解析も行っていること。
この警告を受けて次のようにコードを修正すると、警告が出なくなる。
  /** @type {?ccsample.Vector} */
  this.i3 = null;
  if (i3 !== null) {
    this.i3 = ccsample.copyVec(i3);
  }
条件演算子もちゃんと解析してくれるので、以下のように書いてもいい。
  this.i3 = (i3 === null) ? null : ccsample.copyVec(i3);

このように、型表現を駆使した型定義を書いてClosure Compilerでチェックすることで、想定外のデータが入力される可能性を減らすことができる。
厳密に入力チェックをしていないprivateメソッドなどではかなり有効。

なお、Closure Compiler用の型定義でjsdoc_toolkit-2.4.0を実行したところ、JsDoc Toolkitが認識できないものがあった。
・@typedef を認識しない
・@type の型を{ }で囲った場合、{ }内を出力しない
・配列の型を Array.<...> と書いた場合、< >内を出力しない
JsDocを出力する場合は要注意。

Closure Compilerを使ってJavaScriptコードを圧縮する

githubでソースを公開するようにしたので、GAEにアップするJavaScriptコードを圧縮することにした。
圧縮ツールはいろいろあるが、Googleが提供しているClosure Compilerを使うことにした。
日本語資料は Closure Compilerを使う! が詳しい。

Closure CompilerにはWeb版の Closure Compiler Service UI があるが、一定時間内で連続して利用できる回数などに制限がある。

ローカルPCで圧縮を行う場合、Closure Compiler Application を使用する。
JRE6以上がインストールされていれば、jarファイル1つをダウンロードするだけで特に設定作業は必要無い。

試しにPre3dのコードを圧縮してみた。
windowsの場合、コマンドプロンプトを起動し、compiler.jar のあるフォルダにcdしてコマンドを実行する。
>java -jar compiler.jar --js ..\pre3d\pre3d.js --js ..\pre3d\pre3d_shape_utils.js --js ..\pre3d\pre3d_path_utils.js --js_output_file ..\pre3d\pre3d.min.js
ファイルを圧縮するだけであれば、コマンドオプションは入出力ファイルを設定するだけでいい。
上記のコマンドを実行した結果、合計 66.9KB が 17.8KB になった。圧縮率 26.6% だ。
デフォルトのCompilation LevelsはSIMPLE_OPTIMIZATIONSなので、グローバル変数名は変わらない。
コマンドオプションの詳細は Closure Compiler Application:コマンドラインオプション を参照。
デフォルトの文字エンコーディングは UTF-8 なので注意。

圧縮する際、きちんと構文解析してエラーチェックをする。
  var a = {
    a0: 1,
    a1: 2,
  };
のような、IE7以前でエラーになるケースもチェックしている。
文末の ; が無いと圧縮後ファイルが壊れることも無い。
Closure Compilerは、ファイルサイズよりもgzip圧縮されたときのコードサイズを優先するので、圧縮後のファイルサイズが大きくなるケースがある。

JSDocの @license タグをサポートしており、
/**
 * @license Pre3d, a JavaScript software 3d renderer.
 * (c) Dean McNamee , Dec 2008.
 */
と記述すると、この部分は圧縮されずに
/*
 Pre3d, a JavaScript software 3d renderer.
 (c) Dean McNamee , Dec 2008.
*/
と出力される。圧縮後のファイルに著作権表示などを書く手間がはぶける。

ざっと使ってみて、JavaScriptコード圧縮ツールとしてかなり使えることがわかった。
更に Compilation Levels を ADVANCED_OPTIMIZATIONS にして使用したところ、コードの品質向上にも使えることがわかった。
次エントリでADVANCED_OPTIMIZATIONSについて解説する予定。
<追記>
コードの品質向上について書きました。
[Closure Compiler]型定義チェックでコードの品質を向上する
型定義チェックはデフォルトのSIMPLE_OPTIMIZATIONSでも可能でした。
プロフィール

himax64

Author: 南西
30代後半の無職です。
就活もせずダラダラ生きてます。
作ったもの

最新記事
人気記事
検索フォーム
カテゴリ
月別アーカイブ
最新コメント
最新トラックバック
RSSリンクの表示
QRコード
QRコード
カウンター