By U Zensen.

欲しいものはプログラミングで自分で作る。

tmlibを使って初代ポケモンをアプリで再現してみた!!(基礎&背景編)

2015 / 8 / 17
アプリ制作
20150817-compressor-2

はじめに!!

お久しぶりです。ぴーすけと申します!今年はお盆休みを9連休もいただきまして、友達とバイクで江ノ島に行ったり、バーベキューをしたりと夏を満喫しておりました。しかし、遊んでばかりいた訳では有りません!tmlibを使用したアプリ制作の方も少しずつ勉強しておりました。まだ完成では有りませんが、初代ポケモンの挙動を少し作ることができたので、まとめたいと思います。

【 注意 】

1)tmlibのバージョンは0.5.0で動作するように作成していますが、作成にあたり色々なバージョンのソースコードを参考にしているので0.5.0での最適なコードがかけているかは分かりません。

2)javascript初心者なこともあり、とにかく動かすこととtmlibでの書き方を理解することが目的だったので、ムダにあちこちメソッド化をしてみたり、めちゃくちゃなソースコードになってしまっていると思われます。丸々コピーしてもバグがあるかもしれないので気をつけてくださいね。

*1

まずはソースコード!!

まずはjsdo.itはこちら! 制作にあたって使用している画像はjsdo.itのサーバーに上げさせてもらっていて、それをrunstantとかでも使っています。

そして、runstantはこちら!!

ランダムでアイテムが4つ生成させれるので、アイテムの目の前でAボタンを押すとアイテムが回収できます。どうでしょうか?初代のポケモンと同じような挙動はできているでしょうか?ドットは自分で打ちました。

再現できた挙動!

いまのところ再現できている挙動は

  1. マップの生成
  2. 主人公の上下左右の移動
  3. 壁のあたり判定
  4. アイテムの生成、回収

です!ゲームのプレイ動画を漁りながら似たような挙動を目指して作りました。こう見るとこれだけかーと思ってしまいますが、一つ一つを実装するのに結構時間をかけました。しかし、tmlib.jsのおかげでそれなりに実装できたと思っています。

*2

詳しく解説します!!

tmlib.jsの基本的な書き方!

まずは、tmlib.jsの基本的な書き方です。はじめの一歩としては「tm.main」と「GameScene」というクラス(機能の固まり)のようなものをつくってそこにどんどん追記していきます。色々考えて私が導いた(いまのところ)のテンプレートです!

HTML

<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, user-scalable=no" /> <meta name="apple-mobile-web-app-capable" content="yes" /> <title>${title}</title> <meta name="description" content="${description}" /> <style>${style}</style> <script src="http://cdn.rawgit.com/phi-jp/tmlib.js/0.5.0/build/tmlib.js"></script>      <script>${script}</script> </head> <body> <canvas id='world'></canvas> </body> </html>

js

// ここに定数を記述しておく var SCREEN_WIDTH = 640; var SCREEN_HEIGHT = 960; var SCREEN_CENTER_X = SCREEN_WIDTH / 2; var SCREEN_CENTER_Y = SCREEN_HEIGHT / 2; // 画像はこのようにまとめておく。画像はjsdo.itで画像を上げています。 var ASSETS = { "player" :"http://jsrun.it/assets/b/K/P/z/bKPz4.png", "ball" :"http://jsrun.it/assets/u/2/J/U/u2JUp.png", "tip00" :"http://jsrun.it/assets/f/8/9/f/f89f2.png", "tip10" :"http://jsrun.it/assets/l/I/E/y/lIEyt.png", "tip20" :"http://jsrun.it/assets/r/i/q/p/riqpD.png", }; //メイン まずはここから処理がはじまる tm.main(function() { // ここでスクリーンの設定。htmlに書いてある「#world」というcanvasを対象 // 定数で決めたサイズに設定して、さらに画面いっぱいいっぱいにする var app = tm.app.CanvasApp("#world"); app.resize(SCREEN_WIDTH, SCREEN_HEIGHT); app.fitWindow(); // ロード画面で画像を読み込む var loading = tm.ui.LoadingScene({ assets: ASSETS, // 定数で設定した画像たちを読み込む width: SCREEN_WIDTH, height: SCREEN_HEIGHT, }); // ロード完了後に呼ばれるメソッドを登録 loading.onload = function() { var scene = GameScene(); app.replaceScene(scene);// GameSceneクラスを呼び出す }; // ローディングシーンに入れ替える app.replaceScene(loading); app.run();//実行! }); // ゲームクラス (ロード後に呼び出されるやつ) tm.define("GameScene",{ superClass: "tm.app.Scene", // 初期化 init: function(){ // 親クラスの初期化 this.superInit(); alert("読み込みOK"); // アラートが出ればOK }, });

上記のrunstantです。開くと画面は真っ暗ですが、アラートが表示されたなら問題ないです。

基本的にはjavascriptのオブジェクトの書き方と一緒です。この「GameScene」というのはゲームのメイン画面となるところにします。他にも自作のクラスを作るときにもこの形が基本形となり、クラス名を自分で決めて継承先のクラスを変更することで、クラスが作れると思います。

この「GameScene」にこれからどんどん追記していくのですが、ここに直接コードを書いて行くと少し見づらい気がしたので、私は機能をメソッド化してそれを「GameScene」から呼び出す用にしています。もちろんこのクラスに直接処理を書いても問題なく動きます。

コードを説明する前に文字で処理を説明したいと思います。

js

tm.define("GameScene",{ superClass: "tm.app.Scene", // 初期化 init: function(){ // 親クラスの初期化 this.superInit();      // マップの生成      // キャラクターの生成      // アイテムの生成      // ボタンの生成      // ボタンの一つ一つに機能を追加   }, });

こんな感じです。それでは一つ一つ説明していきたいと思います。ふー長くてすいません。

1.マップの生成

背景マップを生成します。 まずはrunstant 下記のコードにいきなり沢山の変数が登場していますが、それはrunstantに書いてあるので参考にしてください。


// ゲームクラス
tm.define("GameScene",{
    superClass: "tm.app.Scene",
    
    // 初期化
    init: function(){
        // 親クラスの初期化
        this.superInit();

        //ステージ(背景)の生成
        // imgStageBase → 画像
        // stageDate → 背景情報(00 = 何もなし、01 = 置物あり)
        imgStageBase = new Array( STAGE_TIP_Y );
        stageData = new Array( STAGE_TIP_Y );
        for( i=0; i < STAGE_TIP_Y; i++ ) {
            imgStageBase[i] = new Array( STAGE_TIP_X );
            stageData[i] = new Array( STAGE_TIP_X );
            for( j=0; j < STAGE_TIP_X; j++ ) {
                if(i <= 7){
                    //背景の生成
                    stageData[i][j] = "00";
                    imgStageBase[i][j] = tm.display.Sprite("tip00", STAGE_TIP_WIDTH, STAGE_TIP_HEIGHT);
                    imgStageBase[i][j].setPosition(STAGE_TIP_WIDTH*j+STAGE_TIP_WIDTH/2, STAGE_TIP_HEIGHT*i+STAGE_TIP_HEIGHT/2 );
                    imgStageBase[i][j].addChildTo(this);
                }
                 if((j === 0 && i <= 7) || (j === 7 && i <= 7)|| i === 0 || i === 7){//ここで壁の位置を調整している
                    //壁の生成
                    stageData[i][j] = "10";
                    imgStageBase[i][j] = tm.display.Sprite("tip10", STAGE_TIP_WIDTH, STAGE_TIP_HEIGHT);
                    imgStageBase[i][j].setPosition(STAGE_TIP_WIDTH*j+STAGE_TIP_WIDTH/2, STAGE_TIP_HEIGHT*i+STAGE_TIP_HEIGHT/2 );
                    imgStageBase[i][j].addChildTo(this);
                }
            }       
        }
    },
});

上記コードの説明は以下の通りです。

1-compressor

まずは背景の生成です。「stageDate」には、背景のデータを文字にして格納します。床を置きたいところに「00」という文字列をいれておきます。

3-compressor

そして、「stageDate」の「00」が入っている座標と同じ位置の「imgStageBase」に床画像を作成します。マップの文字データが入っている「stageDate」とマップの画像データが入っている「imgStageBase」の関係を同じにするのがポイントです。

2-compressor-2

次に、壁を設置したいところの「stageDate」に「10」をいれておきます。

5-compressor

先ほどと同じように「stageDate」の「10」が入っている座標と同じ位置の「imgStageBase」に壁画像を作成します。

4-compressor

床が「00」で壁が「10」なのは、後であたり判定を付ける際に差別したいためですので、値自体は何でも大丈夫です。実際に「stageDate」や「imgStageBase」に値をいれている処理は下記のような箇所です。

床の生成

stageData[i][j] = "00"; //00を入れる imgStageBase[i][j] = tm.display.Sprite("tip00", STAGE_TIP_WIDTH, STAGE_TIP_HEIGHT); //tip00(床)を横幅をSTAGE_TIP_WIDTH(80)で立幅をSTAGE_TIP_HEIGHT(80)に設定 imgStageBase[i][j].setPosition(STAGE_TIP_WIDTH*j+STAGE_TIP_WIDTH/2, STAGE_TIP_HEIGHT*i+STAGE_TIP_HEIGHT/2 ); //setPositionで生成する座標の設定 imgStageBase[i][j].addChildTo(this); //addChildToでthisに生成

壁の生成

stageData[i][j] = "10"; //10を入れる imgStageBase[i][j] = tm.display.Sprite("tip10", STAGE_TIP_WIDTH, STAGE_TIP_HEIGHT); //tip10(壁)を横幅をSTAGE_TIP_WIDTH(80)で立幅をSTAGE_TIP_HEIGHT(80)に設定 imgStageBase[i][j].setPosition(STAGE_TIP_WIDTH*j+STAGE_TIP_WIDTH/2, STAGE_TIP_HEIGHT*i+STAGE_TIP_HEIGHT/2 ); //setPositionで生成する座標の設定 imgStageBase[i][j].addChildTo(this); //addChildToでthisに生成

ってな感じです。説明が下手でスイマセン。

*3

つづくよ!!

本当は一気にこの記事にまとめようと思いましたが、量が多くなりそうだったので、基礎&背景編としてここで区切ります。次回はキャラクター編をご紹介したいと思います!

まとめ!!

マップの生成に関しては参考にさせていただいたサイトで紹介されている方法をほぼそのまま採用させていただいています。見た目用の変数(imgStageBase)とあたり判定用の変数(stageDate)を用意する方法です。

ほとんどそのまま使わせていただいております。ありがたやです!

Pocket

コメント

コメントを残す