↑トップへ戻る

おはようございます!ぺちぱな。Q 〜たまには決められた通りに繰り返すのもいい〜へようこそ!
今回のハンズオンはenchant.jsと言うJavaScriptライブラリを使って「ゲームがどのような仕組みで動いているのか」をテーマに行います!
用意はよろしいですか?それでは、張り切ってまいりましょう!どうぞ!

配布資料の確認

まず、はじめに本日のハンズオンの配布資料を確認します。
とは言っても、全部を説明するわけではなくて主要となるファイルにフォーカスをあてて紹介したいと思います!

  • ~/Guide/index.html

    みなさまが、今開いているガイドブックです。
    ここのファイルに手を加えていくことは今回はありません〜。

  • ~/Chapter/Chapter3
    ~/Chapter/Chapter4
    ~/Chapter/Chapter5

    本日のハンズオンは章構成で行われます。
    各章で取り上げられるテーマに合わせて機能紹介用のプログラムが全てこの中に入っています。

  • ~/Sample

    具体的にどのようなゲームが作れるのか、enchant.jsが提供しているデモゲームが保存されています。

  • ~/src

    本日のハンズオンで皆さんに編集していただくプログラムのデータがこの中に保存されています。

ダウンロードはこちらからどうぞ。

コンピュータゲームの歴史

1840年代
イギリスの数学者チャールズ・バベッジは、機械にゲームを行わせることに興味を持ち、解析エンジン(コンピュータ)を考えた。ただし、チェスの場合は組合せが膨大になることから成功しなかった。
1983年
7月15日 - 任天堂「ファミリーコンピュータ」(ファミコン)
1994年
11月22日 - セガ「セガサターン」。同時発売としてビクターVサターン・日立Hiサターンがある。

コンピュータゲームの歴史を紐解いてみると、はじまりはなんと1840年頃からとなっており、今日までで170年ほどの歴史があります。
僕の家にはじめてやってきたゲーム機はファミコンでした。はじまりから何年も先の話です。スーパーマリオブラザーズばっかり遊んでいました。

それが、時が経ってセガサターンやプレイステーションが登場し、2Dのものから奥行きのある3Dのゲームが家庭用ゲーム機にも登場してきて、グラフィック・演出も格段にパワーアップしました。「実写のようだな」「とんでもない事が起きてしまった」と当時思ったりしたものでした。

そんな事がゲーム機の世代が変わる度に起こるとは予想も出来ず、大人になった今でも次世代機が発表される度に・新しいゲームが発表される度にわくわくするとは思ってもいませんでした。

今回の「ぺちぱな。」ではそんなゲームがいったいぜんたいどのような仕組みで動いてるのか?をテーマに行います!
ゲーム開発向けフレームワークでもありますenchant.jsを使って、グラフィックの描画・動き・衝突判定を持ったゲームを作りながら、ゲームの仕組みを知っていけたらと思います。

enchant.js とは

enchant.jsは株式会社ユビキタスエンターテインメントが開発したゲーム開発向けのHTML5/JavaScriptベースのフレームワークです。

enchant.js
  • ゲームやアプリを開発できる HTML5 + JavaScript フレームワークです。
  • 2011年に公開され、すでに 1,000 本以上のゲーム/アプリが公開されています。
  • オープンソース (MITライセンス) で、無料で利用できます。
  • ドキュメント・書籍・チュートリアルサイトが充実しています。
  • たくさんのプラグインで機能を拡張できます。
  • UEI/ARC を中心としたメンバによって開発・メンテナンスされています。
  • プログラミング教育のためにも利用されています。
enchant.js - A simple JavaScript framework for creating games and apps. - enchant.js is... より

enchant.jsでは「物体の描画」「キー入力」「衝突判定」と言ったゲーム開発に必要な機能が扱いやすい形で提供されています。
今回は基本機能の部分でenchant.jsに乗っかりつつ、ゲームがどのように作られているのか見ていきたいと思います。

なお、enchant.jsで作られたゲームは、投稿型ゲームサイト9leapに投稿し、公開する事が出来ます。
教育用途でも使われている事から開発にも幾分取っ掛かりやすく(少ないコード量で、大きな動きがつけれたり)
作ったものを公開し・遊んでもらえる環境まで整っているためプログラミングの入門としても適したコンテンツとなっています:-)

サンプルを動かしてみよう

enchant.jsを使ってどんなゲームを作る事が出来るのか、一例としてサンプルのゲームを動かしてみます!
サンプルのゲームは以下のリンクからアクセスする事が出来ます。少しだけ遊んでみましょう!

>> shooting

画面上でマウスを操作すると、ゲームが開始します。
マウスの動きにあわせて上下に移動しながら、向かってくる敵を倒していくシンプルなシューティングゲームになっています。
他にも様々なジャンルのサンプルが用意されており、enchant.jsではゲームを作るための機能が一通り揃ったフレームワークとなっています。

他にもどんなゲームがあるか気になる方は、9leapにユーザーが開発したゲームが数多く投稿されています。
興味のある方はこちらも合わせてご覧ください :-)

↑トップへ戻る

ここでは、実際にキャラクターを操作するプログラムを書いて、ゲームがどのようにして動いているのかを見ていきたいと思います。

ゲームプログラムはパラパラ漫画のように

ゲームプログラムの仕組みをざっくりと別の何かに例えると、ページがどこまでも続くパラパラ漫画に例えることが出来ます。

enchant.js
Wikipedia - パラパラ漫画より

ゲームのプログラムはパラパラ漫画のように絶えず画面が更新される仕組みとなっており、この中で事前に組み込んだプログラムが決められた通りに動いていきます。このような仕組みの事をプログラム的にメインループと呼んでいます。
メインループ上にユーザーのキー操作・時間の経過などを定義する事によって、ページがめくられる度に画面の情報が更新されていくイメージになります。そうとは言っても、これだけではピンと来ないところもあるかもしれません。
そこで、実際にキャラクターを操作するプログラムを作りながら、より詳しく紹介したいと思います。

enchant.jsのセットアップ

enchant.jsを使ったゲーム開発をはじめるにあたってenchant.jsのセットアップを行います。
本項では既に用意されたものを使って、セットアップにあたってどういった記述が必要なのか要点をかいつまんで紹介します。

みなさまに編集していただくソースファイルは、下記ディレクトリに保管されています。
これらをもとに簡単な紹介を行いますので、こちらのプログラムをお好みのテキストエディタで開いてからご確認ください。

  • ~/src/edit/index.html

    開発したゲームが実行されるページのファイルとなります。
    こちらはテキストエディタと合わせてブラウザでもファイル名のリンクから確認してみましょう!

  • ~/src/edit/main.js/

    各章の最後で、こちらのプログラムを拡張しながらゲームを作っていきます。
    ハンズオンで実際に編集するファイルは今回こちらのみとなっております。

みなさま、お好みのテキストエディタでソースファイルを開いていただけたでしょうか?
~/src/edit/index.htmlのプログラムをブラウザ上で確認すると、以下のような画面が表示されます。

enchant.js
So Spacy!(とても、宇宙的...)

宇宙空間を背景に野生のくまが駆け抜けると言うなかなかにスペイシーな画面が表示されるかと思います、いかがでしょうか。
今回のハンズオンではこちらのプログラムを拡張していきながら、ゲームがどのようにして動いているのか紹介します。
まずはじめにenchant.jsで作ったゲームを動かすにあたって、どのような準備が必要なのかを紹介します。

../src/edit/index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta http-equiv="x-ua-compatible" content="IE=Edge">
    <meta name="viewport" content="width=device-width, user-scalable=no">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <script type="text/javascript" src="./libs/enchant.js"></script>
    <script type="text/javascript" src="main.js"></script>
    <style type="text/css">
        body {
            margin: 0;
            padding: 0;
        }
    </style>
</head>
<body>
</body>
</html>

enchant.jsではページを表示するindex.htmlに対して、処理を書く事は基本的にありません。
ソースコード上の8行目と9行目で呼び出されている外部JavaScriptファイルの呼び出しが肝の部分になっています。

こちらでenchant.jsの機能を呼び出す為の準備を行っていて、処理自体はmain.jsに書いていく事になります。
main.jsで書かれた処理をもとにして、ページ上でゲームが実行される仕組みとなります。

~/src/edit/main.js

window.onload = function(){
    enchant();                  // enchant.jsの利用宣言 必ず最初に宣言する

    var game_score    = 0;      // スコア管理用の変数
    var window_width  = 320;    // ゲーム領域の幅
    var window_height = 568;    // ゲーム領域の高さ

    // CoreObjectの定義
    var game = new Core(window_width,window_height);
    game.fps = 30.0;
    game.preload('./libs/images/chara1.png', 
                 './libs/images/background.png',
                 './libs/images/pipe.png');

    game.onload = function(){
        // 背景画像の定義
        var bg   = new Sprite(window_width, window_height);
        bg.image = this.assets['./libs/images/background.png'];
        bg.x     = 0;
        bg.y     = 0;
        bg.frame = 0;
        // 背景のスクロール
        bg.on('enterframe', function(){
            this.x -= 10;
            // 画面外に到達したら座標を調整
            if(this.x <= -window_width){
                this.x = window_width;
            }
        });

        // 背景画像の定義
        var bg2   = new Sprite(window_width, window_height);
        bg2.image = this.assets['./libs/images/background.png'];
        bg2.x     = window_width;
        bg2.y     = 0;
        bg2.frame = 0;
        // 背景のスクロール
        bg2.on('enterframe', function(){
            this.x -= 10;
            // 画面外に到達したら座標を調整
            if(this.x <= -window_width){
                this.x = window_width;
            }
        });
        // 定義した画像を、事前に描画する
        game.rootScene.addChild(bg);
        game.rootScene.addChild(bg2);

        // クマ (プレイヤーの定義)
        var size   = 32;
        var bear   = new Sprite(size,size);
        bear.image = this.assets['./libs/images/chara1.png'];   // 描画する画像を指定する
        bear.x     = (window_width/2)-(size/2);                 // 画像の描画位置の指定(x軸)
        bear.y     = (window_height/2)-(size/2);                // 画像の描画位置の指定(軸)
        bear.frame = 0;                                         // 描画領域の範囲を指定する
        game.rootScene.addChild(bear);                          // クマを画面上に描画する

        // メインループ
        game.on('enterframe', function(){
            // 主要なプログラムはこの中に書いていきます
        });
    }

    game.start();
};

おっとっと、いきなりのプログラムになんだか目眩が・・・。enchant.jsはコードの頭でenchant();を呼び出す事ではじめて使えるようになります。ちょっと面食らう部分もあるかもしれませんが、今回の進行上で重要な部分だけをピックアップしてここでは紹介したいと思います。

前準備はクラスの定義から

~/src/edit/main.js

window.onload = function(){
    enchant();                  // enchant.jsの利用宣言 必ず最初に宣言する

    var game_score    = 0;      // スコア管理用の変数
    var window_width  = 320;    // ゲーム領域の幅
    var window_height = 568;    // ゲーム領域の高さ

    // CoreObjectの定義
    var game = new Core(window_width,window_height);
    game.fps = 30.0;
    game.preload('./libs/images/chara1.png', 
                 './libs/images/background.png',
                 './libs/images/pipe.png');

... 途中省略 ...

JavaScriptではwindow.onload = function(){}{}で囲われた領域のプログラムが画面読み込み時に実行されます。
ここでは9行目でenchant.jsが提供している、Coreクラスのインスタンスを生成しています。

Coreクラスでは引数に幅と高さをそれぞれセットする事でウィンドウ領域のサイズを決める事が出来ます。
window_widthwindow_heightの数値に合わせてサイズが変化するようになっているので、良かったら話を聞きながらでも弄ったりしてみてください:-)

クラスとは用途を絞った機能群を包括した器のようなもので、Coreクラスにはゲームの進行全般を管理する為の機能がまとめられています。
クラスというのがいったいなんなのかピンと来ない人は親子丼をイメージしてみましょう。

美味しそう

親子丼を親子丼たらしめるには、まっさらな器だけでは難しいものがあります。
親子丼たらしめるべく器にご飯をよそいだしの効いた半熟のとき卵鶏肉三つ葉を乗せなければなりません。
クラスもまっさらなままでは、そのクラスが何を為しているのかがわかりません。

そのためCoreクラスではゲームの進行を管理する事をテーマにして、進行管理に必要な機能・細かい設定項目を取りまとめています。
開発者は提供されているクラスをインスタンス化する事によって、手っ取り早くそれらの機能を利用する事が出来るようになります。

今回のぺちぱな。ではCoreクラス以外にもいくつかのクラスが登場します。
クラスごとにどういった用途があって、どのようにして使われるのかはその都度合わせて紹介したいと思います。

~/src/edit/main.js

// CoreObjectの定義
var game = new Core(window_width,window_height);
game.fps = 30.0;
game.preload('./libs/images/chara1.png', 
             './libs/images/background.png',
             './libs/images/pipe.png');

... 途中省略 ...

Coreクラスのインスタンスを生成したら、さらに設定を行っていきます。ソースコードの10行目ではFPSの設定を行っています。
FPSは一秒あたりに処理されるフレーム数のことを表しています。game.fpsには30.0を代入しているので、この例では一秒あたりに30回分のフレーム経過・ループが行われる事を意味しています。

パラパラ漫画に例えると、一秒間に30ページずつめくれていくイメージで考えていただけると大丈夫です:-)

11行目ではpreloadと言う命令を呼び出して、事前に読み込む画像をセットしています。
今回は事前に利用する画像を全てセットしているので、ここに対して手を加えていく事はありません。
もし、ご自身でいろいろ画像を追加したいと思った時にはこちらも忘れずに追加するようにしておきましょう:-)

メインループの基本処理

~/src/edit/main.js

... 途中省略 ...

game.onload = function(){
    /*
        背景の描画処理
    */

    // クマ (プレイヤーの定義)
    var size   = 32;
    var bear   = new Sprite(size,size);
    bear.image = this.assets['./libs/images/chara1.png'];   // 描画する画像を指定する
    bear.x     = (window_width/2)-(size/2);                 // 画像の描画位置の指定(x軸)
    bear.y     = (window_height/2)-(size/2);                // 画像の描画位置の指定(軸)
    bear.frame = 0;                                         // 描画領域の範囲を指定する
    game.rootScene.addChild(bear);                          // クマを画面上に描画する

    // メインループ
    game.on('enterframe', function(){
        // 主要なプログラムはこの中に書いていきます
    });
}

game.start();

ソースコードの15行目から無名関数内にゲーム中の処理を定義しています。
ここからの内容がメイン部分の処理になってきます。
定義した無名関数をgame.onloadに代入する事で代入された関数の処理に沿ってゲームが動くようになります。

上記のコード表では30行目となっていますが、そこからはメインループの定義を書いています。
また、メインループより前にはゲーム上で利用される画像の描画を行っています。

波括弧の範囲内に書いたプログラムは、ページを読み込んだ後もくり返しくり返し実行されます。
この中でユーザーのキー入力などはを受け取りながら、事前に定義した決められた処理を決められた通りにくり返して行くわけです:-)

とは言っても、ここまでの内容だけではいまいちピンと来ない部分もあるかもしれません。
そこで、次の項目では画像の描画についても取り上げつつ、実際にクマを操作するプログラムを作ってより詳しく内容を見ていきます。

↑トップへ戻る

2章ではenchant.jsのセットアップを皮切りに、大枠の動きと仕組みについて紹介を行いました。
3章では、2章でちょっと出てきたメインループがどういうものなのかをプログラムを書きながら紹介したいと思います。
実際にキー入力操作を受け取るプログラムを書いて、クマを動かせるようにしてみましょう:-)

スプライトを描画する

画像を描画するには、Spriteクラスを用いて行います。

~/src/edit/main.js

... 途中省略 ...
    // クマ (プレイヤーの定義)
    var size   = 32;
    var bear   = new Sprite(size,size);
    bear.image = this.assets['./libs/images/chara1.png'];   // 描画する画像を指定する
    bear.x     = (window_width/2)-(size/2);                 // 画像の描画位置の指定(x軸)
    bear.y     = (window_height/2)-(size/2);                // 画像の描画位置の指定(軸)
    bear.frame = 0;                                         // 描画領域の範囲を指定する
    game.rootScene.addChild(bear);                          // クマを画面上に描画する

... 途中省略 ...

Spriteクラスをインスタンス化する時に、引数に描画する画像の幅と高さをそれぞれセットする事ができます。
こうする事でスプライトを生成する事が出来ます。スプライトはイメージ的には額のようなもので、実際の画像から描画領域を決定してはめこまれた物が画面上に表示されるようになっています。
ここでは描画を行う前に画像の指定と、それをどこに描画するのかx軸とy軸それぞれに指定しています。
実際に53行目で指定している画像chara1.pngは以下のような画像になっています。

3-1.png
いろいろなクマが...
3-1_mark.png
bear.frameの値に合わせて変わるんです。

このように複数の画像を1つの画像にタイル状に取りまとめたものをスプライトシートと言います。 個別に画像を準備する場合と比べた場合、役割に合わせて画像をまとめる事で管理が行いやすく、画像の容量を抑える事が出来るメリットがあります。

enchant.jsではこのようなスプライトシートを手軽に使う為の機能が提供されています。56行目bear.frameがそうです。

明示的に指定していない場合、ないしは0を代入している場合はスプライトシートの左上を基準にインスタンス化した際に引数で渡した画像の幅と高さをもとにしてスプライトを生成します。試しにサンプルのプログラムでbear.frameに代入する値を0から1へとじゅんぐり上げていくと赤枠の描画領域から、右に1つずつずれて表示されます。もし良かったら聞きながらでも構いませんので、いろいろと数値をいじってどうなるか試してみてください:-)

キー入力を受け取る 〜もしなになにだったら〜

スプライト描画についての仕組みがわかったところで、いよいよ描画されたクマをユーザーの操作で動かせるようにしていきます。
実際に、キー入力によってクマが上下左右に入力されたキーに合わせて動くようにします。

キー入力を受け取って画面に結果を反映させる為には「もし上方向にキーが押されていたら」「もし下方向にキーが押されていたら」のように「もしなになにだったら」と言う処理を追加してあげなければなりません。JavaScriptではif文を使うことによって実現する事が出来ます。

JavaScriptでもしもなになにだったら〜

// 単体
if (/* 条件式 */) {
    // 条件式に一致した時に実行される処理
}

// 複数分岐
if (/* 条件1 */) {
    // 条件1に一致した時に実行される処理
}else if (/* 条件2 */){
    // 条件2に一致した時に実行される処理
}else{
    // 条件1にも条件2にも一致しない場合実行される処理
}

// 例 ログには「正」の字が出力される
var value = 1;
if(value==1){
    console.log('正');
}else{
    console.log('不正');
}

JavaScriptにおけるif文の書き方は上記のような形になっています。条件式をもとにして正しい内容であれば、括弧の中へと処理が進んでいくようになります。また、else ifを使うことによって、複数の条件を候補としたより柔軟な処理が行えるようになります。

この内容をもとにして、キー入力を受け取れるようにしたサンプルが下記になります。

~/Chapter/Chapter3/inputs/main.js

window.onload = function(){
    enchant();  // enchant.jsの利用宣言 必ず最初に宣言する

    var window_width  = 320;    // ゲーム領域の幅
    var window_height = 568;    // ゲーム領域の高さ

    // CoreObjectの定義
    var game = new Core(window_width,window_height);
    game.fps = 30.0;

    game.onload = function(){
        // メインループ(この中に処理を書いていきます)
        game.on('enterframe', function(){
            if(game.input.up){
                console.log('方向キーの上キーが押されました');
            }else if (game.input.down){
                console.log('方向キーの下キーが押されました');
            }else if (game.input.left){
                console.log('方向キーの左キーが押されました');
            }else if (game.input.right){
                console.log('方向キーの右キーが押されました');
            }
        });
    }

    game.start();
};

ブラウザから確認すると、画面上ではこれと言って何も表示されていませんが、上下左右の方向キーのいずれかを入力すると、対応するログがコンソール上に出力される事を確認する事ができます。
enchant.jsはCoreクラスのinputでゲームに対する入力操作を管理しており、if文を使ってこのような形で条件式としてメインループ内で定義することによってユーザーのキー入力に対する処理を実装する事が出来ます。

クマを動かせるようにしてみよう:実践編

それではここまでの内容を踏まえて~/src/edit/main.jsを拡張します。クマを操作して動かす為のプログラムを書いてみましょう!
なお、はじめに処理の流れを確認したいと思います。おおまかな処理の流れは以下の通りです。

おだいもく
  • 処理自体はメインループの中に定義していく
  • 現在中央に表示されている、クマをキー入力に合わせて動かせるようにする
  • if文を使って「もし上方向に押されていたら」と言う形で、上下左右の方向キーに対する処理を追加する
  • 3番で定義したif文にそれぞれクマの移動処理を実装します

なお、描画されたクマのスプライトの座標はbear.x, bear.yのように定義したスプライトのx, yそれぞれに数値を代入する事で表示される座標を更新する事が出来るようになっています。
この点に対してあらかじめ確認した上で、クマを自由に動けるようにプログラムを拡張してみましょう!

↑トップへ戻る

3章では、メインループの仕組みをよりイメージしやすくするべくキー入力操作のプログラムを実装しました!
キャラクターを画面上に描画して、動かせるようになったら今度は衝突判定について紹介していきます。
キャラクターを動かして、衝突判定をつけれるようになったらそれはもうゲーム!それでは、張り切ってまいりましょう!

当たって砕ける、衝突判定いろいろ

衝突判定はある物体と、それとは異なる別の物体が衝突したのかそうでないかを判定するプログラムの事を言います。別の言い方として当たり判定なんて言い方もあります。衝突判定にもいろいろな方法がありますが、その中でも基本となるものは矩形による衝突判定処理です。

衝突判定
矩形の衝突判定

このように描画された物体の範囲を矩形として扱うことで、それぞれの座標を確認して物体同士の重なりを表現し判定する事が出来ます。
ただし、描画される物体には矩形でないものが多いので、画像によっては物体同士が衝突していなくても衝突されたものとして扱われる場合もあります。出来る限り画像と矩形の大きさを同じようにする事で自然な形になります。

矩形の判定が基本的な判定処理となりますが、単純な矩形の判定を自分で作ろうと思っても結構大変です。
そこで、今回はenchant.jsのSpriteクラスに内包されているintersectメソッドを使った衝突判定の付け方を紹介します。

衝突判定を作るには

それでは衝突判定の実装について、サンプルをもとに紹介したいと思います! ブラウザから確認してみると以下のような画面が表示されます。

クマと土管と...
クマと土管と...

クマ土管という珍妙な組み合わせの画面が表示されましたでしょうか! こちらのプログラムでは実際にクマを操作して動かす事が出来るようになっており、土管にぶつかるとブラウザ上でアラートが表示されるようになっています。そこにはなんと衝撃のメッセージが・・・。

~/Chapter/Chapter4/collision/main.js

... 途中省略 ...

// 障害物の定義
var pipe   = new Sprite(64, 568);
pipe.image = game.assets['./libs/images/pipe.png'];
pipe.x     = window_width-128;
pipe.y     = 100;
pipe.frame = 0;
game.rootScene.addChild(bear);  // クマを画面上に描画する
game.rootScene.addChild(pipe);  // 障害物を画面上に描画する

// メインループ(この中に処理を書いていきます)
game.on('enterframe', function(){
    // クマと土管が衝突したらif文の中へ
    if(bear.intersect(pipe)){
        game.stop();
        window.alert("GAME OVER...");
    }

... 途中省略 ...

事前に障害物となる土管を描画する事で、メインループの先頭でクマと土管の衝突判定処理を組み込んでいます。
衝突判定の実装は33行目から行っています。このように描画した物体(スプライト)のintersectメソッドを呼び出して、引数に衝突判定を行う物体をセットする事で、物体同士の衝突判定を行う事が出来ます。物体同士が衝突している場合にif文の中の処理が実行されます。

そして、なんという事でしょう・・・。ここではクマと土管が衝突するとGAME OVER...と書かれたアラートが出力されます!
ここでは簡単なゲームオーバー処理を行っています。Coreクラスのstopメソッドがそうです。
stopメソッドを呼び出す事によって、startメソッドが呼び出されているCoreクラス内のメインループを全て停止する事が出来ます。

また、ここで描画されている土管は今後も障害物用として利用していきます。
土管の元画像はクマの元画像の用に複数の画像を取りまとめたスプライトシートとして管理しています。

pipe.png
土管が障害物になるんです

こちらもクマの画像と同様にframeの値を0から1に変更する事によって、画面上に描画する内容を変更することが出来ます。
これを上手く使うとサンプルでは下から上に突き出しているような表現になっていますが、座標と合わせて調整してあげる事で上から下に突き出しているような表現も行えるようになります。
もし良かったら聞きながらでも構いませんので、いろいろとframeや座標(x,y軸)を弄って試してみてください:-)

クマが障害物にぶつかった時のイベントを作ってみよう:実践編

それではここまでの内容を踏まえて~/src/edit/main.jsを拡張していきましょう!
自分が操作するクマが宇宙空間に突如現れた土管に衝突した時のプログラムを書いていきます。
またしても、はじめに処理の流れを確認したいと思います。おおまかな処理の流れは以下の通りです。

おだいもく
  • 処理自体はメインループの中に定義していく
  • 画面上の適当な位置へ土管を描画する
  • if文と組み合わせて「クマと土管が衝突した場合」を条件にした、衝突判定を作る
  • メインループを停止し、アラートを出力する

4章では衝突判定についての紹介と、enchant.jsでの実装の仕方について紹介しました。
次章が最後の内容となっていて、これまでの内容を踏まえてよりゲームっぽくしていきたいと思います。

↑トップへ戻る

4章では衝突判定についての紹介と、enchant.jsの場合における実装の仕方について紹介してきました。5章ではこれまでの内容を踏まえて等間隔での障害物の配置や、スコアの加算処理を組み合わせてよりゲームっぽくするために必要な機能の作り方を紹介したいと思います。

完成図をイメージしよう

さあさあ、それでは早速作り進めていきましょう!・・・と言いたいところですが、気持ちをグッとこらえてどんな形にしていくのかまずは完成図をイメージしていきましょう。こちらを開いてみてください。

pipe.png
これが・・・ゲームだ!

宇宙空間突如として飛来した土管の群れ・・・。それを懸命潜り抜けていくクマ・・・。明日は、どっちだ!?
と、ちょっとばかり取り乱してしまいましたが、遊んでみるとなんだかゲームっぽい。と言うか、ゲームになってる。なんだかそんな感じがしませんか?どうでしょう・・・。
遊んでみるととても難しそうに感じるかもしれませんが、これまでの内容をベースにしていく事でここまでのものが作れるようになります。
ゲームを構成する要素として、どのようなものが備わっているのかを落ち着いて確認してみましょう。

完成予定のゲームを構成する要素
  • クマの表示・キー操作による上下移動
  • 障害物となる土管の表示・右から左への移動
  • 障害物は一定間隔でランダムな隙間を作って表示される
  • 土管を潜り抜けると、上部のスコアが加算されていく
  • クマと土管における衝突判定、ゲーム終了のアラートが表示される

こうして明文化して列挙してみると、なんだかちょっとだけ出来そうな気がしてきました。この中で今回のハンズオンでまだ取り扱っていない内容は2番の土管移動と3番4番の内容となっています。どちらもゲームを作る上で大切な機能となっています!
これらの機能はenchant.jsでは比較的簡単に実装する事が出来ます。次の内容より早速ですがどのような仕組みで作られているのかをご紹介したいと思います。遊びながらでもオッケーです:-)

動くのはクマだけじゃない!?障害物にも動きをつけてみよう!

まず、はじめに障害物に動きをつける方法についてご紹介したいと思います。
4章で作ったプログラムの内容には障害物に対する衝突判定が実装されているものの、それが動いたりすることはありませんでした。
これが障害物が動くようになる事によって、動く障害物をかわしていくといったゲームが作れるようになります!
こちらもサンプルのプログラムを見ながら紹介したいと思います。こちらを開いてみてください:-)

move_pipe.png
右から左に土管が流れていますか?

キー操作の時に行った方法を使って、ループをくり返す度に土管のx軸座標を更新します。そうすると、上手い具合に画面上で障害物が動いてるさまを表現する事ができます。特段変わった仕組みは今回の内容では行っておりませんが、1つだけ確認したい内容があります。サンプルに実装されている、下記のコードです。

~/Chapter/Chapter5/move/main.js

... 途中省略 ...

game.onload = function(){
    // 障害物の定義
    var pipe   = new Sprite(64, window_height);
    pipe.image = game.assets['./libs/images/pipe.png'];
    pipe.x     = window_width;
    pipe.y     = 100;
    pipe.frame = 0;
    game.rootScene.addChild(pipe);  // 障害物を画面上に描画する

    // 画面全体とは別にスプライトに対しても毎フレームごとの動きを定義できる
    pipe.on('enterframe', function(){
        this.x -= 2;
    });

... 途中省略 ...

これまでは、ゲームの進行全般を管理しているCoreクラスで提供されているonメソッドを使って、毎フレームごとに繰り返し実行される処理を定義してきました。実は、Coreクラスからだけではなく同じようにSpriteクラスからでもonメソッドを呼び出す事ができます。
21行目から24行目がその処理となっていて。こうする事で、Coreクラスで定義しているメインループとは別に描画しているスプライトに対しても毎フレームごとに実行される処理を定義する事が出来るようになります!

なお、JavaScriptで受け取ったイベントの処理内容として定義されている無名関数内のthisはイベントもとのオブジェクトの事を指します。
ちょっとでも覚えておくと、見通しも良くより安全なコードが書けるかもしれません:-)
本サンプルでは描画した土管のスプライトを指しており、毎フレームにx軸を少しずつずらしていく。という処理になっています。

一定のタイミングで障害物を配置してみよう

障害物の土管を動かせるようになったら、今度は障害物を一定のタイミング毎に配置をしていく方法をサンプルをもとに紹介していきます!
まずはじめにこちらのプログラムを開いてみてください。

schedule.png
一定の間隔で土管がいろいろな高さでどんどん流れてきます。

動かしてみると、一定の間隔で土管が右から左に不規則な高さで流れていくのを確認する事が出来ます。
このような仕組みはゲームでは結構多くて、作り方もいろいろあるかと思うのですが今回は一例として経過フレーム数をもとにして、障害物となる土管を描画していく方法について紹介したいと思います。

enchant.jsではCoreクラスのframeというプロパティから、ゲームがスタートしてからの経過フレーム数を受け取る事が出来ます。
今回のサンプルでは経過フレーム数をもとに、60フレームおきに土管を描画して右から左に流すように処理を定義しています。

~/Chapter/Chapter5/schedule/main.js

... 途中省略 ...

// 60フレームおきに、土管を表示する
if(game.frame%60==0){
    // 障害物の定義
    var pipe_width  = 64;
    var pipe_height = 568;

    var pipe        = new Sprite(pipe_width, pipe_height);
    pipe.image      = game.assets['./libs/images/pipe.png'];
    pipe.x          = window_width;
    pipe.y          = (pipe_height/2)-Math.floor(Math.random() * 150);
    pipe.frame      = 0;
    game.rootScene.addChild(pipe);  // 障害物を画面上に描画する

    pipe.on('enterframe', function(){
        this.x -= 2;

        // クマと土管が衝突したらif文の中へ
        if(bear.intersect(this)){
            game.stop();
            window.alert("GAME OVER...");
        }
    });
}

... 途中省略 ...

経過フレーム数が割り切れる場合に〜〜をする場合3章でも取り上げた、if文を組み合わせる事で実現する事が出来ます。
条件式には経過フレーム数/60の余りが0だったら真とする。という条件が入っています。
こうする事によって経過フレーム数が60の倍数の時に60で割り切れるので、60フレームおきに何らかの処理を行う。と言った事が出来るようになります。

今回の内容では他に真新しいところはなく、60フレームおきに障害物を描画するというものになっていますが、障害物の高さをまばらにする為に乱数を生成して調整するようにしています。
50行目で行っておりMath.floor(Math.random()*150)では150までを範囲にランダムに数を返すようになっています。
こういった乱数を用いて障害物の高さをまばらにする事で、遊んでいて退屈しないような仕組みを取り入れれるようになります。

一定のタイミングで障害物を配置してみよう:実践編

それではここまでの内容を踏まえて~/src/edit/main.jsを拡張していきましょう!宇宙空間突如として飛来した土管の群れ・・・。
こちらをクマを操作して潜り抜けていくプログラムを作っていきます。
今回の内容は少し複雑なところもあって項目も多いので、落ち着いてまずは処理の流れを確認したいと思います。

おだいもく
  • 処理はメインループの中で定義していく
  • 自身のキャラクターとなるクマの移動範囲を上方向と下方向のみに修正する
  • 60フレームおきに処理を動かす為の条件式を追加する
  • 60フレームおきに上下それぞれに潜り抜ける隙間が有る土管を描画し、右から左へとフレーム毎に移動させる
  • if文と組み合わせて「クマと土管が衝突した場合」を条件にした衝突判定を作る
  • 衝突した場合メインループを停止し、アラートを出力する

ここまで行く事が出来れば、あとはスコア加算の処理を行うだけです:-)
ゲーム上で文字を描画する場合、enchant.jsではLabelクラスを利用する事が出来ます。
次の項目ではそんなLabelクラスについて紹介させていただき、スコアの加算処理を作ってみたいと思います。

スコア用のラベルを表示するには?

画面上に文字を表示する場合、enchant.jsではLabelクラスを使って実現する事が出来ます。
ブラウザから確認すると、画面上部中央に「1192」と書かれている画面が表示されているかと思います。

~/Chapter/Chapter5/label/main.js

window.onload = function(){
    enchant();

    // CoreObjectの定義
    var window_width  = 320;    // ゲーム領域の幅
    var window_height = 568;    // ゲーム領域の高さ

    var game = new Core(window_width,window_height);
    game.fps = 30.0;

    game.onload = function(){
        // スコア表示用のラベル
        var score_label   = new Label(String(1192));        // new Label(1192);とかだとエラーになる
        var label_width   = score_label._boundWidth;        // 描画したラベルの幅を取得する
        var label_height  = score_label._boundHeight;       // 描画したラベルの高さを取得する

        score_label.x     = 10;
        score_label.y     = 10;
        score_label.color = '#000';
        game.rootScene.addChild(score_label);
    }

    game.start();
};

スプライトの動きなど一切行われていない、ラベルを表示するだけのシンプルなプログラムになっています。
Label型のオブジェクトを生成してからそれに対して必要な情報をセットし、表示座標と文字の色を調整しています。

注意事項としては、Labelクラスのオブジェクトを作成する時にセットする引数が文字列以外の場合エラーとなってしまいます。
なので、スコアと言った数字のデータを表示したい時は13行目のように文字列形式でキャストするようにしてください。

スコア判定を追加してみよう:実践編

それではここまでの内容を踏まえて~/src/edit/main.jsを拡張していきましょう!
最後になっても、変わらずまずはじめに処理の流れについての確認から行いたいと思います。

おだいもく
  • ゲーム上にラベルを出力するプログラムを追加する
  • 自身のキャラクターが土管を潜り抜ける度に、スコアが2点ずつ加算されていく

スコアに加算する条件もいろいろな方法があるかとは思いますが、障害物の土管が右から左に流れていった時に画面の中央よりも左側に移動するタイミングでスコアを加算してあげる方法が比較的シンプルで取っ付きやすいかもしれません。
もし、行き詰まったりされた時にはご参考にしていただけると幸いです:-)

↑トップへ戻る

お疲れ様でした、いかがでしたでしょうか?
僕達が普段遊んでいるゲームがどのようにして動いているのか、ちょっとでも雰囲気が掴めたなら幸いです。

ゲームエンジンも今回はenchant.jsを採用しましたが、他にも沢山のものがあります。
enchant.jsを掘り下げてみるのも良いですし、他にもどういったものがあるのか見てみるのも良いかもしれません。
エンジンが変わっても、言語が変わっても今回やった内容や見方なんかは使っていける部分もある・・・と良いなと思います。
自分に合ったものを見つけて、仲間を見つけて。ああだこうだ言いながら「僕が考えたさいきょうのげーむ」を作ってみるのも良いかもしれません。もちろん一人でやるのも良いですが、誰かとやるのもそれはそれでまた別の楽しさがあります。
また、次も機会があれば今度はゲームエンジンを変えたりして3Dなんかも・・・。
なんて思いつつ、ありがとうございました!

@eccyun
てんてーちぶ
Twitter