enchant.js入門

大学の授業で話すことになったのでenchant.jsの使い方を説明します.「授業でプログラミングを触ったことがある」くらいの人を想定しているつもりです.enchant.jsはjavascriptでゲームを開発するためのライブラリで,PCだけでなくiPhoneAndroidなどのスマートフォンのブラウザでも動作するゲームを作ることができます.またコンテストが開催されており,作ったゲームを投稿することもできます.


本稿では

  • 開発環境の構築
  • 画像の表示
  • 画像の移動
  • キー入力による移動
  • マップの表示
  • ステージのスクロール
  • キャラクタの動き
  • マップとの当たり判定

という順序で最終的に付属のアクションゲームと同じようなものを作るところまでいきます.

ブラウザを用意する

制作したゲームを実行するためのブラウザを用意します.FirefoxChromeなどモダンなブラウザならだいたいいけるみたいです.今回はFirefox4を使用します.

公式サイトよりダウンロード・インストールします.
http://mozilla.jp/firefox/


また,Firefoxで開発する場合Firebugというアドオンがあると便利なのでこれも導入します(Chromeだと同等の機能が標準で備わっています).
Firebug

ライブラリを導入する

リポジトリからパッケージをダウンロードします.
GitHub - wise9/enchant.js: A simple JavaScript framework for creating games and apps
パッケージを解凍し,examples/action下のindex.htmlをブラウザで開くとサンプルのゲームで遊べます.

テンプレートの作成

javascriptでゲームを開発する場合,まずブラウザに表示させるためのhtmlファイルが必要です.先ほど開いたindex.htmlを適当な場所にコピーして次のように書き換えてください.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>enchant</title>
    <script type="text/javascript">
      console.log("hoge");
    </script>
    <style type="text/css">
      body { margin: 0; }
    </style>
  </head>
  <body>
  </body>
</html>

ファイルを開く前に,ブラウザのコンソール画面を有効にします.Firefox + Firebugの場合[ツール] -> [Firebug] -> [Firebugを開く]でコンソールを表示できます.


この状態でファイルを開くと,コンソールにメッセージが表示されます.htmlではscriptタグで囲んだ部分をjavascriptとして評価します.この場合7行目がそれにあたり,console.logはコンソールにメッセージを表示させる関数です.

また,scriptタグ内に書いたコードはすぐに実行されますが,ページ内のまだ読み込まれていない要素にアクセスしたい場合すぐに実行されると困ります.全ての要素の読み込みが終わってからコードを実行したい場合次のようにします.

window.onload = function() {
  console.log("hoge");
}

javascrpitではfunctionで囲った部分が関数として評価され,window.onloadに代入した関数はページの読み込み後に実行されます.

スプライトの表示

実際にゲームのコードを書いていきます.まずダウンロードしたパッケージ内に入っていたenchant.jsとサンプルフォルダに入っていたchar1.gifをhtmlファイルと同じフォルダにコピーします.

enchant.jsを読み込むには,scriptタグのsrcにファイルパスを指定します.コードを次のように書き換えて実行するとクマが表示されます.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>enchant</title>
    <script type="text/javascript" src="enchant.js"></script>
    <script type="text/javascript">
      enchant();
      window.onload = function() {
        var game = new Game(320, 320);
        game.fps = 24;
        game.preload('chara1.gif');
        game.onload = function() {
          var bear = new Sprite(32, 32);
          bear.image = game.assets['chara1.gif'];
          game.rootScene.addChild(bear);
        }
        game.start();
      }
    </script>
    <style type="text/css">
      body { margin: 0; }
    </style>
  </head>
  <body>
  </body>
</html>


enchant()はライブラリを使用するための初期化処理で,enchant.jsを使用する際は必ず最初に呼び出します.

var game = new Game(320, 320);
game.fps = 24;
game.preload('chara1.gif');

ゲーム画面を320x320で作り,フレームレートを設定し,使用する画像を読み込んでいます.

game.onload = function() {
  ...
}

window.onloadと同様に,game.onloadに関数を登録しておくと画像の読み込みや描画領域の確保などが終わったあとに登録した関数が実行されます.

  var bear = new Sprite(32, 32);
  bear.image = game.assets['chara1.gif'];
  game.rootScene.addChild(bear);

画像を表示するにはSpriteを用意し,シーンに追加します.初期化時にスプライトのサイズを,imageプロパティにgame.assetsを通して読み込んだ画像を指定します.ここで,画像のサイズとスプライトのサイズが違う場合画像の左上が切り取られるような形で表示されます.

game.start();

最後にゲームを起動します.

画像の移動

表示した画像を移動させます.

bear.addEventListener(Event.ENTER_FRAME, function(e) {
  bear.x += 1;
});

addEventListenerを使うとSpriteやSceneなどにイベントと処理のペアを登録することができます.ここでは,ENTER_FRAMEイベントにクマを移動させる処理を登録し,フレーム毎に徐々にクマの位置を移動させることでアニメーションを実現しています.他にも画面をタップ(PC/Macではクリック)したときのイベントなどがあります.イベント一覧はドキュメントのenchant.Eventで確認できます.

キー入力による移動

キー入力によってクマが移動するようにします.コードを次のように書き換えます.

bear.addEventListener(Event.ENTER_FRAME, function(e) {
  if(game.input.right)
    bear.x += 5;
  else if(game.input.left)
    bear.x -= 5;
});

game.inputにはフレーム毎の入力の状態が保持されており,上記のコードではユーザのキー入力に応じてクマの位置を移動させています.

マップの表示

キャラクタのように激しく動きまわるものでなく,ステージや背景など比較的動かないものはMapを使うと楽に書けます.サンプルフォルダのmap2.gifをhtmlファイルと同じフォルダにコピーし,ソースコードを書き足します.

// preloadにも追加
game.preload("chara1.gif", "map2.gif");
var blocks = [
  [-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, ...(略.適宜サンプルからコピーしてください.
];

var map = new Map(16, 16);
map.image = game.assets["map2.gif"];
map.loadData(blocks);


マップを生成するにはマップチップというものを使います.これは,マップを構成する画像を並べてひとつの画像にしたものです.Mapは初期化時にチップ1つのの大きさを,imageにマップチップの画像を指定します.チップの並びはloadDataに二次元配列を渡すことで設定します.配列内の数値はマップチップの座標に対応(下図)しており,何も無い部分は-1にします.

ステージのスクロール

クマの移動によってマップの表示されてない部分を見えるようにします.

var stage = new Group();
stage.addChild(map);
stage.addChild(bear);
stage.addEventListener(Event.ENTER_FRAME, function(e) {
  if(stage.x > 64 - bear.x)
    stage.x = 64 - bear.x;
});

// rootSceneへのaddChildは書き換え
game.rootScene.addChild(stage);

マップのスクロールはマップとクマをステージとしてひとつにまとめ,ステージ自体を動かすことで実現します.Groupは複数のマップやキャラクタをまとめるために使います.stageにもEventListenerを追加し,フレーム毎にクマの位置に応じてステージも動かします.

キャラクタの動き

キャラクタの動きを実装します.今までキー入力によって一定の速度で移動していたものを加速しながら動くようにします.bearのaddEventListenerを次のように書き換えます.

bear.vx = 0;
bear.addEventListener(Event.ENTER_FRAME, function(e) {
  var ax = 0;
  if(game.input.right) ax += 0.5;
  if(game.input.left) ax -= 0.5;
  bear.vx += ax;
  bear.x += bear.vx;
});

加速運動を表現するには速度と加速度が必要なので,それぞれvx,axとします.毎フレームごとにキー入力によって加速度を変更し,加速度を速度に,速度を位置に加算します.実行して右キーを押し続けるともの凄い速さでクマが飛んでいきます.これではゲームにならないので,速度に制限をもたせます.

bear.vx = Math.min(Math.max(bear.vx, -10), 10);
bear.x += bear.vx;

Math.maxは引数の中で一番大きい値を返し,minは小さい値を返します.速度を位置に加算する直前にmaxとminを使って速度に上限と下限をもたせます.実行すると徐々に速くなっていきますが,一定の速度以上にはなりません.次に,キーを離すと減速するようにします.

if(game.input.right) ax += 0.5;
if(game.input.left) ax -= 0.5;
if (bear.vx > 0.3)
  ax -= 0.3;
else if (bear.vx > 0)
  ax -= bear.vx;
if (bear.vx < -0.3)
  ax += 0.3;
else if (bear.vx < 0)
  ax -= bear.vx;

速度が一定以上の場合加速度を減らし,ほぼ止まりかけのときは決め打ちで止めます.逆向きも同じです.次に,クマのアニメーションを設定します.

// bear初期化時
bear.pose = 0;
var ax = 0;
if(game.input.right) ax += 0.5;
if(game.input.left) ax -= 0.5;
if (ax > 0) bear.scaleX = 1;
if (ax < 0) bear.scaleX = -1;
if (ax != 0) {
  if (game.frame % 3 == 0) {
    bear.pose++;
    bear.pose %= 2;
  }
  bear.frame = bear.pose + 1;
} else
  bear.frame = 0;

scaleXはキャラクタのX軸方向の拡大率を表し,これに-1を代入することで左右の表示を反転することができます.bear.frameはMapと同じように値によって表示する画像を変えることができます.ここでは,bearにposeプロパティを設定し,一定フレーム毎にその値を変え,frameに代入しています.game.frameはゲームが開始してからのフレーム数を返し,それを定数で割った余りを見ることで一定フレーム毎の処理を行っています.

ジャンプと重力

上下の運動を実装します.bearの初期化と位置の更新の前後を次のように書き換えます.

bear.vy = 0;
bear.jumping = false;
if(game.input.up && !bear.jumping) {
  bear.vy = -9;
  bear.jumping = true;
}
bear.vy += 0.5;

bear.x += bear.vx;
bear.y += bear.vy;

if(bear.y >= 100) {
  bear.y = 100;
  bear.jumping = false;
}

実行すると,上キーでジャンプします.まずジャンプ中かどうかを判定するフラグを用意し,フラグが立っていないかつ上キーが押されていた場合上方向へ速度を設定します.さらに,毎フレームごとに下方向へ速度を加算することで重力を表現します.位置の更新が終わった後,着地を判定します.

当たり判定

最後に,キャラとマップの当たり判定を書きます.速度を決定した後のコードを次のように書き換えます.

var dx = bear.x + bear.vx + 5;
var dy = bear.y + bear.vy;
if(map.hitTest(dx, dy + bear.height)
|| map.hitTest(dx + bear.width - 10, dy + bear.height)) {
  dy = Math.floor((dy + bear.height) / 16) * 16 - bear.height;
  bear.vy = 0;
  bear.jumping = false;
}
bear.x = dx - 5;
bear.y = dy;

実行すると,一定の高さを基準にジャンプしていたのがマップに着地するようになりました.map.hitTestは引数にとったx,y座標にマップがあるかどうかを判定します.ここでは,bearの左下(dx, dy + bear.height)か右下(dx + bear.width, dy + bear.height)がマップに当たっていた場合,y座標に地面の高さを代入します(ちなみにxに5を足しているのは微調整です.width - 10も同様).Math.floorは引数にとった値の小数点を切り捨てた値を返すので,dyにはマップの境界(- bearの高さ)が代入されます.後は同様に上と左右の当たり判定も加えていきます.上と左右を加えていく際にはもう少し細かい処理が必要なのでここでは割愛しますが,基本的には同じです.詳しくはサンプルのソースを見てください.

おわりに

これで一通り完成しました.他にも http://9leap.net/ でenchant.jsを使ったゲームのソースが見れます.僕もenchant.jsを使って9leapにゲームを投稿したので,ぜひ遊んでみてください.
9leap : 合コンクエスト by blankblank - どこでも遊べる、投稿型ゲームサイト


参考

今回使ったソース

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>enchant</title>
    <script type="text/javascript" src="enchant.js"></script>
    <script type="text/javascript">
      enchant();
      window.onload = function() {
        var game = new Game(320, 320);
        game.fps = 24;
        game.preload("chara1.gif", "map2.gif");
        game.onload = function() {
          var blocks = [
            [-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1],
            [-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1],
            [-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1],
            [-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1],
            [-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1],
            [-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1],
            [-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1],
            [-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 4, 4, 4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1],
            [-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 2, 2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 3, 3, 3,-1,-1,-1,-1, 4, 4, 4, 4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1],
            [-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 3, 3, 3,-1,-1,-1,-1, 3, 3, 3, 3, 4, 4, 4, 4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1],
            [-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 2, 2, 2, 2,-1,-1,-1,-1,-1,-1,-1,-1,-1, 3, 3, 3,-1,-1,-1,-1, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1],
            [ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,-1,-1,-1,-1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 3, 3, 3,-1,-1,-1,-1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1],
            [ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,-1,-1,-1,-1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,-1,-1,-1,-1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 4, 4, 4, 4, 4, 4, 3, 3, 3,-1,-1,-1,-1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,-1,-1,-1,-1, 4, 4, 4, 4, 4, 4, 4,-1,-1, 2, 2,-1,-1, 2, 2,-1,-1, 2, 2,-1,-1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4],
            [ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,-1,-1,-1,-1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,-1,-1,-1,-1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 3, 3, 3, 3, 3, 3, 3, 3, 3,10,10,10,10, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,10,10,10,10, 3, 3, 3, 3, 3, 3, 3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3],
            [-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1],
            [-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1],
            [-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1],
            [-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1],
            [-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1],
            [-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1]
          ];
          
          var map = new Map(16, 16);
          map.image = game.assets["map2.gif"];
          map.loadData(blocks);
          
          var bear = new Sprite(32, 32);
          bear.image = game.assets["chara1.gif"];
          bear.vx = 0;
          bear.vy = 0;
          bear.pose = 0;
          bear.jumping = false;
          bear.addEventListener(Event.ENTER_FRAME, function(e) {
            var ax = 0;
            if(game.input.right) ax += 0.5;
            if(game.input.left) ax -= 0.5;
            if (ax > 0) bear.scaleX = 1;
            if (ax < 0) bear.scaleX = -1;
            if (ax != 0) {
              if (game.frame % 3 == 0) {
                  bear.pose++;
                  bear.pose %= 2;
              }
              bear.frame = bear.pose + 1;
            } else
              bear.frame = 0;
            if (bear.vx > 0.3)
              ax -= 0.3;
            else if (bear.vx > 0)
              ax -= bear.vx;
            if (bear.vx < -0.3)
              ax += 0.3;
            else if (bear.vx < 0)
              ax -= bear.vx;
            bear.vx += ax;
            
            bear.vx = Math.min(Math.max(bear.vx, -10), 10);
            if(game.input.up && !bear.jumping) {
              bear.vy = -9;
              bear.jumping = true;
            }
            bear.vy += 0.5;
            
            var dx = bear.x + bear.vx + 5;
            var dy = bear.y + bear.vy;
            if(map.hitTest(dx, dy + bear.height)
            || map.hitTest(dx + bear.width - 10, dy + bear.height)) {
              dy = Math.floor(dy / 16) * 16;
              bear.vy = 0;
              bear.jumping = false;
            }
            bear.x = dx - 5;
            bear.y = dy;
          });
          
          var stage = new Group();
          stage.addChild(map);
          stage.addChild(bear);
          stage.addEventListener(Event.ENTER_FRAME, function(e) {
            if(stage.x > 64 - bear.x)
              stage.x = 64 - bear.x;
          });
          
          game.rootScene.addChild(stage);
        }
        game.start();
      }
    </script>
    <style type="text/css">
      body { margin: 0; }
    </style>
  </head>
  <body>
  </body>
</html>