2013/7/31 - 11:49 - Category / game, JavaScript, tmlib.js
2013/7/14 - 00:38 - Category / JavaScript, Node.js, tmlib.js
node.jsを使ったサンプル集です。ゲーム制作でサンプル作ってたら溜まったので整理がてら公開します。node.jsをローカルで実行できる環境を作ってない方は、「初心者でも安心!?Node.jsをインストールするなら仮想サーバを使おう」を参考にしてみてください。また、socket.ioやmongoDBを利用する際は、都度インストールしてください。
※ タイトルを「node.js 怒濤の50サンプル!! – websocket編」から「node.js 怒濤の50サンプル!! – socket.io編」に変更
※ 厳密には異なるものとは知らず誤解を招くタイトルだったためです。申し訳ありません。
2013/6/17 - 19:28 - Category / game, JavaScript, tmlib.js, Tutorial
モンスターを表示しよう
サンプル
コードの解説
モンスターを表示して動かしてみます。プレイヤークラスと同じような要領でクラスを作成します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 |
/** * ゲーム用定数作成 */ var SCREEN_WIDTH = 960; var SCREEN_HEIGHT = 640; var RESULT_PARAM = { score: 256, msg: "【避けゲー制作チュートリアル】", hashtags: ["omatoro", "tmlibチュートリアル"], url: "http://omatoro.github.io/tmlib.js_tutorial_avoidgame/", width: SCREEN_WIDTH, height: SCREEN_HEIGHT, related: "tmlib.js Tutorial testcording", }; var UI_DATA = { main: { // MainScene用ラベル children: [{ type: "Label", name: "timeLabel", x: 200, y: 120, width: SCREEN_WIDTH, fillStyle: "white", // text: "残り時間を表示する", text: " ", fontSize: 40, align: "left" }] } }; var PLAYER_WIDTH = 20; var PLAYER_HEIGHT = 16; var ENEMY_WIDTH = 38; var ENEMY_HEIGHT = 30; /** * リソースの読み込み */ var ASSETS = { "player": "http://rawgithub.com/omatoro/tmlib.js_tutorial_avoidgame/gh-pages/rsc/[Animal]Chicken.png", "playerSS": "http://rawgithub.com/omatoro/tmlib.js_tutorial_avoidgame/gh-pages/rsc/playerSS.tmss", "enemy": "http://rawgithub.com/omatoro/tmlib.js_tutorial_avoidgame/gh-pages/rsc/[Monster]Dragon_B_pochi.png", "backMap": "http://rawgithub.com/omatoro/tmlib.js_tutorial_avoidgame/gh-pages/rsc/map.png", }; /** * ゲーム起動処理 */ tm.main(function() { // アプリケーション作成 var app = tm.app.CanvasApp("#world"); app.resize(SCREEN_WIDTH, SCREEN_HEIGHT); // 画面サイズに合わせる app.fitWindow(); // リサイズ対応 app.background = "rgb(0, 0, 0)"; // 背景色をセット var loadingScene = tm.app.LoadingScene({ assets: ASSETS, nextScene: TitleScene, width: SCREEN_WIDTH, height: SCREEN_HEIGHT, }); // シーンの切り替え app.replaceScene(loadingScene); // tmlibの実行 app.run(); }); /** * TitleScene */ tm.define("TitleScene", { superClass : "tm.app.TitleScene", init : function() { this.superInit({ title : "避けゲー制作チュートリアル", width : SCREEN_WIDTH, height : SCREEN_HEIGHT }); // 画面(シーンの描画箇所)をタッチした時の動作 this.addEventListener("pointingend", function(e) { // シーンの遷移 e.app.replaceScene(MainScene()); }); }, }); /** * MainScene */ tm.define("MainScene", { superClass : "tm.app.Scene", init : function() { this.superInit(); // Map this.map = tm.app.Sprite("backMap", SCREEN_WIDTH, SCREEN_HEIGHT).addChildTo(this); this.map.position.set(SCREEN_WIDTH/2, SCREEN_HEIGHT/2); // Player this.player = Player().addChildTo(this); this.player.position.set(150, 600); // enemy this.enemyGroup = tm.app.CanvasElement().addChildTo(this); // スコア用カウントアップ this.timer = 0; // ラベル表示 this.fromJSON(UI_DATA.main); // 画面(シーンの描画箇所)をタッチした時の動作 this.addEventListener("pointingend", function(e) { // シーンの遷移 e.app.replaceScene(EndScene()); }); }, update: function (app) { // カウントアップを行う ++this.timer; // 敵の生成(難易度をどんどん上げる) if (this.timer % 30 === 0) { for (var i = 0, n = (this.timer / 300); i < n; ++i) { var enemy = Enemy().addChildTo(this.enemyGroup); enemy.x = Math.rand(0, SCREEN_WIDTH); enemy.y = 0 - enemy.height; } } }, }); /** * EndScene */ tm.define("EndScene", { superClass : "tm.app.ResultScene", init : function() { this.superInit(RESULT_PARAM); }, // Backボタンを押したらTitleSceneに戻る onnextscene: function (e) { e.target.app.replaceScene(TitleScene()); }, }); /* * player */ tm.define("Player", { superClass: "tm.app.AnimationSprite", init: function () { this.superInit("playerSS", PLAYER_WIDTH*4, PLAYER_HEIGHT*4); this.gotoAndPlay("left"); }, }); /* * enemy */ tm.define("Enemy", { superClass: "tm.app.Sprite", init: function() { this.superInit("enemy", ENEMY_WIDTH*4, ENEMY_HEIGHT*4); this.speed = Math.rand(6, 12); }, update: function() { this.y += this.speed; // 画面から見えなくなったら消す if (this.y > SCREEN_HEIGHT + this.height) { this.remove(); } } }); |
まずはじめに、プレイヤークラスを追加したやり方と同じように画像を読み込みます。
1 |
"enemy": "http://rawgithub.com/omatoro/tmlib.js_tutorial_avoidgame/gh-pages/rsc/[Monster]Dragon_B_pochi.png", |
MainSceneクラスでは、モンスターのクラスであるEnemyクラスを複数入れるグループを追加します。
1 2 |
// enemy this.enemyGroup = tm.app.CanvasElement().addChildTo(this); |
モンスターは複数作るので、グループ用の変数enemyGroupを作っておきます。モンスターは時間経過にともなって生成していくので、スコアの計算と併用するtimer変数も用意しておきましょう。
1 2 |
// スコア用カウントアップ this.timer = 0; |
そして、ゲームの処理が行われるたびに処理されるupdate関数を作ります。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
update: function (app) { // カウントアップを行う ++this.timer; // 敵の生成(難易度をどんどん上げる) if (this.timer % 30 === 0) { for (var i = 0, n = (this.timer / 300); i < n; ++i) { var enemy = Enemy().addChildTo(this.enemyGroup); enemy.x = Math.rand(0, SCREEN_WIDTH); enemy.y = 0 - enemy.height; } } }, |
時間を計るtimerのカウントアップと、モンスターの生成処理を行なっています。モンスターを作るタイミングになればEnemyクラスを使ってモンスターを生成します。生成したモンスターは、モンスターのグループに入れることで画面に描画されるようになります。複数のオブジェクトを管理したいときは、このようにグループを使うと便利です。
次はEnemyクラスです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
/* * enemy */ tm.define("Enemy", { superClass: "tm.app.Sprite", init: function() { this.superInit("enemy", ENEMY_WIDTH*4, ENEMY_HEIGHT*4); this.speed = Math.rand(6, 12); }, update: function() { this.y += this.speed; // 画面から見えなくなったら消す if (this.y > SCREEN_HEIGHT + this.height) { this.remove(); } } }); |
init関数は、クラスを使うときに最初に処理される関数でした。なので、init関数内でモンスターが落ちてくるスピードをランダムに決定しています。
1 |
this.speed = Math.rand(6, 12); |
このランダムに決定したスピードを使って下へ移動する処理を作ります。移動処理もゲーム処理が実行されるたびに処理されるupdate関数に書きます。
1 2 3 4 5 6 7 8 |
update: function() { this.y += this.speed; // 画面から見えなくなったら消す if (this.y > SCREEN_HEIGHT + this.height) { this.remove(); } } |
スピードの分だけY座標を足していけば、モンスターが落ちてくるように動かすことができますね。なお、画面から見えなくなったらモンスターを消す処理も入れています。
これだけでも、何となくゲームの形になってきました。次はプレイヤーを動かせるようにしていきます。
プレイヤーを動かせるようにしよう
サンプル
コードの解説
タッチするたびに、プレイヤーの移動方向が逆になるようにします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 |
/** * ゲーム用定数作成 */ var SCREEN_WIDTH = 960; var SCREEN_HEIGHT = 640; var RESULT_PARAM = { score: 256, msg: "【避けゲー制作チュートリアル】", hashtags: ["omatoro", "tmlibチュートリアル"], url: "http://omatoro.github.io/tmlib.js_tutorial_avoidgame/", width: SCREEN_WIDTH, height: SCREEN_HEIGHT, related: "tmlib.js Tutorial testcording", }; var UI_DATA = { main: { // MainScene用ラベル children: [{ type: "Label", name: "timeLabel", x: 200, y: 120, width: SCREEN_WIDTH, fillStyle: "white", // text: "残り時間を表示する", text: " ", fontSize: 40, align: "left" }] } }; var PLAYER_WIDTH = 20; var PLAYER_HEIGHT = 16; var PLAYER_GROUND_LIMIT_LEFT = PLAYER_WIDTH/2; var PLAYER_GROUND_LIMIT_RIGHT = SCREEN_WIDTH - PLAYER_WIDTH/2; var ENEMY_WIDTH = 38; var ENEMY_HEIGHT = 30; /** * リソースの読み込み */ var ASSETS = { "player": "http://rawgithub.com/omatoro/tmlib.js_tutorial_avoidgame/gh-pages/rsc/[Animal]Chicken.png", "playerSS": "http://rawgithub.com/omatoro/tmlib.js_tutorial_avoidgame/gh-pages/rsc/playerSS.tmss", "enemy": "http://rawgithub.com/omatoro/tmlib.js_tutorial_avoidgame/gh-pages/rsc/[Monster]Dragon_B_pochi.png", "backMap": "http://rawgithub.com/omatoro/tmlib.js_tutorial_avoidgame/gh-pages/rsc/map.png", }; /** * ゲーム起動処理 */ tm.main(function() { // アプリケーション作成 var app = tm.app.CanvasApp("#world"); app.resize(SCREEN_WIDTH, SCREEN_HEIGHT); // 画面サイズに合わせる app.fitWindow(); // リサイズ対応 app.background = "rgb(0, 0, 0)"; // 背景色をセット var loadingScene = tm.app.LoadingScene({ assets: ASSETS, nextScene: TitleScene, width: SCREEN_WIDTH, height: SCREEN_HEIGHT, }); // シーンの切り替え app.replaceScene(loadingScene); // tmlibの実行 app.run(); }); /** * TitleScene */ tm.define("TitleScene", { superClass : "tm.app.TitleScene", init : function() { this.superInit({ title : "避けゲー制作チュートリアル", width : SCREEN_WIDTH, height : SCREEN_HEIGHT }); // 画面(シーンの描画箇所)をタッチした時の動作 this.addEventListener("pointingend", function(e) { // シーンの遷移 e.app.replaceScene(MainScene()); }); }, }); /** * MainScene */ tm.define("MainScene", { superClass : "tm.app.Scene", init : function() { this.superInit(); // Map this.map = tm.app.Sprite("backMap", SCREEN_WIDTH, SCREEN_HEIGHT).addChildTo(this); this.map.position.set(SCREEN_WIDTH/2, SCREEN_HEIGHT/2); // Player this.player = Player().addChildTo(this); this.player.position.set(150, 600); // enemy this.enemyGroup = tm.app.CanvasElement().addChildTo(this); // スコア用カウントアップ this.timer = 0; // ラベル表示 this.fromJSON(UI_DATA.main); // // 画面(シーンの描画箇所)をタッチした時の動作 // this.addEventListener("pointingend", function(e) { // // シーンの遷移 // e.app.replaceScene(EndScene()); // }); }, update: function (app) { // カウントアップを行う ++this.timer; // 敵の生成(難易度をどんどん上げる) if (this.timer % 30 === 0) { for (var i = 0, n = (this.timer / 300); i < n; ++i) { var enemy = Enemy().addChildTo(this.enemyGroup); enemy.x = Math.rand(0, SCREEN_WIDTH); enemy.y = 0 - enemy.height; } } }, }); /** * EndScene */ tm.define("EndScene", { superClass : "tm.app.ResultScene", init : function() { this.superInit(RESULT_PARAM); }, // Backボタンを押したらTitleSceneに戻る onnextscene: function (e) { e.target.app.replaceScene(TitleScene()); }, }); /* * player */ tm.define("Player", { superClass: "tm.app.AnimationSprite", init: function () { this.superInit("playerSS", PLAYER_WIDTH*4, PLAYER_HEIGHT*4); // this.gotoAndPlay("left"); // 移動の方向を保持 this.direct = "left"; }, moveLimit: function () { // 画面からはみ出ないようにする if (this.x < PLAYER_GROUND_LIMIT_LEFT) { this.x = PLAYER_GROUND_LIMIT_LEFT; } if (this.x > PLAYER_GROUND_LIMIT_RIGHT) { this.x = PLAYER_GROUND_LIMIT_RIGHT; } }, clickLeft: function () { this.gotoAndPlay("left"); this.x -= 4; }, clickRight: function () { this.gotoAndPlay("right"); this.x += 4; }, update: function (app) { // 移動処理 if (app.pointing.getPointingStart()) { this.direct = (this.direct === "left") ? "right" : "left"; } switch (this.direct) { case "left": this.clickLeft(); break; case "right": this.clickRight(); break; } // 移動の限界 this.moveLimit(); }, }); /* * enemy */ tm.define("Enemy", { superClass: "tm.app.Sprite", init: function() { this.superInit("enemy", ENEMY_WIDTH*4, ENEMY_HEIGHT*4); this.speed = Math.rand(6, 12); }, update: function() { this.y += this.speed; // 画面から見えなくなったら消す if (this.y > SCREEN_HEIGHT + this.height) { this.remove(); } } }); |
まずは、MainSceneをタッチするとEndSceneに移動する処理を消しておきます。
1 2 3 4 5 |
// // 画面(シーンの描画箇所)をタッチした時の動作 // this.addEventListener("pointingend", function(e) { // // シーンの遷移 // e.app.replaceScene(EndScene()); // }); |
次はプレイヤーの処理です。プレイヤーが最初に移動する方向をinit関数で決めておきます。
1 2 |
// 移動の方向を保持 this.direct = "left"; |
そして、update関数で移動処理を作っていきます。タッチを認識したら移動する方向を変えるようにします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
update: function (app) { // 移動処理 if (app.pointing.getPointingStart()) { this.direct = (this.direct === "left") ? "right" : "left"; } switch (this.direct) { case "left": this.clickLeft(); break; case "right": this.clickRight(); break; } // 移動の限界 this.moveLimit(); }, |
app.pointing.getPointingStart()関数を呼び出すことで、タッチされたかどうかが分かります。タッチされたらdirect変数の中のデータ、”left”と”right”を入れ替えましょう。そして、現在どっちを向いているのかをswitchで判定したら、実際にプレイヤーを動かしていきます。
1 2 3 4 5 6 7 8 9 |
clickLeft: function () { this.gotoAndPlay("left"); this.x -= 4; }, clickRight: function () { this.gotoAndPlay("right"); this.x += 4; }, |
“left”だったら左方向に移動+アニメーション、”right”だったら右方向に移動+アニメーション、という具合ですね。そして最後に、画面外に出られないようにする処理を呼び出しています。
1 2 3 4 5 6 7 8 9 |
moveLimit: function () { // 画面からはみ出ないようにする if (this.x < GROUND_LIMIT_LEFT) { this.x = GROUND_LIMIT_LEFT; } if (this.x > GROUND_LIMIT_RIGHT) { this.x = GROUND_LIMIT_RIGHT; } }, |
画面外に出てしまうような移動を行なっていたら、画面に収まる位置に戻すようにしています。これでプレイヤーが画面外に出ることはできません。
モンスターと接触したらゲームオーバーにしよう
サンプル
コードの解説
忘れてはならないのがプレイヤーとモンスターの当たり判定です。モンスターと接触したら、即ゲームオーバーにします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 |
/** * ゲーム用定数作成 */ var SCREEN_WIDTH = 960; var SCREEN_HEIGHT = 640; var RESULT_PARAM = { score: 256, msg: "【避けゲー制作チュートリアル】", hashtags: ["omatoro", "tmlibチュートリアル"], url: "http://omatoro.github.io/tmlib.js_tutorial_avoidgame/", width: SCREEN_WIDTH, height: SCREEN_HEIGHT, related: "tmlib.js Tutorial testcording", }; var UI_DATA = { main: { // MainScene用ラベル children: [{ type: "Label", name: "timeLabel", x: 200, y: 120, width: SCREEN_WIDTH, fillStyle: "white", // text: "残り時間を表示する", text: " ", fontSize: 40, align: "left" }] } }; var PLAYER_WIDTH = 20; var PLAYER_HEIGHT = 16; var PLAYER_GROUND_LIMIT_LEFT = PLAYER_WIDTH/2; var PLAYER_GROUND_LIMIT_RIGHT = SCREEN_WIDTH - PLAYER_WIDTH/2; var ENEMY_WIDTH = 38; var ENEMY_HEIGHT = 30; /** * リソースの読み込み */ var ASSETS = { "player": "http://rawgithub.com/omatoro/tmlib.js_tutorial_avoidgame/gh-pages/rsc/[Animal]Chicken.png", "playerSS": "http://rawgithub.com/omatoro/tmlib.js_tutorial_avoidgame/gh-pages/rsc/playerSS.tmss", "enemy": "http://rawgithub.com/omatoro/tmlib.js_tutorial_avoidgame/gh-pages/rsc/[Monster]Dragon_B_pochi.png", "backMap": "http://rawgithub.com/omatoro/tmlib.js_tutorial_avoidgame/gh-pages/rsc/map.png", }; /** * ゲーム起動処理 */ tm.main(function() { // アプリケーション作成 var app = tm.app.CanvasApp("#world"); app.resize(SCREEN_WIDTH, SCREEN_HEIGHT); // 画面サイズに合わせる app.fitWindow(); // リサイズ対応 app.background = "rgb(0, 0, 0)"; // 背景色をセット var loadingScene = tm.app.LoadingScene({ assets: ASSETS, nextScene: TitleScene, width: SCREEN_WIDTH, height: SCREEN_HEIGHT, }); // シーンの切り替え app.replaceScene(loadingScene); // tmlibの実行 app.run(); }); /** * TitleScene */ tm.define("TitleScene", { superClass : "tm.app.TitleScene", init : function() { this.superInit({ title : "避けゲー制作チュートリアル", width : SCREEN_WIDTH, height : SCREEN_HEIGHT }); // 画面(シーンの描画箇所)をタッチした時の動作 this.addEventListener("pointingend", function(e) { // シーンの遷移 e.app.replaceScene(MainScene()); }); }, }); /** * MainScene */ tm.define("MainScene", { superClass : "tm.app.Scene", init : function() { this.superInit(); // Map this.map = tm.app.Sprite("backMap", SCREEN_WIDTH, SCREEN_HEIGHT).addChildTo(this); this.map.position.set(SCREEN_WIDTH/2, SCREEN_HEIGHT/2); // Player this.player = Player().addChildTo(this); this.player.position.set(150, 600); // enemy this.enemyGroup = tm.app.CanvasElement().addChildTo(this); // スコア用カウントアップ this.timer = 0; // ラベル表示 this.fromJSON(UI_DATA.main); }, update: function (app) { // カウントアップを行う ++this.timer; // 制限時間を表示する this.timeLabel.text = "生き残ってる時間 : " + ((this.timer / 30) |0); // 敵の生成(難易度をどんどん上げる) if (this.timer % 30 === 0) { for (var i = 0, n = (this.timer / 300); i < n; ++i) { var enemy = Enemy().addChildTo(this.enemyGroup); enemy.x = Math.rand(0, SCREEN_WIDTH); enemy.y = 0 - enemy.height; } } var self = this; var ec = this.enemyGroup.children; ec.each(function(enemy) { if (self.player.isHitElement(enemy)) { app.replaceScene(EndScene(self.timer)) }; }); }, }); /** * EndScene */ tm.define("EndScene", { superClass : "tm.app.ResultScene", init : function(time) { // スコア計算 RESULT_PARAM.score = (Math.floor(time*100/30)/100) + "秒生き残ることができました。"; // スコア this.superInit(RESULT_PARAM); }, // Backボタンを押したらTitleSceneに戻る onnextscene: function (e) { e.target.app.replaceScene(TitleScene()); }, }); /* * player */ tm.define("Player", { superClass: "tm.app.AnimationSprite", init: function () { this.superInit("playerSS", PLAYER_WIDTH*4, PLAYER_HEIGHT*4); // this.gotoAndPlay("left"); // 移動の方向を保持 this.direct = "left"; }, moveLimit: function () { // 画面からはみ出ないようにする if (this.x < PLAYER_GROUND_LIMIT_LEFT) { this.x = PLAYER_GROUND_LIMIT_LEFT; } if (this.x > PLAYER_GROUND_LIMIT_RIGHT) { this.x = PLAYER_GROUND_LIMIT_RIGHT; } }, clickLeft: function () { this.gotoAndPlay("left"); this.x -= 4; }, clickRight: function () { this.gotoAndPlay("right"); this.x += 4; }, update: function (app) { // 移動処理 if (app.pointing.getPointingStart()) { this.direct = (this.direct === "left") ? "right" : "left"; } switch (this.direct) { case "left": this.clickLeft(); break; case "right": this.clickRight(); break; } // 移動の限界 this.moveLimit(); }, }); /* * enemy */ tm.define("Enemy", { superClass: "tm.app.Sprite", init: function() { this.superInit("enemy", ENEMY_WIDTH*4, ENEMY_HEIGHT*4); this.speed = Math.rand(6, 12); }, update: function() { this.y += this.speed; // 画面から見えなくなったら消す if (this.y > SCREEN_HEIGHT + this.height) { this.remove(); } } }); |
ついでなので、生き残っている時間を表示するようにしておきます。
1 2 |
// 制限時間を表示する this.timeLabel.text = "生き残ってる時間 : " + ((this.timer / 30) |0); |
表示している文字を途中で変更したいときは、上記のように書きます。次はモンスターとプレイヤーの当たり判定です。
1 2 3 4 5 6 7 |
var self = this; var ec = this.enemyGroup.children; ec.each(function(enemy) { if (self.player.isHitElement(enemy)) { app.replaceScene(EndScene(self.timer)) }; }); |
each関数を使うことで、enemyGroupに登録しているモンスター全てに対して行う処理を書くことができます。each関数に当たり判定の処理を渡すことで、簡単に総当りの当たり判定が作れますね。当たり判定の処理はtmlib.jsが用意していますので、たった一行で書くことができます。
1 |
self.player.isHitElement(enemy) |
敵とヒットしてゲームオーバーになったら生き残った時間をそのまま使ってスコアにしたいですね。そこで、EndSceneに生き残った時間を渡す処理を書きます。
1 |
app.replaceScene(EndScene(self.timer)) |
生き残った時間を受け取る処理は次のように書きます。
1 2 3 4 |
tm.define("EndScene", { superClass : "tm.app.ResultScene", init : function(time) { // ここ |
このように、init関数は引数としてデータを受け取ることができます。受け取った値を使ってスコアを表示しましょう。
1 2 |
// スコア計算 RESULT_PARAM.score = (Math.floor(time*100/30)/100) + "秒生き残ることができました。"; |
これで生き残った時間を表示できるようになりました。ゲームとしてはこれで完成ですがちょっとした味付けを次項でやってみます。
2013/6/17 - 19:27 - Category / game, JavaScript, tmlib.js, Tutorial
BGMを再生しよう
サンプル
コードの解説
tmlib.jsは音の再生もサポートしているので、BGMを鳴らしてみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 |
/** * ゲーム用定数作成 */ var SCREEN_WIDTH = 960; var SCREEN_HEIGHT = 640; var RESULT_PARAM = { score: 256, msg: "【避けゲー制作チュートリアル】", hashtags: ["omatoro", "tmlibチュートリアル"], url: "http://omatoro.github.io/tmlib.js_tutorial_avoidgame/", width: SCREEN_WIDTH, height: SCREEN_HEIGHT, related: "tmlib.js Tutorial testcording", }; var UI_DATA = { main: { // MainScene用ラベル children: [{ type: "Label", name: "timeLabel", x: 200, y: 120, width: SCREEN_WIDTH, fillStyle: "white", // text: "残り時間を表示する", text: " ", fontSize: 40, align: "left" }] } }; var PLAYER_WIDTH = 20; var PLAYER_HEIGHT = 16; var PLAYER_GROUND_LIMIT_LEFT = PLAYER_WIDTH/2; var PLAYER_GROUND_LIMIT_RIGHT = SCREEN_WIDTH - PLAYER_WIDTH/2; var ENEMY_WIDTH = 38; var ENEMY_HEIGHT = 30; /** * リソースの読み込み */ var ASSETS = { "player": "http://rawgithub.com/omatoro/tmlib.js_tutorial_avoidgame/gh-pages/rsc/[Animal]Chicken.png", "playerSS": "http://rawgithub.com/omatoro/tmlib.js_tutorial_avoidgame/gh-pages/rsc/playerSS.tmss", "enemy": "http://rawgithub.com/omatoro/tmlib.js_tutorial_avoidgame/gh-pages/rsc/[Monster]Dragon_B_pochi.png", "bgm": "http://rawgithub.com/omatoro/tmlib.js_tutorial_avoidgame/gh-pages/rsc/Comical01_Koya_short2.mp3", "backMap": "http://rawgithub.com/omatoro/tmlib.js_tutorial_avoidgame/gh-pages/rsc/map.png", }; /** * ゲーム起動処理 */ tm.main(function() { // アプリケーション作成 var app = tm.app.CanvasApp("#world"); app.resize(SCREEN_WIDTH, SCREEN_HEIGHT); // 画面サイズに合わせる app.fitWindow(); // リサイズ対応 app.background = "rgb(0, 0, 0)"; // 背景色をセット var loadingScene = tm.app.LoadingScene({ assets: ASSETS, nextScene: TitleScene, width: SCREEN_WIDTH, height: SCREEN_HEIGHT, }); // シーンの切り替え app.replaceScene(loadingScene); // tmlibの実行 app.run(); }); /** * TitleScene */ tm.define("TitleScene", { superClass : "tm.app.TitleScene", init : function() { this.superInit({ title : "避けゲー制作チュートリアル", width : SCREEN_WIDTH, height : SCREEN_HEIGHT }); // 画面(シーンの描画箇所)をタッチした時の動作 this.addEventListener("pointingend", function(e) { // シーンの遷移 e.app.replaceScene(MainScene()); }); }, }); /** * MainScene */ tm.define("MainScene", { superClass : "tm.app.Scene", init : function() { this.superInit(); // BGM再生 this.bgm = tm.asset.AssetManager.get("bgm"); this.bgm.setVolume(1.0).setLoop(true).play(); // Map this.map = tm.app.Sprite("backMap", SCREEN_WIDTH, SCREEN_HEIGHT).addChildTo(this); this.map.position.set(SCREEN_WIDTH/2, SCREEN_HEIGHT/2); // Player this.player = Player().addChildTo(this); this.player.position.set(150, 600); // enemy this.enemyGroup = tm.app.CanvasElement().addChildTo(this); // スコア用カウントアップ this.timer = 0; // ラベル表示 this.fromJSON(UI_DATA.main); }, update: function (app) { // カウントアップを行う ++this.timer; // 制限時間を表示する this.timeLabel.text = "生き残ってる時間 : " + ((this.timer / 30) |0); // 敵の生成(難易度をどんどん上げる) if (this.timer % 30 === 0) { for (var i = 0, n = (this.timer / 300); i < n; ++i) { var enemy = Enemy().addChildTo(this.enemyGroup); enemy.x = Math.rand(0, SCREEN_WIDTH); enemy.y = 0 - enemy.height; } } var self = this; var ec = this.enemyGroup.children; ec.each(function(enemy) { if (self.player.isHitElement(enemy)) { self.bgm.stop(); app.replaceScene(EndScene(self.timer)) }; }); }, }); /** * EndScene */ tm.define("EndScene", { superClass : "tm.app.ResultScene", init : function(time) { // スコア計算 RESULT_PARAM.score = (Math.floor(time*100/30)/100) + "秒生き残ることができました。"; // スコア this.superInit(RESULT_PARAM); }, // Backボタンを押したらTitleSceneに戻る onnextscene: function (e) { e.target.app.replaceScene(TitleScene()); }, }); /* * player */ tm.define("Player", { superClass: "tm.app.AnimationSprite", init: function () { this.superInit("playerSS", PLAYER_WIDTH*4, PLAYER_HEIGHT*4); // this.gotoAndPlay("left"); // 移動の方向を保持 this.direct = "left"; }, moveLimit: function () { // 画面からはみ出ないようにする if (this.x < PLAYER_GROUND_LIMIT_LEFT) { this.x = PLAYER_GROUND_LIMIT_LEFT; } if (this.x > PLAYER_GROUND_LIMIT_RIGHT) { this.x = PLAYER_GROUND_LIMIT_RIGHT; } }, clickLeft: function () { this.gotoAndPlay("left"); this.x -= 4; }, clickRight: function () { this.gotoAndPlay("right"); this.x += 4; }, update: function (app) { // 移動処理 if (app.pointing.getPointingStart()) { this.direct = (this.direct === "left") ? "right" : "left"; } switch (this.direct) { case "left": this.clickLeft(); break; case "right": this.clickRight(); break; } // 移動の限界 this.moveLimit(); }, }); /* * enemy */ tm.define("Enemy", { superClass: "tm.app.Sprite", init: function() { this.superInit("enemy", ENEMY_WIDTH*4, ENEMY_HEIGHT*4); this.speed = Math.rand(6, 12); }, update: function() { this.y += this.speed; // 画面から見えなくなったら消す if (this.y > SCREEN_HEIGHT + this.height) { this.remove(); } } }); |
画像と同じように音楽もロードすることができます。MainSceneが開始するときにBGMがなり始めるようにします。
1 2 3 |
// BGM再生 this.bgm = tm.asset.AssetManager.get("bgm"); this.bgm.setVolume(1.0).setLoop(true).play(); |
ボリュームやBGMをループして再生する設定など行った後、play関数で音の再生を開始します。
このままだと、EndSceneになってもBGMが鳴り続けてしまいます。EndSceneに遷移する前にBGMをストップしておきます。
1 |
self.bgm.stop(); |
ジャイロセンサーを使って、スマートフォンを傾けても移動できるようにしよう
サンプル
コードの解説
ジャイロセンサーとは、iPhoneの傾きを検知するセンサーです。tmlib.jsでは、このセンサーをゲームに応用しやすいようにしてくれています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 |
/** * ゲーム用定数作成 */ var SCREEN_WIDTH = 960; var SCREEN_HEIGHT = 640; var RESULT_PARAM = { score: 256, msg: "【避けゲー制作チュートリアル】", hashtags: ["omatoro", "tmlibチュートリアル"], url: "http://omatoro.github.io/tmlib.js_tutorial_avoidgame/", width: SCREEN_WIDTH, height: SCREEN_HEIGHT, related: "tmlib.js Tutorial testcording", }; var UI_DATA = { main: { // MainScene用ラベル children: [{ type: "Label", name: "timeLabel", x: 200, y: 120, width: SCREEN_WIDTH, fillStyle: "white", text: " ", fontSize: 40, align: "left" }] } }; var PLAYER_WIDTH = 20; var PLAYER_HEIGHT = 16; var PLAYER_GROUND_LIMIT_LEFT = PLAYER_WIDTH/2; var PLAYER_GROUND_LIMIT_RIGHT = SCREEN_WIDTH - PLAYER_WIDTH/2; var ENEMY_WIDTH = 38; var ENEMY_HEIGHT = 30; /** * リソースの読み込み */ var ASSETS = { "player": "http://rawgithub.com/omatoro/tmlib.js_tutorial_avoidgame/gh-pages/rsc/[Animal]Chicken.png", "playerSS": "http://rawgithub.com/omatoro/tmlib.js_tutorial_avoidgame/gh-pages/rsc/playerSS.tmss", "enemy": "http://rawgithub.com/omatoro/tmlib.js_tutorial_avoidgame/gh-pages/rsc/[Monster]Dragon_B_pochi.png", "bgm": "http://rawgithub.com/omatoro/tmlib.js_tutorial_avoidgame/gh-pages/rsc/Comical01_Koya_short2.mp3", "backMap": "http://rawgithub.com/omatoro/tmlib.js_tutorial_avoidgame/gh-pages/rsc/map.png", }; /** * ゲーム起動処理 */ tm.main(function() { // アプリケーション作成 var app = tm.app.CanvasApp("#world"); app.resize(SCREEN_WIDTH, SCREEN_HEIGHT); // 画面サイズに合わせる app.fitWindow(); // リサイズ対応 app.background = "rgb(0, 0, 0)"; // 背景色をセット var loadingScene = tm.app.LoadingScene({ assets: ASSETS, nextScene: TitleScene, width: SCREEN_WIDTH, height: SCREEN_HEIGHT, }); // シーンの切り替え app.replaceScene(loadingScene); // tmlibの実行 app.run(); }); /** * TitleScene */ tm.define("TitleScene", { superClass : "tm.app.TitleScene", init : function() { this.superInit({ title : "避けゲー制作チュートリアル", width : SCREEN_WIDTH, height : SCREEN_HEIGHT }); // 画面(シーンの描画箇所)をタッチした時の動作 this.addEventListener("pointingend", function(e) { // シーンの遷移 e.app.replaceScene(MainScene()); }); }, }); /** * MainScene */ tm.define("MainScene", { superClass : "tm.app.Scene", init : function() { this.superInit(); // BGM再生 this.bgm = tm.asset.AssetManager.get("bgm"); this.bgm.setVolume(1.0).setLoop(true).play(); // Map this.map = tm.app.Sprite("backMap", SCREEN_WIDTH, SCREEN_HEIGHT).addChildTo(this); this.map.position.set(SCREEN_WIDTH/2, SCREEN_HEIGHT/2); // Player this.player = Player().addChildTo(this); this.player.position.set(150, 600); // enemy this.enemyGroup = tm.app.CanvasElement().addChildTo(this); // スコア用カウントアップ this.timer = 0; // ラベル表示 this.fromJSON(UI_DATA.main); }, update: function (app) { // カウントアップを行う ++this.timer; // 制限時間を表示する this.timeLabel.text = "生き残ってる時間 : " + ((this.timer / 30) |0); // 敵の生成(難易度をどんどん上げる) if (this.timer % 30 === 0) { for (var i = 0, n = (this.timer / 300); i < n; ++i) { var enemy = Enemy().addChildTo(this.enemyGroup); enemy.x = Math.rand(0, SCREEN_WIDTH); enemy.y = 0 - enemy.height; } } var self = this; var ec = this.enemyGroup.children; ec.each(function(enemy) { if (self.player.isHitElement(enemy)) { self.bgm.stop(); app.replaceScene(EndScene(self.timer)) }; }); }, }); /** * EndScene */ tm.define("EndScene", { superClass : "tm.app.ResultScene", init : function(time) { // スコア計算 RESULT_PARAM.score = (Math.floor(time*100/30)/100) + "秒生き残ることができました。"; // スコア this.superInit(RESULT_PARAM); }, // Backボタンを押したらTitleSceneに戻る onnextscene: function (e) { e.target.app.replaceScene(TitleScene()); }, }); /* * player */ tm.define("Player", { superClass: "tm.app.AnimationSprite", init: function () { this.superInit("playerSS", PLAYER_WIDTH*4, PLAYER_HEIGHT*4); // 移動の方向を保持 this.direct = "left"; // スマホかどうか判断 this.isMobile = tm.isMobile; // スマホだったら加速度を使うので、タッチ入力での移動を行わない this.update = (this.isMobile) ? this.updateMobile : this.updateNotMobile; }, moveLimit: function () { // 画面からはみ出ないようにする if (this.x < PLAYER_GROUND_LIMIT_LEFT) { this.x = PLAYER_GROUND_LIMIT_LEFT; } if (this.x > PLAYER_GROUND_LIMIT_RIGHT) { this.x = PLAYER_GROUND_LIMIT_RIGHT; } }, clickLeft: function () { this.gotoAndPlay("left"); this.x -= 4; }, clickRight: function () { this.gotoAndPlay("right"); this.x += 4; }, updateNotMobile: function (app) { // 移動処理 if (app.pointing.getPointingStart()) { this.direct = (this.direct === "left") ? "right" : "left"; } switch (this.direct) { case "left": this.clickLeft(); break; case "right": this.clickRight(); break; } // 移動の限界 this.moveLimit(); }, updateMobile: function (app) { // 移動処理:モバイルなら加速度センサーを利用 var gravity = app.accelerometer.gravity; this.x -= gravity.y * 1.7; // 移動方向によってアニメーション if (gravity.y > 0) { this.gotoAndPlay("left"); } else { this.gotoAndPlay("right"); } // 移動の限界 this.moveLimit(); }, }); /* * enemy */ tm.define("Enemy", { superClass: "tm.app.Sprite", init: function() { this.superInit("enemy", ENEMY_WIDTH*4, ENEMY_HEIGHT*4); this.speed = Math.rand(6, 12); }, update: function() { this.y += this.speed; // 画面から見えなくなったら消す if (this.y > SCREEN_HEIGHT + this.height) { this.remove(); } } }); |
しかし、プレイヤークラスのupdateを上記の処理にそのまま書き換えてしまうとPCではプレイヤーを操作することはできません。その対策として、PC用のupdateとスマートフォン用のupdateを別に作っておきます。そして、スマートフォンなのかPCなのかを判断してupdateを適切にセットすると、とりあえずiPhoneとPCの区別をつけることができます。
1 2 3 4 |
// スマホかどうか判断 this.isMobile = tm.isMobile; // スマホだったら加速度を使うので、タッチ入力での移動を行わない this.update = (this.isMobile) ? this.updateMobile : this.updateNotMobile; |
「正確にジャイロセンサーが付いていたら、ジャイロセンサーを使った操作に切り替える」という処理ではないので気をつけてくださいね。
これでチュートリアルは終了です。お疲れ様でした!落ちゲーのゲームも色々処理を追加しようと思えばたくさん思いつくとおもいます。このチュートリアルでゲームを作り始めてくれたりするととても嬉しいです。
2013/6/17 - 17:02 - Category / game, JavaScript, tmlib.js, Tutorial
ローディング画面を使って画像を読み込もう!
サンプル
コードの解説
タイトル画面が表示される前に、ローディングの画面を挟んで画像をロードしてみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
/** * ゲーム用定数作成 */ var SCREEN_WIDTH = 960; var SCREEN_HEIGHT = 640; var RESULT_PARAM = { score: 256, msg: "【避けゲー制作チュートリアル】", hashtags: ["omatoro", "tmlibチュートリアル"], url: "http://omatoro.github.io/tmlib.js_tutorial_avoidgame/", width: SCREEN_WIDTH, height: SCREEN_HEIGHT, related: "tmlib.js Tutorial testcording", }; var UI_DATA = { main: { // MainScene用ラベル children: [{ type: "Label", name: "timeLabel", x: 200, y: 120, width: SCREEN_WIDTH, fillStyle: "white", // text: "残り時間を表示する", text: " ", fontSize: 40, align: "left" }] } }; /** * リソースの読み込み */ var ASSETS = { "backMap": "http://rawgithub.com/omatoro/tmlib.js_tutorial_avoidgame/gh-pages/rsc/map.png", }; /** * ゲーム起動処理 */ tm.main(function() { // アプリケーション作成 var app = tm.app.CanvasApp("#world"); app.resize(SCREEN_WIDTH, SCREEN_HEIGHT); // 画面サイズに合わせる app.fitWindow(); // リサイズ対応 app.background = "rgb(0, 0, 0)"; // 背景色をセット var loadingScene = tm.app.LoadingScene({ assets: ASSETS, nextScene: TitleScene, width: SCREEN_WIDTH, height: SCREEN_HEIGHT, }); // シーンの切り替え app.replaceScene(loadingScene); // tmlibの実行 app.run(); }); /** * TitleScene */ tm.define("TitleScene", { superClass : "tm.app.TitleScene", init : function() { this.superInit({ title : "避けゲー制作チュートリアル", width : SCREEN_WIDTH, height : SCREEN_HEIGHT }); // 画面(シーンの描画箇所)をタッチした時の動作 this.addEventListener("pointingend", function(e) { // シーンの遷移 e.app.replaceScene(MainScene()); }); }, }); /** * MainScene */ tm.define("MainScene", { superClass : "tm.app.Scene", init : function() { this.superInit(); // ラベル表示 this.fromJSON(UI_DATA.main); // 画面(シーンの描画箇所)をタッチした時の動作 this.addEventListener("pointingend", function(e) { // シーンの遷移 e.app.replaceScene(EndScene()); }); }, }); /** * EndScene */ tm.define("EndScene", { superClass : "tm.app.ResultScene", init : function() { this.superInit(RESULT_PARAM); }, // Backボタンを押したらTitleSceneに戻る onnextscene: function (e) { e.target.app.replaceScene(TitleScene()); }, }); |
このように読み込みたい画像のURLを書いた変数、ASSETSを用意します。ASSETSの”backMap”とは画像の別名です。画像に別名をつけることで、URLを変更しても他のプログラム箇所は書き換えなくてよくなっています。
画像を表示しよう
サンプル
コードの解説
画像を表示してみます。マップの画像を表示させてみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
/** * ゲーム用定数作成 */ var SCREEN_WIDTH = 960; var SCREEN_HEIGHT = 640; var RESULT_PARAM = { score: 256, msg: "【避けゲー制作チュートリアル】", hashtags: ["omatoro", "tmlibチュートリアル"], url: "http://omatoro.github.io/tmlib.js_tutorial_avoidgame/", width: SCREEN_WIDTH, height: SCREEN_HEIGHT, related: "tmlib.js Tutorial testcording", }; var UI_DATA = { main: { // MainScene用ラベル children: [{ type: "Label", name: "timeLabel", x: 200, y: 120, width: SCREEN_WIDTH, fillStyle: "white", // text: "残り時間を表示する", text: " ", fontSize: 40, align: "left" }] } }; /** * リソースの読み込み */ var ASSETS = { "backMap": "http://rawgithub.com/omatoro/tmlib.js_tutorial_avoidgame/gh-pages/rsc/map.png", }; /** * ゲーム起動処理 */ tm.main(function() { // アプリケーション作成 var app = tm.app.CanvasApp("#world"); app.resize(SCREEN_WIDTH, SCREEN_HEIGHT); // 画面サイズに合わせる app.fitWindow(); // リサイズ対応 app.background = "rgb(0, 0, 0)"; // 背景色をセット var loadingScene = tm.app.LoadingScene({ assets: ASSETS, nextScene: TitleScene, width: SCREEN_WIDTH, height: SCREEN_HEIGHT, }); // シーンの切り替え app.replaceScene(loadingScene); // tmlibの実行 app.run(); }); /** * TitleScene */ tm.define("TitleScene", { superClass : "tm.app.TitleScene", init : function() { this.superInit({ title : "避けゲー制作チュートリアル", width : SCREEN_WIDTH, height : SCREEN_HEIGHT }); // 画面(シーンの描画箇所)をタッチした時の動作 this.addEventListener("pointingend", function(e) { // シーンの遷移 e.app.replaceScene(MainScene()); }); }, }); /** * MainScene */ tm.define("MainScene", { superClass : "tm.app.Scene", init : function() { this.superInit(); // Map this.map = tm.app.Sprite("backMap", SCREEN_WIDTH, SCREEN_HEIGHT).addChildTo(this); this.map.position.set(SCREEN_WIDTH/2, SCREEN_HEIGHT/2); // ラベル表示 this.fromJSON(UI_DATA.main); // 画面(シーンの描画箇所)をタッチした時の動作 this.addEventListener("pointingend", function(e) { // シーンの遷移 e.app.replaceScene(EndScene()); }); }, }); /** * EndScene */ tm.define("EndScene", { superClass : "tm.app.ResultScene", init : function() { this.superInit(RESULT_PARAM); }, // Backボタンを押したらTitleSceneに戻る onnextscene: function (e) { e.target.app.replaceScene(TitleScene()); }, }); |
これで画像を表示することができました。次はアニメーションをさせてみましょう。
スプライトアニメーションをさせてみよう
サンプル
コードの解説
プレイヤークラスを用意しちゃいましょう。初めて自分用のクラスを作りますが、作り方は既存のクラスの拡張と同じです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
/** * ゲーム用定数作成 */ var SCREEN_WIDTH = 960; var SCREEN_HEIGHT = 640; var RESULT_PARAM = { score: 256, msg: "【避けゲー制作チュートリアル】", hashtags: ["omatoro", "tmlibチュートリアル"], url: "http://omatoro.github.io/tmlib.js_tutorial_avoidgame/", width: SCREEN_WIDTH, height: SCREEN_HEIGHT, related: "tmlib.js Tutorial testcording", }; var UI_DATA = { main: { // MainScene用ラベル children: [{ type: "Label", name: "timeLabel", x: 200, y: 120, width: SCREEN_WIDTH, fillStyle: "white", // text: "残り時間を表示する", text: " ", fontSize: 40, align: "left" }] } }; var PLAYER_WIDTH = 20; var PLAYER_HEIGHT = 16; /** * リソースの読み込み */ var ASSETS = { "player": "http://rawgithub.com/omatoro/tmlib.js_tutorial_avoidgame/gh-pages/rsc/[Animal]Chicken.png", "playerSS": "http://rawgithub.com/omatoro/tmlib.js_tutorial_avoidgame/gh-pages/rsc/playerSS.tmss", "backMap": "http://rawgithub.com/omatoro/tmlib.js_tutorial_avoidgame/gh-pages/rsc/map.png", }; /** * ゲーム起動処理 */ tm.main(function() { // アプリケーション作成 var app = tm.app.CanvasApp("#world"); app.resize(SCREEN_WIDTH, SCREEN_HEIGHT); // 画面サイズに合わせる app.fitWindow(); // リサイズ対応 app.background = "rgb(0, 0, 0)"; // 背景色をセット var loadingScene = tm.app.LoadingScene({ assets: ASSETS, nextScene: TitleScene, width: SCREEN_WIDTH, height: SCREEN_HEIGHT, }); // シーンの切り替え app.replaceScene(loadingScene); // tmlibの実行 app.run(); }); /** * TitleScene */ tm.define("TitleScene", { superClass : "tm.app.TitleScene", init : function() { this.superInit({ title : "避けゲー制作チュートリアル", width : SCREEN_WIDTH, height : SCREEN_HEIGHT }); // 画面(シーンの描画箇所)をタッチした時の動作 this.addEventListener("pointingend", function(e) { // シーンの遷移 e.app.replaceScene(MainScene()); }); }, }); /** * MainScene */ tm.define("MainScene", { superClass : "tm.app.Scene", init : function() { this.superInit(); // Map this.map = tm.app.Sprite("backMap", SCREEN_WIDTH, SCREEN_HEIGHT).addChildTo(this); this.map.position.set(SCREEN_WIDTH/2, SCREEN_HEIGHT/2); // Player this.player = Player().addChildTo(this); this.player.position.set(150, 600); // ラベル表示 this.fromJSON(UI_DATA.main); // 画面(シーンの描画箇所)をタッチした時の動作 this.addEventListener("pointingend", function(e) { // シーンの遷移 e.app.replaceScene(EndScene()); }); }, }); /** * EndScene */ tm.define("EndScene", { superClass : "tm.app.ResultScene", init : function() { this.superInit(RESULT_PARAM); }, // Backボタンを押したらTitleSceneに戻る onnextscene: function (e) { e.target.app.replaceScene(TitleScene()); }, }); /* * player */ tm.define("Player", { superClass: "tm.app.AnimationSprite", init: function () { this.superInit("playerSS", PLAYER_WIDTH*4, PLAYER_HEIGHT*4); this.gotoAndPlay("left"); }, }); |
画像の追加や、MainSceneへの処理の追加はMapクラスと同じですね。今回違うのは、プレイヤークラスの作り方です。プレイヤークラスをちょっと詳しくみてみましょう。
1 2 |
var PLAYER_WIDTH = 20; var PLAYER_HEIGHT = 16; |
1 2 3 4 5 6 7 8 9 10 11 |
/* * player */ tm.define("Player", { superClass: "tm.app.AnimationSprite", init: function () { this.superInit("playerSS", PLAYER_WIDTH*4, PLAYER_HEIGHT*4); this.gotoAndPlay("left"); }, }); |
プレイヤーのサイズを変数として作っています。サイズは幅20pixel x 高さ16pixelです。画像全体の大きさではないことに注意してください。キャラクターのサイズです。画像を見ると分かりますが、一つ一つの動きごとに絵が書かれているので画像を見たほうが分かりやすいでしょうか。
流用するクラスは”tm.app.AnimationSprite”となっています。これはスプライトアニメーションを簡単に行えるようにtmlib.jsが用意しているクラスです。
superInitにアニメーションの仕方を記述しているファイルである”playerSS”を渡すことで、アニメーションができるようになります。
1 |
this.superInit("playerSS", PLAYER_WIDTH*4, PLAYER_HEIGHT*4); |
サイズを4倍にしているのは、プレイヤーの表示を大きくするためです。元の画像が小さいので。
これでアニメーションの準備は終わりです。次のコードでアニメーションが開始されます。
1 |
this.gotoAndPlay("left"); |
“left”を指定することで、左方向に移動しているようなアニメーションが再生されます。実際にindex.htmlをブラウザで開くとアニメーションされますのでチェックしてみてください。
次は、アニメーションの仕方を指定する方法です。アニメーションの仕方は”playerSS”ファイルに書かれています。中身はこんな感じです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
{ "image": "player", "frame": { "width":20, "height":16, "count": 24 }, "animations": { "down": { "frames": [1, 2, 1, 0], "next": "down", "frequency": 5 }, "up": { "frames": [19, 20, 19, 18], "next": "up", "frequency": 5 }, "left": { "frames": [7, 8, 7, 6], "next": "left", "frequency": 5 }, "right": { "frames": [13, 14, 13, 12], "next": "right", "frequency": 5 }, "upleft": { "frames": [16, 17, 16, 15], "next": "upleft", "frequency": 5 }, "upright": { "frames": [22, 23, 22, 21], "next": "upright", "frequency": 5 }, "downleft": { "frames": [4, 5, 4, 3], "next": "downleft", "frequency": 5 }, "downright": { "frames": [10, 11, 10, 9], "next": "downright", "frequency": 5 } } } |
jsonファイルの形式になっています。画像名や画像のサイズ、アニメーション方法などをまとめて書いています。