HTMLでゲームを作るならCanvas一択???

お久しぶりです、最近は学校の課題やレポートに追われてる西脇です。

Windows8アプリの勉強会用にとても簡単に作れるハエたたきゲームをつくりました。

正直、DL数はあまり期待せずに作ったミニゲームのアプリだったんですが、先日500DLいきました。。。

今まで3つアプリを公開したんですが、

・はえたたき職人(526DL<人気TOP 89位内ランクイン中>)
http://apps.microsoft.com/webpdp/app/1b032214-0770-4fb0-aa06-ea4c34dcfa67

・EventReader(146DL)
http://apps.microsoft.com/webpdp/app/eventreader/d48ba964-b9fd-4fd1-b1c4-de88b8902aaf

・ClosetCordinater(68DL)
http://apps.microsoft.com/webpdp/app/closetcordinater/e91f7fb1-e6a2-4040-bbe4-8ff09d65c147

正直ClosetCordinaterのアプリが一番期待していたんですがまったくDLされてないですね。
ただ、ClosetCordinaterはMSPフェローの仲間と絶賛改造中なのでこれは化けると思います!!!その様子もこれからちょっとずつ書いていきます!!

ここで、思ったこと。
やっぱりゲームカテゴリはDLされやすいですね。
ライフスタイルなどとは、比較にならないくらいDLされやすいようです。

なので、しばらくはミニゲームを作っていきたいと思います。
今は射的アプリ<射的職人>を作っています。
f:id:ukinau:20121110235255p:plain


でここからが本題
HTML+JSでミニゲーム?あぁ、Canvasでしょ?
って思うと思うんですが、僕の答えはNOです。
クリックゲーであれば、わざわざCanvasを使う必要はないと思います。

もちろんCanvasで作った方がいろいろと都合がいいのは確かです。でも使わなくても作れないことないよ?しかも簡単だよ?ってことで今回紹介していきます。

じゃあまず、Canvasだと何が大変なのか?(あんまり僕もやってないので間違ってるかもしれません)

・当たり判定は自分でしないといけない。
Canvasでのアニメーションは、パラパラ漫画のようなもので少し動いた先の世界を描写しなおすことを繰り返すことで実現しているので。割と複雑な感じになってしまう。

簡単なクリックゲーを作るのに、わざわざCanvasを使う必要があるか?

じゃあCanvasなしでどうする?


アニメーション
 →setIntervalで少しずつアニメーション対称のleft,topを動かしていきます。
当たり判定(タッチされたかどうか)
 →onclickイベント
ほらこれでできそうじゃね?


クリックゲーは、Canvasを使わない方がコスパよさそうですね~。

クリックゲーも見せ方次第では、ちょっと面白そうなものは作れそうですね~。

ワ○ワ○パニックのようなゲームとか。

Windows8ファイルシステム内の画像表示とメモリ(JS+HTML)その2

先ほどの投稿では伝えたいことをなにも伝えてませんでした(^^;)
では続きから
ListViewでFileSystemの画像表示は?
ってことなんですけど。
ここから先ではListViewについて基本的なことは分かっている前提で話を進めます。
分からない人はここを見てください。

http://msdn.microsoft.com/ja-jp/library/windows/apps/hh465496.aspx

でListViewで画像表示させるにはどうすんの???

ここではpictureライブラリの中の画像のtest.jpgとtest1.jpgを読みこむとします。

さきほどのドキュメントとか見る限りこんな感じでいけるんじゃ、

javascript

var list = new WinJS.Binding.List();
var loadNames = ["test.jpg","test1.jpg"];

loadNames.forEach(function(fileName){
  Windows.Storage.KnownFolders.picturesLibrary.getFileAsync(fileName).
   then(pushList);
}

function pushList(file){
  var imageURL = URL.createObjectURL(file)
  list.push({
    title: "FileName",
    image: imageURL
  });
    URL.revokeObjectURL(imageURL);
}

html

<div id="testImageView" data-win-control="WinJS.Binding.Template">
        <div style="width: 150px; height: 100px;">
            <!-- Displays the "picture" field. -->
            <img src="#" style="width: 150px; height: 150px;" 
                 data-win-bind="alt: title; src: image" />
        </div>
</div>

はい!これでは動きません。
なぜか、これではのsrcにURLが設定される前にURL.revokeObjectURLが呼ばれているので
srcに値が設定される頃にはすでにimageURLからバイナリへの参照はできません。

まぁ考えてみれば当たり前。
じゃあ
revokeObjectURLしなければいいんじゃない???
確かに、revokeObjectURLしなければ動きます。では確保したメモリいつ解放しますか?って状態になります。
ここまでは何となく納得。
次に
createObjectURLでURLを生成するときに便利なオプションがあることを知ります。
oneTimeOnlyオプションです。
どうやらtrueにすると一度表示した画像を自動的にrevokeしてくれるらしいです。
こんな感じ
createObjectURL(file,{oneTimeOnly: true});
よしこれなら!!!
・・・・・これを信じきってしまったのがダメだったんですね
↑これに気づくのに大分時間かかった。。。

どうやらBindingTemplateを使うと上のオプションも無効なようです。

えっ!?じゃあListViewで表示するときどうすりゃいいのさ!?

テンプレートの使用にBinding.Templateではなくtemplate関数を使えばいいのです。

詳しくはここ
http://msdn.microsoft.com/ja-jp/library/windows/apps/jj585523.aspx

書くほどのことじゃないかもしれないけどテンプレート関数はこんな感じ
テンプレートとは言うもののただの関数なのでこの関数内でrevokeすることも可能なのでurlを
srcに設定した後、revokeしています。

  function itemTemplateFunction(itemPromise) {

       return itemPromise.then(function (item) {
           var div = document.createElement("div");

           var img = document.createElement("img");
           img.src = item.data.image;
           img.alt = item.data.title;
           div.appendChild(img);
      URL.revokeObjectURL(item.data.image);

           return div;
       });
    };

これをlistViewのテンプレートに設定することで、ファイルシステムの画像表示からちゃんとメモリ解放までできます。
えっoneTimeOnlyで生成したのにメモリ解放されてないっぽいよ!?
って人は、まずここを疑ってください。

何度もいいますが、
ファイルシステムの画像を表示するListViewを作るときは、テンプレートにWinJS.Binding.Templateクラス(HTML)を使わずテンプレート関数を使ってください。

ギャラリー系のアプリを使うと必ずメモリ管理がネックになってきます。
なので、解放し忘れ等には十分気をつけましょう。

PS.
メモリ系のテストはめんどくさかったですww
そのために画像をアプリに100枚登録したり....

ちなみに、画像をたくさん表示させるアプリとは、このアプリです。

「ClosetCordinater」
家にある服(買った服)を登録していろいろなコーディネートを作るアプリです。
http://apps.microsoft.com/webpdp/app/closetcordinater/e91f7fb1-e6a2-4040-bbe4-8ff09d65c147

ぜひ使ってみてください!!



追記ーーーーーーー

画像表示に必要だと思うdocumentのリンクを以下に貼ります
ファイルシステムのアクセス効率化
  http://msdn.microsoft.com/ja-jp/library/windows/apps/hh781216.aspx
・画像の選択から表示
  http://msdn.microsoft.com/ja-jp/library/windows/apps/hh465499.aspx

ーーーーーーーーー

Windows8ファイルシステム内の画像表示とメモリ(JS+HTML)その1

画像を管理するようなアプリを作っていて、ファイルシステムの画像を表示させたり等してたのでその時に大分ハマったメモリ周りについて少し書いていきます。

まず、Windows8アプリでファイルシステムの画像を表示させるとき

<img src="C:¥images¥test.jpeg" />

これだと表示できません。

この時点で大分マジかって感じだったんですけど。

どうやら一度ファイルを読み込んであげないといけないみたいなんですね!(セキュリティ対策だそうです。)
表示までのプロセスはここをみればだいたい分かる。
http://msdn.microsoft.com/ja-jp/library/windows/apps/hh465499.aspx
上がだいたい何をしてるかってのは、
FileOpenPickerでユーザに表示させたい画像を選ばせてるんですね。
見ていただきたいのはprocessResultの部分これは、ユーザが選んだファイルのStorageFile Classのオブジェクトを引数に取ってます。
表示させるためには、そのファイルオブジェクトからURL(パス)を生成してそれをimgタグのsrcに設定します。
こんな感じ。

/**この関数は、何かのイベントに引っ掛けるなりしてください例えばonClickとか **/
function loadImage(eventInfo) {

    var picker = new Windows.Storage.Pickers.FileOpenPicker();
    picker.fileTypeFilter.replaceAll([".jpg", ".bmp", ".gif", ".png"]);
    picker.pickSingleFileAsync().then(processResults, displayError);
}

function processResult(file){
  var img = document.createElement("img");
    var url = URL.createObjectURL(file);
  img.src = url;
    URL.revokeObjectURL(url);
    document.body.appendChild(img);

}

で、URLを生成してsrcに設定したら必ずrevokeObjectURL(url)を必ず実行してください。
そうしないとメモリをどんどん食ってしまって大変なことになります。

で、ここまでなら公式ドキュメントを見ればいいのです
さて問題はここから。

先ほどまでにまぁだいたいの表示プロセスは分かりました。
表示したい画像のfileオブジェクトを取得

fileオブジェクトからURLを生成

imgタグのsrcにセット
まぁ大雑把にこんな感じです。
画像fileオブジェクトの取得の仕方はFileOpenPicker以外にもいろいろあると思います。
こんなのとか

var pictureLibraly = Windows.Storage.KnownFolders.picturesLibrary
pictureLibraly.getFileAsync("test.jpg").
then(file){
// what you want to do  with file object 
}

でこれは置いといて、
んじゃ、これをListViewと組み合わせて使ってみようか。⬅ここで大分ハマルんですね。

ちょっと長くなりそうなので次の投稿にしときます。

windows8データの保存について(HTML+JS)

Windowsストアアプリ(旧メトロアプリ)の開発に関してちょっとまとめときます。

Windowsストアアプリでデータの保存をするとき、デフォルトで用意されてるのはKVSのような連想配列です。
valueは文字列のみ保存可能なものです。
つまり、永続的に保存させたい時は
test["key"] = "value"
こんな感じで保存します。
http://msdn.microsoft.com/ja-jp/library/windows/apps/hh465118.aspx

当初JSからanyPC向けにRDBMSを使えないと思ってたためにこの方法に頼るしかなかったためにこの方法で実装しました。(今ではsqliteが使えるようです。)
でも、containerとかそういうのを意識してずっとグチャグチャ書くのもあれだし、いつかRDBMSに変更するかもしれないしってことで
データの保存はラッパーを通すことにしました。

あと、連想配列って考えると、最新レコード4件とかそういうのを考慮するのがめんどくさいってのもここで解決させます。

外側からはORマッパー触る感覚で操作できればいいかな!そんなに複雑なことをやるつもりもないので、実装は最低限にしておきます。

valueには文字列以外にcompositeとよばれるObjectが代入できる、
compositeは普通のオブジェクトと同じように値を持つことができる。
しかしcompositeのvalueも文字列しか代入できないので注意

containerは連想配列に代入する連想配列のようなもの。
でもcontainerは普通に代入するんじゃないので注意

つまり、こんな感じ(以下イメージの話で完全なソースではありません)


  • Containerを使っていないとき普通に保存する

1: var localSetting = WinJS.Application.local;
2: localSetting['key'] = 'value'; //Keyに対して単なる文字列かAtomicオブジェクトしか代入できない

  • compositeオブジェクトを使って代入

1: var localSetting = WinJS.Application.local;
2: var atomic = new Windows.Storage.ApplicationDataComposite();
3: atomic["key1"] = "value1";
4: atomic["key2"] = "value2";
5: localSetting['key'] = atomic; //compositeならオブジェクトのように保存できる

  • Containerを使うとこんなことができる

1: var localSetting = WiJS.Application.local;
2: var container = localSetting.createContainer('ContainerName',
Windows.Storage.ApplicationDataCreateDisspositon.Always);
3: var localSetting.containers.lookup('ContainerName').values['key'] = 'value';



最初は
KVSでしかも文字列のみとかWindowsストアアプリでデータ保存系のアプリは難しくね?
って思ってたんだけど・・・
これらを使うと意外にちゃんとデータ管理できる!

って感じでここまでが準備段階。
目標はなんちゃってRDBなのでほんとデータ保存して引っ張れたらOK

データ構造(そんなすごい物ではないが)から

これを

テーブル名  
レコード  
レコード  
レコード  
レコード  


こんな感じで実装

container名
compositeObj
compositeObj
compositeObj
compositeObj
                                • -

でcompositeObjは必ずユーニークなキーとなるindexを持っている。
indexは0から始まり、compositeObjのindexもオートインクリメントされて保存される。
それで
container[indexの値] = compositeObj;
こんな感じで実際に保存されている。
[例]
container[0] = compositeObj;
container[1] = compositeObj;
container[2] = compositeObj;

で次に保存されるcompositeObjに割り当てられるindex値をcontainerが直接持っている。この場合は
container['index'] = 3
こんな感じ。

まとめるとcontainerの中身は

container['index'] = 6;
container[0] = compositeObj;
container[1] = compositeObj;
container[2] = compositeObj;
container[3] = compositeObj;
container[4] = compositeObj;
container[5] = compositeObj;


こんな感じで保存すると決めると結構ちゃんと保存できる。
(あっ、これだと不整合起きるんじゃねとかはとりあえずなしで)
javascriptってシングルスレッドだし、アプリのデータに双方が同時にアクセスすることは多分ないから大丈夫なはず・・・・


こんな構造って決めとくと、割とあとはスムーズ。

例えばテーブル作るのこんな感じ

/**
 * @param tableName
 * String 作成するテーブル名  
 * @return resultStatus
 * Boolean テーブル作成がうまくいったかどうか
 * *考えられるエラー原因: テーブルがすでに存在する
 **/
 function createTable(tableName){
   var resultStatus = false;
   if (localSettings.containers.hasKey(tableName)) {
      return resultStatus;
   } else {
      var container = localSettings.createContainer(tableName,
           Windows.Storage.ApplicationDataCreateDisposition.Always);
      localSettings.containers.lookup(tableName).values["index"] = 0;
      resultStatus = true;
      return resultStatus;
   }
 }

レコードの追加とか。

/**
 * 指定したテーブルにレコードを追加
 * @param recordObj,tableName
 * recordObj Object 追加したいレコードオブジェクト(生のオブジェクト)
 * tableName String レコードを追加したいテーブル名
 * 
 * @return resultStatus
 * 成功したらIndex
 * 失敗したらfalse
 * *考えられるエラー原因: テーブルが存在しない
 **/
function insertRecordToTable(recordObj,tableName){
  var resultStatus = false;
  if(localSettings.containers.hasKey(tableName)){
    var index = parseInt(localSettings.containers.lookup(tableName).values["index"]);
    recordObj.index = index;
    localSettings.containers.lookup(tableName).values[index] = toAtomic(recordObj);
    localSettings.containers.lookup(tableName).values["index"] = index+1;
    resultStatus = recordObj.index;
    return resultStatus;
   } else{
    return resultStatus; 
   } 
 }


全てのソースはあとでGithubに上げます。
まぁ大したもんじゃないんですけど。。。。

最後に
この方法が実用的であるかどうかって話はあると思うんですが、
データ保存を実際のアプリロジックに含めず上記のように分離してインターフェースからアクセスすることで
データ保存の処理を丸々入れ替えても現在のインターフェースを実装すればアプリはそのまま動くので、こういう作り方は有効だなと!(当たり前っちゃ当たり前なんだが。。。。)
daoのデザインパターンに近いんですかね???

まぁもっとSqliteの情報とか出てきたら入れ替えてみようかと思います。そもそもJSからいじれるのかとか、アーキテクチャ依存しないのかとか

GitHubに上げました
https://github.com/ukinau/VDB_windows8/blob/master/vmDb.js
すこし、インデントとかずれてるけど気にしない気にしない


久しぶりの更新でした。