スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

JsUnitを触ってみたが・・・

JavaScriptのプログラミングをしたが、テストらしいテストをしていない。
何かいいテストツールはないかなと調べたら、JsUnitというツールを見つけた。
「JsUnit」で検索して1ページ目に表示されたサイトをひと通り見てみたが、JsUnit を使った JavaScript のユニットテストなどを読んでも使い方のイメージが湧かない。

使いながら覚えようと簡単なテストページを作ってみたが、testRunner.htmlから起動しても動かない。
試行錯誤の結果、
「Test Page(Test Functionを記述したhtmlファイル)がtestRunner.htmlと同一フォルダまたは配下のフォルダにないと動かない」
ことがわかった。
しかし、この挙動の理由はわからない。

気になったので動きを調べてみた。わかったことは以下のとおり。
testRunner.htmlはいくつかのフレームに分割されており、Test Pageを指定して実行すると、テスト用のhidden frameにロードされる。
これは単にテストフレームの location.href にhtmlファイルのパスをセットしているのだが、ここに「testRunner.htmlと同一フォルダまたは配下のフォルダにないTest Pageのパス」をセットすると、Test PageがロードされないかjsUnitCore.jsのonloadイベントが実行されない。
セットされているパスは正しい。

これ以上深入りせずに調査打ち切り。
testRunner.html配下のフォルダにTest Pageを作れば動くんだから、その方法で明日から本格的にJsUnitでテストしよう。
スポンサーサイト

戦車バトルゲームを作った

前回前々回の記事で書いた、「戦車が勝手に戦うゲームみたいなもの」がやっとできた。
こちらで公開しています → HTML5 Canvas Tank Battle Game
英語としておかしい気もするが気にしない。

今日は難易度調整ばかりしてた。パラメーターで調整できればいいのだが、そうはいかないところもありソースコードをチョコチョコ修正した。
自分がクリアできるレベルに調整したので、かなり易しくなった気がする。

グラフィックがショボいので画像の解説文も載せた。
ゲームのやり方も見ただけではわかりにくいかと思って遊び方の説明文を書いたが、説明文が長い。
他の人たちのシンプルなゲームは1,2行で済んでるのに、何でこんなに長いんだろう。

戦車が目的地に向かって進むときの経路探索にはA*(エースター)アルゴリズムを使用した。
やっててよかったA*。この前作ったソースコードがほとんど流用できた。

Internet Explorerでの動作が重いため、明日はIE用にちょっと修正しよう。

戦車っぽいモノが戦うゲーム完成まであと少し

この前の記事に書いた「戦車っぽいモノが戦うゲームっぽい何か」が、もう少しで公開できそう。

前回の記事から追加した仕様は
・ステージをクリアしたら次のステージへ進む
・ステージによってマップの大きさ、敵の数、敵のスピードなどが変わる
・プレイヤー戦車の行き先を指定できる
・弾が当たって壊れたときの画像追加 (前回は、壊れたら消えるだけだった)

前回の記事ではプレイヤー戦車をコントロール不可だったが、それだとなかなか敵戦車に近づかなくてイライラする。
行き先を指定することで最小限のコントロールができ、ゲームっぽくなった。

戦車の動きにおかしなところがいくつかあるが、最低限以下の2点は公開前に修正したい。
・当たり判定処理に不備があり、弾がすり抜けるケースがある
・行動判定処理に不備があり、前に敵戦車がいるとプレイヤー戦車が動けず撃たれるままになるケースがある

相変わらず見た目はショボいし、音も出ない状態で公開するつもり。
ステージマップ作ってテストしなきゃいけないから、明日の公開は無理そうだ。

何となくプログラミング中

HTML5 Canvasで戦略シミュレーションゲームっぽいものを作りたいと思って、
まずはマップにユニット置かなきゃと HTML5 Canvas + jQuery でドラッグ&ドロップ を作ってみた。
あれから1週間、何となくJavaScriptでプログラミングをしているのだが、形になっていない。

現在できているモノの仕様は
・山、草原、道、川の地形があるマップ表示
・ユニットは戦車のみ。草原、道のみ移動可能
・敵戦車の位置はランダムに設定される
・プレイヤー戦車ユニットの位置をマウスで指定すると、戦車が動き出す
・プレイヤー戦車も敵戦車も同じポリシーで勝手に動く。コントロール不可
・前後左右を見て、射程距離内に攻撃対象がいたら弾を撃つ。山の向こうの攻撃対象は見えない
・攻撃対象がいなかったら、前後左右のうち通った回数が最小のエリアを選んで進む
・弾が当たったら壊れる。何発で壊れるかはパラメーターで制御
・敵戦車が全滅するか、プレイヤー戦車が壊れたら終了
こんな感じ。
戦略でも無いしシミュレーションでも無いし、ゲームですら無い。

ゲームになるまでの道は遠そうなので、そろそろ打ち切ろう。
せっかく作ったので公開したいが、何か中途半端だ。
どこで妥協して公開するかが問題だ。

[JavaScript]クラスのメソッドは prototype に設定するべきか?

オブジェクト指向プログラム言語としてのJavaScript15.prototypeプロパティ に、
「prototypeを使ってメソッドを定義すれば、インスタンスごとにメソッドを持たないためメモリ効率が良い」
といったことが書かれてあった。

1998年に書かれたかなり古いドキュメントだが、気になったので
「prototypeを使うことでどのぐらいの違いが出るか」
を計測してみた。

Firefox 3.6で計測したところ、劇的な違いが出た。
prototypeを使ったほうがメモリやCPUの負荷は小さいし処理速度も速い。
「インスタンスを100個生成し、それぞれ100回メソッド実行」の場合は、prototypeを使うことで処理速度が約1/10になった。
「インスタンスを500個生成し、それぞれ500回メソッド実行」の場合は、処理速度が約1/25になった。

これはすごい。prototypeを使うべきだ。
と思ったのだが、Internet Explorer と Google Chromeで計測してみたところ、ほとんど差が無かった。
prototypeを使った方が遅くなる場合もある。
おそらくブラウザ内の処理はprototypeを使おうが使うまいが同じなのだろう。

そんなわけで、自分としては「10個以上インスタンスを生成する場合はprototypeを使うかな」という結論に至った。

一応計測結果を載せておく。平均を取るほどの値ではないと思うので、代表的な数値を抽出した。
<単位:ミリ秒>
 Firefox 3.6   IE 8.0   Chrome 8.0 
 インスタンスを100個生成し、
 それぞれ100回メソッド実行
prototype使用2200
 prototype使用せず 24200
インスタンスを500個生成し、
それぞれ500回メソッド実行
prototype使用244406
 prototype使用せず 5604206
Google Chromeが速すぎる。デベロッパーツールで確認してみたが、ちゃんとインスタンスを生成してメソッドも実行している。

計測するために作成したソースコードもアップしておく。
prototest.html

自分のprototypeの理解は曖昧だったが、「オブジェクトにプロパティが存在しない場合は、prototypeのプロパティを探す」という説明を見て、何となくわかった気がした。

HTML5 Canvas + jQuery でドラッグ&ドロップ

タイトルのとおり、drag&drop を HTML5 Canvasで実装してみた。
こちらで公開しています → HTML5 Canvasでドラッグ&ドロップ
スタートボタンを押して実行してください。
実装してみてわかったことを簡単に解説する。

1. ソースコード
作成したソースコードは以下のとおり。jQuery は ver1.4.2 を使用。
・html および共通の Canvas 描画クラス
dtest.html  dtestcom.js
・drag&drop処理(1px 移動するごとに Canvas 更新)
dtest1.js
・drag&drop処理(1/24秒ごとに Canvas 更新)
dtest2.js

2. Canvas 更新方法について
「mousemove イベントが発生するたび更新」 → 「1px 移動するごとに更新」 → 「1/24秒ごとに更新」 の順にCPU負荷が小さくなる。

1) 1px 移動するごとに Canvas 更新
ドラッグ中状態で mousemove イベントが発生した際、現在の座標からX軸方向またはY軸方向に 1px 以上移動した場合のみ更新する。
その部分のソースコードは以下のとおり。
// 画面更新するか判定
// cx, cy ポインタのCanvas座標
// dtest1.DRAG.itemAr[dtest1.drag.item].x ドラッグしているアイテムのx座標
// dtest1.DRAG.itemAr[dtest1.drag.item].y ドラッグしているアイテムのy座標
var updSep = 1; // 何px動いたら画面更新するか
if (Math.abs(cx - dtest1.DRAG.itemAr[dtest1.drag.item].x) >= updSep ||
  Math.abs(cy - dtest1.DRAG.itemAr[dtest1.drag.item].y) >= updSep) {
  // アイテムの座標更新
  dtest1.DRAG.itemAr[dtest1.drag.item].x = cx;
  dtest1.DRAG.itemAr[dtest1.drag.item].y = cy;
  // 画面更新
  dtest1.DRAG.draw();
}
わずか1pxだが、この判定処理を入れたことで移動中のCPU負荷が(自分の環境では)1割ぐらい減った。
1px を 2px にしたら動きがぎこちなくなった。

2) 1/24秒ごとに Canvas 更新
ドラッグ中状態の場合、イベントに関係無く 1/24秒ごとに Canvas を更新する。
ドラッグしているアイテムの座標は mousemove イベントが発生するたびに更新する。
mousedownイベント発生時に、ドラッグ対象アイテムがあったら window.setInterval() でタイマー実行。
mouseup/mouseleaveイベント発生時などに window.clearInterval() でタイマー停止。

1px 移動するごとに更新する場合に比べて、移動中のCPU負荷が(自分の環境では)半分ぐらい減った。
ソースコードの修正箇所は思ったほど多くは無かった。

今後 drag&drop 以外の処理を追加していくつもりだが、
「スタート時にタイマーを実行し、イベント発生時にフラグのオンオフを行う。Canvas を更新するかはフラグをチェックして判定」
とすればCPU負荷を抑えられる上に複雑にならないだろう。

3. その他
・document上の座標をCanvas上の座標へ変換
イベントオブジェクトから取得できるマウスポインタの座標 (evt.pageX, evt.pageY) はdocument上の座標。
CanvasのDOM要素の左上座標は (offset().left, offset().top) で取得できるので、それを使ってCanvas上の座標へ変換できる。
// Canvas描画クラス
  // document上のCanvas左上座標を求める
  // <canvas>タグを認識しないケースを考慮し、同じサイズの<div>タグで囲ってそのDOM要素を取得
  // names.con.id.cvdiv は<div>タグのid
  var $cvdiv = $('#' + names.con.id.cvdiv);
  this.cvpos.x = $cvdiv.offset().left;
  this.cvpos.y = $cvdiv.offset().top;

// mousedownイベント時の処理
  // ポインタ座標をCanvas座標へ変換
  // dtest2.DRAG はCanvas描画クラスのインスタンス
  var cx = evt.pageX - dtest2.DRAG.cvpos.x;
  var cy = evt.pageY - dtest2.DRAG.cvpos.y;

・クリックした場所にドラッグ対象アイテムがあるかの判定
いわゆる当たり判定。
可能なら Canvas をグリッドでエリア分割すれば楽。

久しぶりだと思ったら、この前 HTML5 Canvas のプログラミングしたのは1ヶ月半前だった。

作りたいものが思いつかないので、余計なことを考えてみた

Google App Engine で何か作りたいのだが思いつかない。
HTML5+jQuery+Python で、何かサーバーにデータを登録するようなゲームを作ろうかな。
でもJavaScriptのソースコードをいじれば送信データを簡単に改変できそうだ。

データ改ざん対策をちょっと調べてみたが、「セッションを使う」「ソースコードの難読化」ぐらいしか見つけられなかった。

セッション使っても、JavaScriptを差し替えれば改変データを送信できそうな気がする。そんなに簡単じゃないのかな。
ソースコードを難読化すれば、よっぽどの人気ゲームでもない限り解析してデータ改ざんするモチベーションは湧かないだろう。
でもプログラミング勉強中の自分は、ソースコードを読んでもらいたいので難読化は使いたくない。
何してるかわからない難読化ソースはかなり怪しく見えるし。

いざとなったら送信データ暗号化部分だけswfファイルにするか。ActionScript知らないけど。
Flash使ってないサイトはここらへんの対策どうやってるんだろう。

デザインパターンの基本を学ぶ

「デザインパターン」という言葉は聞いたことがあったが、それが何かはよくわからなかった。
検索して上から2番目に出てきた
デザインパターン[モデリング] -TECHSCORE-
を読むことにした。

ここにある言葉を借りると、デザインパターンとは「クラス設計する際によく出会う問題とそれに対処する良い設計」として紹介されている23パターンのこと。
自分で作ったクラスを継承したことが今まで無かったのだが、「なるほどクラスの継承はこんな風に使えるのか」と今更ながら知ることができた。

今日は 17.Observerパターン まで読んだ。Javaのソースコードを読むのは久しぶりだったので、「super()ってなんだっけ」とか調べながらだったが、書いていることは理解できた。
「このデザインパターンパターンを使うことでどんなメリットがあるか」はよくわかったのだが、実際に自分がクラス設計する際にどれだけデザインパターンを使えるだろう。

自分用に「各デザインパターンの概要メモ」をまとめながら読んでいるので、何度かメモを読み返すようにするか。

「オブジェクト指向における再利用のためのデザインパターン」が出版されたのが1995年ってことは、今はもっと洗練されているのだろうか。
でも上記 TECHSCORE 記事の初出は2006年だから、そんなに古くはないのかな。

[Google App Engine] クエリの ORDER BY 制限を回避する

Google App Engine の仕様に苦しめられたので、今日自分がやったことを簡単にまとめる。

1. クエリの制限
Google App Engine のデータストアでは、実行できるクエリに制限がある。

主な制限は以下のとおり
・不等式が使用できるのは1つのプロパティ(DBで言うところの列名)に限られる
# これはOK
SELECT * FROM Test WHERE date = DATE('2010-12-01') AND cost >= 100
# これは BadFilterError 例外が発生する
SELECT * FROM Test WHERE date >= DATE('2010-12-01') AND cost >= 100

・不等式を使用したクエリで ORDER BY を使用する場合、ORDER BY の先頭は不等式を使用したプロパティにする必要がある
# これはOK
SELECT * FROM Test WHERE date >= DATE('2010-12-01') ORDER BY date
# これは BadArgumentError 例外が発生する
SELECT * FROM Test WHERE date >= DATE('2010-12-01') ORDER BY item

自分が作っているプログラムで、「月単位で item の cost をまとめる」みたいなことをする必要があった。
普通のDBでは、以下のようなプログラムにすればよい。
from google.appengine.ext import db
# test データモデル
class Test(db.Model):
  item = db.StringProperty(required=True)
  cost = db.IntegerProperty(default=0)
  date = db.DateProperty(required=True)
(中略)
# 月単位で item の cost をまとめる処理
query = db.GqlQuery("SELECT * FROM Test WHERE date >= DATE('2010-12-01') AND date < DATE('2011-01-01') ORDER BY item")
for entity in query:
  # entity.item が前の値と同じなら entity.cost を加算
しかし Google App Engine では、上記の ORDER BY 制限のため BadArgumentError 例外が発生する。

2. ORDER BY 制限を回避する
ORDER BY の制限だけなら、
query = db.GqlQuery("SELECT * FROM Test WHERE item != '' AND date >= DATE('2010-12-01') AND date < DATE('2011-01-01') ORDER BY item")
で回避できそうだが、これは上記の「不等式が使用できるのは1つのプロパティ」に反するので BadFilterError 例外が発生する。

仕方なくリストを使ってソートした。
# クエリから ORDER BY を削除
query = db.GqlQuery("SELECT * FROM Test WHERE date >= DATE('2010-12-01') AND date < DATE('2011-01-01')")
entityL = [] # ソートするための暫定リスト
for entity in query:
  # 値として有り得ない文字列を使って、値を結合
  entityL.append(entity.item + '$$$$' + str(entity.cost))
entityL.sort()
for test in entityL:
  # 文字列を分割して item と cost を取得
  testSep = test.split('$$$$',1)
  item = testSep[0]
  cost = int(testSep[1])
  # item が前の値と同じなら cost を加算
エンティティ数が少ないからこれで何とかなったが、数が多い場合は処理時間や負荷が心配になる。

クエリで不等式を使わないよう、プロパティを追加する方法も考えられる。
# month プロパティを追加
class Test(db.Model):
  item = db.StringProperty(required=True)
  cost = db.IntegerProperty(default=0)
  date = db.DateProperty(required=True)
  month = db.StringProperty()
(中略)
# データ登録時に年月を設定
test.month = '%4d%2d' % (test.date.year, test.date.month)
test.put()

この場合、クエリで ORDER BY が使用できるので以下のように簡潔になる。
query = db.GqlQuery("SELECT * FROM Test WHERE month = '201012' ORDER BY item")
for entity in query:
  # entity.item が前の値と同じなら entity.cost を加算

ここらへんを考慮してデータ設計しなきゃいけないってことだな。
Google App Engine のデータストアには他にもいろいろ考慮すべき点がありそうだから、ちゃんと調べるか。

本日のPython関連作業についてダラダラと書く

今日もPythonでちょこちょこプログラミング。

いつものようにライブラリリファレンスをチェックしようと思ったら、Python 和訳ドキュメントのライブラリリファレンスが 404 Not Found に。
どうやらドキュメントを Python 2.6.2 対応にしたようだ。
URLが結構変わっていて、例えば「Python チュートリアル 5. データ構造」は
旧:http://www.python.jp/doc/release/tut/node7.html
新:http://www.python.jp/doc/release/tutorial/datastructures.html
となっている。
Google App Engine は Python 2.5 なので、Python 2.5 ドキュメント を見るようにしよう。
過去のブログエントリも Python 2.5 ドキュメントのURLに変更した。

自分が作ったデータモデル(DBで言うところのテーブル)がだいぶ増えてきたので、モデルクラスのリストを使って各データモデルのエンティティ(DBで言うところのレコード)数を取得するようにした。
動的にクラス名を取得したいのだが、リファレンスを見てもわからない。
ネットで調べたら __name__ 属性でクラス名が取得できることがわかった。
リファレンスのどこに書いているか調べたら、リファレンスマニュアルの 3.2 標準型の階層 のクラスの項にあった。
ライブラリリファレンスの調べ方はだいぶわかったが、リファレンスマニュアルはどこに何が書いてあるのかよくわからない。

自分が作ったデータモデルの一覧を取得したいと思ったが、ネットで調べても見つけられなかった。
管理テーブルみたいなのは無いのかな。
プロフィール

himax64

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

最新記事
人気記事
検索フォーム
カテゴリ
月別アーカイブ
最新コメント
最新トラックバック
RSSリンクの表示
QRコード
QRコード
カウンター
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。