├── bosu.mp3 ├── hakai.mp3 ├── mogura.png ├── index.html ├── main.js └── enchant.js /bosu.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ixkaito/moguratataki/master/bosu.mp3 -------------------------------------------------------------------------------- /hakai.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ixkaito/moguratataki/master/hakai.mp3 -------------------------------------------------------------------------------- /mogura.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ixkaito/moguratataki/master/mogura.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | enchant(); 2 | //ドロイド君の出現数 3 | maxDroid = 30;    4 | 5 | 6 | 7 | //穴クラスの定義 8 | Pit = Class.create(Sprite,{ //Spriteクラスを継承する 9 | initialize:function(x,y){ 10 | enchant.Sprite.call(this,48,48); //Spriteクラスのコンストラクタ呼び出し 11 | this.image = game.assets['mogura.png']; 12 | this.x = x; 13 | this.y = y; 14 | this.addEventListener('enterframe',this.tick); //イベントリスナーを定義 15 | this.addEventListener('touchstart',this.hit); //叩いた場合のイベントリスナーを定義 16 | this.mode = 2; //ドロイド君の出現モードを待つ、からに設定 17 | this.nextMode = 0; 18 | this.waitFor = game.frame+Math.floor(Math.random()*100); 19 | }, 20 | tick:function(){ //ドロイド君が出るアニメーションを繰り返す 21 | if(game.frame%2!=0)return; //4フレームごとに実行する 22 | switch(this.mode){ 23 | case 0: //穴からドロイド君がでてくる 24 | this.frame++; 25 | if(this.frame>=4){ 26 | this.mode=2; //出切ったら、待つモードへ 27 | this.nextMode=1;//待った後に遷移するモードは1(隠れる) 28 | this.waitFor = game.frame+Math.floor(Math.random()*30); 29 | } 30 | break; 31 | case 1://ドロイド君が穴に隠れる 32 | this.frame--; 33 | if(this.frame<=0){ 34 | this.mode=2;  //出切ったら、待つモードへ 35 | this.nextMode=0;//待った後に遷移するモードは0(出現) 36 | this.waitFor = game.frame+Math.floor(Math.random()*200); 37 | 38 | //ドロイド君の最大数を減らす 39 | maxDroid--; 40 | //もしこれ以上ドロイド君は出現しないなら、穴を塞ぐ 41 | if(maxDroid<=0)this.mode=3; 42 | } 43 | break; 44 | case 2://待つ 45 | if(game.frame>this.waitFor){ 46 | this.mode = this.nextMode; 47 | } 48 | break; 49 | case 3://なにもしない(この穴からもうドロイド君は出ない) 50 | break; 51 | } 52 | }, 53 | hit:function(){ //ドロイド君を殴る 54 | if(this.frame==5)return;//既に殴れた状態だったらなにもしない 55 | if(this.frame>=2){ // ドロイド君が半分以上出ていた場合 56 | this.frame=5; //殴れたドロイド君 57 | this.mode=2; //待ちモードに入る 58 | this.nextMode=1; 59 | this.waitFor = game.frame+10; //待つフレーム数は10で一定 60 | scoreLabel.add(1); //スコアに追加 61 | var sound1 = game.assets['hakai.mp3'].clone(); 62 | sound1.play(); 63 | } 64 | } 65 | }); 66 | //ScoreLabelクラスの定義 67 | ScoreLabel = Class.create(Label,{ //Labelクラスを継承する 68 | initialize:function(x,y){ 69 | enchant.Label.call(this,"SCORE:0"); //Labelクラスのコンストラクタ呼び出し 70 | this.x=x; 71 | this.y=y; 72 | this.score = 0; 73 | }, 74 | add:function(pts){ //スコアを加算 75 | this.score+=pts; 76 | this.text="SCORE:"+this.score; //表示を修正 77 | } 78 | }); 79 | 80 | window.onload = function(){//初期化 81 | 82 | setTimeout(function() { 83 | 84 | enchant.Sound.enabledInMobileSafari = true; 85 | game = new Game(320, 320); 86 | game.preload('mogura.png');//ドロイド君画像を読み込み 87 | game.preload('hakai.mp3'); 88 | game.preload('bosu.mp3'); 89 | game.onload = function(){ 90 | 91 | var sound2 = game.assets['bosu.mp3'].clone(); 92 | sound2.play(); 93 | 94 | //スコアラベルを表示 95 | scoreLabel=new ScoreLabel(5,5); 96 | game.rootScene.addChild(scoreLabel); 97 | 98 | //穴を4x4に並べる 99 | for(y=0;y<4;y++){ 100 | for(x=0;x<4;x++){ 101 | var pit = new Pit(x*48+20,y*48+20); 102 | game.rootScene.addChild(pit); 103 | } 104 | } 105 | } 106 | game.start(); 107 | 108 | }, 500); 109 | } 110 | -------------------------------------------------------------------------------- /enchant.js: -------------------------------------------------------------------------------- 1 | /** 2 | * enchant.js v0.4.0 3 | * 4 | * Copyright (c) Ubiquitous Entertainment Inc. 5 | * Dual licensed under the MIT or GPL Version 3 licenses 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | * 25 | * This program is free software: you can redistribute it and/or modify 26 | * it under the terms of the GNU General Public License as published by 27 | * the Free Software Foundation, either version 3 of the License, or 28 | * (at your option) any later version. 29 | * 30 | * This program is distributed in the hope that it will be useful, 31 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 32 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 33 | * GNU General Public License for more details. 34 | * 35 | * You should have received a copy of the GNU General Public License 36 | * along with this program. If not, see . 37 | */ 38 | 39 | if (typeof Object.defineProperty != 'function') { 40 | Object.defineProperty = function(obj, prop, desc) { 41 | if ('value' in desc) obj[prop] = desc.value; 42 | if ('get' in desc) obj.__defineGetter__(prop, desc.get); 43 | if ('set' in desc) obj.__defineSetter__(prop, desc.set); 44 | return obj; 45 | }; 46 | } 47 | if (typeof Object.defineProperties != 'function') { 48 | Object.defineProperties = function(obj, descs) { 49 | for (var prop in descs) if (descs.hasOwnProperty(prop)) { 50 | Object.defineProperty(obj, prop, descs[prop]); 51 | } 52 | return obj; 53 | }; 54 | } 55 | if (typeof Object.create != 'function') { 56 | Object.create = function(prototype, descs) { 57 | function F() {}; 58 | F.prototype = prototype; 59 | var obj = new F(); 60 | if (descs != null) Object.defineProperties(obj, descs); 61 | return obj; 62 | }; 63 | } 64 | if (typeof Object.getPrototypeOf != 'function') { 65 | Object.getPrototypeOf = function(obj) { 66 | return obj.__proto__; 67 | }; 68 | } 69 | 70 | /** 71 | * グローバルにライブラリのクラスをエクスポートする. 72 | * 73 | * 引数に何も渡さない場合enchant.jsで定義されたクラス及びプラグインで定義されたクラス 74 | * 全てがエクスポートされる. 引数が一つ以上の場合はenchant.jsで定義されたクラスのみ 75 | * がデフォルトでエクスポートされ, プラグインのクラスをエクスポートしたい場合は明示的に 76 | * プラグインの識別子を引数として渡す必要がある. 77 | * 78 | * @example 79 | * enchant(); // 全てのクラスがエクスポートされる 80 | * enchant(''); // enchant.js本体のクラスのみがエクスポートされる 81 | * enchant('ui'); // enchant.js本体のクラスとui.enchant.jsのクラスがエクスポートされる 82 | * 83 | * @param {...String} [modules] エクスポートするモジュール. 複数指定できる. 84 | */ 85 | var enchant = function(modules) { 86 | if (modules != null) { 87 | if (!(modules instanceof Array)) { 88 | modules = Array.prototype.slice.call(arguments); 89 | } 90 | modules = modules.filter(function(module) { 91 | return [module].join(); 92 | }); 93 | } 94 | 95 | (function include(module, prefix) { 96 | var submodules = []; 97 | for (var prop in module) if (module.hasOwnProperty(prop)) { 98 | if (typeof module[prop] == 'function') { 99 | window[prop] = module[prop]; 100 | } else if (Object.getPrototypeOf(module[prop]) == Object.prototype) { 101 | if (modules == null) { 102 | submodules.push(prop); 103 | } else { 104 | i = modules.indexOf(prefix + prop); 105 | if (i != -1) { 106 | submodules.push(prop); 107 | modules.splice(i, 1); 108 | } 109 | } 110 | } 111 | } 112 | for (var i = 0, len = submodules.length; i < len; i++) { 113 | include(module[submodules[i]], prefix + submodules[i] + '.'); 114 | } 115 | })(enchant, ''); 116 | 117 | if (modules != null && modules.length) { 118 | throw new Error('Cannot load module: ' + modules.join(', ')); 119 | } 120 | }; 121 | 122 | (function() { 123 | 124 | "use strict"; 125 | 126 | var VENDER_PREFIX = (function() { 127 | var ua = navigator.userAgent; 128 | if (ua.indexOf('Opera') != -1) { 129 | return 'O'; 130 | } else if (ua.indexOf('MSIE') != -1) { 131 | return 'ms'; 132 | } else if (ua.indexOf('WebKit') != -1) { 133 | return 'webkit'; 134 | } else if (navigator.product == 'Gecko') { 135 | return 'Moz'; 136 | } else { 137 | return ''; 138 | } 139 | })(); 140 | var TOUCH_ENABLED = (function() { 141 | var div = document.createElement('div'); 142 | div.setAttribute('ontouchstart', 'return'); 143 | return typeof div.ontouchstart == 'function'; 144 | })(); 145 | var RETINA_DISPLAY = (function() { 146 | if (navigator.userAgent.indexOf('iPhone') != -1 && window.devicePixelRatio == 2) { 147 | var viewport = document.querySelector('meta[name="viewport"]'); 148 | if (viewport == null) { 149 | viewport = document.createElement('meta'); 150 | document.head.appendChild(viewport); 151 | } 152 | viewport.setAttribute('content', 'width=640px'); 153 | return true; 154 | } else { 155 | return false; 156 | } 157 | })(); 158 | 159 | // the running instance 160 | var game; 161 | 162 | /** 163 | * クラスのクラス. 164 | * 165 | * @param {Function} [superclass] 継承するクラス. 166 | * @param {*} definition クラス定義. 167 | * @constructor 168 | */ 169 | enchant.Class = function(superclass, definition) { 170 | return enchant.Class.create(superclass, definition); 171 | }; 172 | 173 | /** 174 | * クラスを作成する. 175 | * 176 | * ほかのクラスを継承したクラスを作成する場合, コンストラクタはデフォルトで 177 | * 継承元のクラスのものが使われる. コンストラクタをオーバーライドする場合継承元の 178 | * コンストラクタを適用するには明示的に呼び出す必要がある. 179 | * 180 | * @example 181 | * var Ball = Class.create({ // 何も継承しないクラスを作成する 182 | * initialize: function(radius) { ... }, // メソッド定義 183 | * fall: function() { ... } 184 | * }); 185 | * 186 | * var Ball = Class.create(Sprite); // Spriteを継承したクラスを作成する 187 | * var Ball = Class.create(Sprite, { // Spriteを継承したクラスを作成する 188 | * initialize: function(radius) { // コンストラクタを上書きする 189 | * Sprite.call(this, radius*2, radius*2); // 継承元のコンストラクタを適用する 190 | * this.image = game.assets['ball.gif']; 191 | * } 192 | * }); 193 | * 194 | * @param {Function} [superclass] 継承するクラス. 195 | * @param {*} [definition] クラス定義. 196 | * @static 197 | */ 198 | enchant.Class.create = function(superclass, definition) { 199 | if (arguments.length == 0) { 200 | return enchant.Class.create(Object, definition); 201 | } else if (arguments.length == 1 && typeof arguments[0] != 'function') { 202 | return enchant.Class.create(Object, arguments[0]); 203 | } 204 | 205 | for (var prop in definition) if (definition.hasOwnProperty(prop)) { 206 | if (Object.getPrototypeOf(definition[prop]) == Object.prototype) { 207 | if (!('enumerable' in definition[prop])) definition[prop].enumerable = true; 208 | } else { 209 | definition[prop] = { value: definition[prop], enumerable: true, writable: true }; 210 | } 211 | } 212 | var constructor = function() { 213 | if (this instanceof constructor) { 214 | constructor.prototype.initialize.apply(this, arguments); 215 | } else { 216 | return new constructor(); 217 | } 218 | }; 219 | constructor.prototype = Object.create(superclass.prototype, definition); 220 | constructor.prototype.constructor = constructor; 221 | if (constructor.prototype.initialize == null) { 222 | constructor.prototype.initialize = function() { 223 | superclass.apply(this, arguments); 224 | }; 225 | } 226 | return constructor; 227 | }; 228 | 229 | /** 230 | * @scope enchant.Event.prototype 231 | */ 232 | enchant.Event = enchant.Class.create({ 233 | /** 234 | * DOM Event風味の独自イベント実装を行ったクラス. 235 | * ただしフェーズの概念はなし. 236 | * @param {String} type Eventのタイプ 237 | * @constructs 238 | */ 239 | initialize: function(type) { 240 | /** 241 | * イベントのタイプ. 242 | * @type {String} 243 | */ 244 | this.type = type; 245 | /** 246 | * イベントのターゲット. 247 | * @type {*} 248 | */ 249 | this.target = null; 250 | /** 251 | * イベント発生位置のx座標. 252 | * @type {Number} 253 | */ 254 | this.x = 0; 255 | /** 256 | * イベント発生位置のy座標. 257 | * @type {Number} 258 | */ 259 | this.y = 0; 260 | /** 261 | * イベントを発行したオブジェクトを基準とするイベント発生位置のx座標. 262 | * @type {Number} 263 | */ 264 | this.localX = 0; 265 | /** 266 | * イベントを発行したオブジェクトを基準とするイベント発生位置のy座標. 267 | * @type {Number} 268 | */ 269 | this.localY = 0; 270 | }, 271 | _initPosition: function(pageX, pageY) { 272 | this.x = this.localX = (pageX - game._pageX) / game.scale; 273 | this.y = this.localY = (pageY - game._pageY) / game.scale; 274 | } 275 | }); 276 | 277 | /** 278 | * Gameのロード完了時に発生するイベント. 279 | * 280 | * 画像のプリロードを行う場合ロードが完了するのを待ってゲーム開始時の処理を行う必要がある. 281 | * 発行するオブジェクト: enchant.Game 282 | * 283 | * @example 284 | * var game = new Game(320, 320); 285 | * game.preload('player.gif'); 286 | * game.onload = function() { 287 | * ... // ゲーム開始時の処理を記述 288 | * }; 289 | * game.start(); 290 | * 291 | * @type {String} 292 | */ 293 | enchant.Event.LOAD = 'load'; 294 | 295 | /** 296 | * Gameのロード進行中に発生するイベント. 297 | * プリロードする画像が一枚ロードされる度に発行される. 発行するオブジェクト: enchant.Game 298 | * @type {String} 299 | */ 300 | enchant.Event.PROGRESS = 'progress'; 301 | 302 | /** 303 | * フレーム開始時に発生するイベント. 304 | * 発行するオブジェクト: enchant.Game, enchant.Node 305 | * @type {String} 306 | */ 307 | enchant.Event.ENTER_FRAME = 'enterframe'; 308 | 309 | /** 310 | * フレーム終了時に発生するイベント. 311 | * 発行するオブジェクト: enchant.Game 312 | * @type {String} 313 | */ 314 | enchant.Event.EXIT_FRAME = 'exitframe'; 315 | 316 | /** 317 | * Sceneが開始したとき発生するイベント. 318 | * 発行するオブジェクト: enchant.Scene 319 | * @type {String} 320 | */ 321 | enchant.Event.ENTER = 'enter'; 322 | 323 | /** 324 | * Sceneが終了したとき発生するイベント. 325 | * 発行するオブジェクト: enchant.Scene 326 | * @type {String} 327 | */ 328 | enchant.Event.EXIT = 'exit'; 329 | 330 | /** 331 | * NodeがGroupに追加されたとき発生するイベント. 332 | * 発行するオブジェクト: enchant.Node 333 | * @type {String} 334 | */ 335 | enchant.Event.ADDED = 'added'; 336 | 337 | /** 338 | * NodeがSceneに追加されたとき発生するイベント. 339 | * 発行するオブジェクト: enchant.Node 340 | * @type {String} 341 | */ 342 | enchant.Event.ADDED_TO_SCENE = 'addedtoscene'; 343 | 344 | /** 345 | * NodeがGroupから削除されたとき発生するイベント. 346 | * 発行するオブジェクト: enchant.Node 347 | * @type {String} 348 | */ 349 | enchant.Event.REMOVED = 'removed'; 350 | 351 | /** 352 | * NodeがSceneから削除されたとき発生するイベント. 353 | * 発行するオブジェクト: enchant.Node 354 | * @type {String} 355 | */ 356 | enchant.Event.REMOVED_FROM_SCENE = 'removedfromscene'; 357 | 358 | /** 359 | * Nodeに対するタッチが始まったとき発生するイベント. 360 | * クリックもタッチとして扱われる. 発行するオブジェクト: enchant.Node 361 | * @type {String} 362 | */ 363 | enchant.Event.TOUCH_START = 'touchstart'; 364 | 365 | /** 366 | * Nodeに対するタッチが移動したとき発生するイベント. 367 | * クリックもタッチとして扱われる. 発行するオブジェクト: enchant.Node 368 | * @type {String} 369 | */ 370 | enchant.Event.TOUCH_MOVE = 'touchmove'; 371 | 372 | /** 373 | * Nodeに対するタッチが終了したとき発生するイベント. 374 | * クリックもタッチとして扱われる. 発行するオブジェクト: enchant.Node 375 | * @type {String} 376 | */ 377 | enchant.Event.TOUCH_END = 'touchend'; 378 | 379 | /** 380 | * Entityがレンダリングされるときに発生するイベント. 381 | * 発行するオブジェクト: enchant.Entity 382 | * @type {String} 383 | */ 384 | enchant.Event.RENDER = 'render'; 385 | 386 | /** 387 | * ボタン入力が始まったとき発生するイベント. 388 | * 発行するオブジェクト: enchant.Game, enchant.Scene 389 | * @type {String} 390 | */ 391 | enchant.Event.INPUT_START = 'inputstart'; 392 | 393 | /** 394 | * ボタン入力が変化したとき発生するイベント. 395 | * 発行するオブジェクト: enchant.Game, enchant.Scene 396 | * @type {String} 397 | */ 398 | enchant.Event.INPUT_CHANGE = 'inputchange'; 399 | 400 | /** 401 | * ボタン入力が終了したとき発生するイベント. 402 | * 発行するオブジェクト: enchant.Game, enchant.Scene 403 | * @type {String} 404 | */ 405 | enchant.Event.INPUT_END = 'inputend'; 406 | 407 | /** 408 | * leftボタンが押された発生するイベント. 409 | * 発行するオブジェクト: enchant.Game, enchant.Scene 410 | * @type {String} 411 | */ 412 | enchant.Event.LEFT_BUTTON_DOWN = 'leftbuttondown'; 413 | 414 | /** 415 | * leftボタンが離された発生するイベント. 416 | * 発行するオブジェクト: enchant.Game, enchant.Scene 417 | * @type {String} 418 | */ 419 | enchant.Event.LEFT_BUTTON_UP = 'leftbuttonup'; 420 | 421 | /** 422 | * rightボタンが押された発生するイベント. 423 | * 発行するオブジェクト: enchant.Game, enchant.Scene 424 | * @type {String} 425 | */ 426 | enchant.Event.RIGHT_BUTTON_DOWN = 'rightbuttondown'; 427 | 428 | /** 429 | * rightボタンが離された発生するイベント. 430 | * 発行するオブジェクト: enchant.Game, enchant.Scene 431 | * @type {String} 432 | */ 433 | enchant.Event.RIGHT_BUTTON_UP = 'rightbuttonup'; 434 | 435 | /** 436 | * upボタンが押された発生するイベント. 437 | * 発行するオブジェクト: enchant.Game, enchant.Scene 438 | * @type {String} 439 | */ 440 | enchant.Event.UP_BUTTON_DOWN = 'upbuttondown'; 441 | 442 | /** 443 | * upボタンが離された発生するイベント. 444 | * 発行するオブジェクト: enchant.Game, enchant.Scene 445 | * @type {String} 446 | */ 447 | enchant.Event.UP_BUTTON_UP = 'upbuttonup'; 448 | 449 | /** 450 | * downボタンが離された発生するイベント. 451 | * 発行するオブジェクト: enchant.Game, enchant.Scene 452 | * @type {String} 453 | */ 454 | enchant.Event.DOWN_BUTTON_DOWN = 'downbuttondown'; 455 | 456 | /** 457 | * downボタンが離された発生するイベント. 458 | * 発行するオブジェクト: enchant.Game, enchant.Scene 459 | * @type {String} 460 | */ 461 | enchant.Event.DOWN_BUTTON_UP = 'downbuttonup'; 462 | 463 | /** 464 | * aボタンが押された発生するイベント. 465 | * 発行するオブジェクト: enchant.Game, enchant.Scene 466 | * @type {String} 467 | */ 468 | enchant.Event.A_BUTTON_DOWN = 'abuttondown'; 469 | 470 | /** 471 | * aボタンが離された発生するイベント. 472 | * 発行するオブジェクト: enchant.Game, enchant.Scene 473 | * @type {String} 474 | */ 475 | enchant.Event.A_BUTTON_UP = 'abuttonup'; 476 | 477 | /** 478 | * bボタンが押された発生するイベント. 479 | * 発行するオブジェクト: enchant.Game, enchant.Scene 480 | * @type {String} 481 | */ 482 | enchant.Event.B_BUTTON_DOWN = 'bbuttondown'; 483 | 484 | /** 485 | * bボタンが離された発生するイベント. 486 | * 発行するオブジェクト: enchant.Game, enchant.Scene 487 | * @type {String} 488 | */ 489 | enchant.Event.B_BUTTON_UP = 'bbuttonup'; 490 | 491 | 492 | /** 493 | * @scope enchant.EventTarget.prototype 494 | */ 495 | enchant.EventTarget = enchant.Class.create({ 496 | /** 497 | * DOM Event風味の独自イベント実装を行ったクラス. 498 | * ただしフェーズの概念はなし. 499 | * @constructs 500 | */ 501 | initialize: function() { 502 | this._listeners = {}; 503 | }, 504 | /** 505 | * イベントリスナを追加する. 506 | * @param {String} type イベントのタイプ. 507 | * @param {function(e:enchant.Event)} listener 追加するイベントリスナ. 508 | */ 509 | addEventListener: function(type, listener) { 510 | var listeners = this._listeners[type]; 511 | if (listeners == null) { 512 | this._listeners[type] = [listener]; 513 | } else if (listeners.indexOf(listener) == -1) { 514 | listeners.unshift(listener); 515 | } 516 | }, 517 | /** 518 | * イベントリスナを削除する. 519 | * @param {String} type イベントのタイプ. 520 | * @param {function(e:enchant.Event)} listener 削除するイベントリスナ. 521 | */ 522 | removeEventListener: function(type, listener) { 523 | var listeners = this._listeners[type]; 524 | if (listeners != null) { 525 | var i = listeners.indexOf(listener); 526 | if (i != -1) { 527 | listeners.splice(i, 1); 528 | } 529 | } 530 | }, 531 | /** 532 | * イベントを発行する. 533 | * @param {enchant.Event} e 発行するイベント. 534 | */ 535 | dispatchEvent: function(e) { 536 | e.target = this; 537 | e.localX = e.x - this._offsetX; 538 | e.localY = e.y - this._offsetY; 539 | if (this['on' + e.type] != null) this['on' + e.type](); 540 | var listeners = this._listeners[e.type]; 541 | if (listeners != null) { 542 | listeners = listeners.slice(); 543 | for (var i = 0, len = listeners.length; i < len; i++) { 544 | listeners[i].call(this, e); 545 | } 546 | } 547 | } 548 | }); 549 | 550 | /** 551 | * @scope enchant.Game.prototype 552 | */ 553 | enchant.Game = enchant.Class.create(enchant.EventTarget, { 554 | /** 555 | * ゲームのメインループ, シーンを管理するクラス. 556 | * 557 | * インスタンスは一つしか存在することができず, すでにインスタンスが存在する状態で 558 | * コンストラクタを実行した場合既存のものが上書きされる. 存在するインスタンスには 559 | * enchant.Game.instanceからアクセスできる. 560 | * 561 | * @param {Number} width ゲーム画面の横幅. 562 | * @param {Number} height ゲーム画面の高さ. 563 | * @constructs 564 | * @extends enchant.EventTarget 565 | */ 566 | initialize: function(width, height) { 567 | enchant.EventTarget.call(this); 568 | 569 | var initial = true; 570 | if (game) { 571 | initial = false; 572 | game.stop(); 573 | } 574 | game = enchant.Game.instance = this; 575 | 576 | /** 577 | * ゲーム画面の横幅. 578 | * @type {Number} 579 | */ 580 | this.width = width || 320; 581 | /** 582 | * ゲーム画面の高さ. 583 | * @type {Number} 584 | */ 585 | this.height = height || 320; 586 | /** 587 | * ゲームの表示倍率. 588 | * @type {Number} 589 | */ 590 | this.scale = 1; 591 | 592 | var stage = document.getElementById('enchant-stage'); 593 | if (!stage) { 594 | stage = document.createElement('div'); 595 | stage.id = 'enchant-stage'; 596 | stage.style.width = window.innerWidth + 'px'; 597 | stage.style.height = window.innerHeight + 'px'; 598 | stage.style.position = 'absolute'; 599 | if (document.body.firstChild) { 600 | document.body.insertBefore(stage, document.body.firstChild); 601 | } else { 602 | document.body.appendChild(stage); 603 | } 604 | this.scale = Math.min( 605 | window.innerWidth / this.width, 606 | window.innerHeight / this.height 607 | ); 608 | this._pageX = 0; 609 | this._pageY = 0; 610 | } else { 611 | var style = window.getComputedStyle(stage); 612 | width = parseInt(style.width); 613 | height = parseInt(style.height); 614 | if (width && height) { 615 | this.scale = Math.min( 616 | width / this.width, 617 | height / this.height 618 | ); 619 | } else { 620 | stage.style.width = this.width + 'px'; 621 | stage.style.height = this.height + 'px'; 622 | } 623 | while (stage.firstChild) { 624 | stage.removeChild(stage.firstChild); 625 | } 626 | stage.style.position = 'relative'; 627 | var bounding = stage.getBoundingClientRect(); 628 | this._pageX = Math.round(window.scrollX + bounding.left); 629 | this._pageY = Math.round(window.scrollY + bounding.top); 630 | } 631 | if (!this.scale) this.scale = 1; 632 | stage.style.fontSize = '12px'; 633 | stage.style.webkitTextSizeAdjust = 'none'; 634 | this._element = stage; 635 | 636 | /** 637 | * ゲームのフレームレート. 638 | * @type {Number} 639 | */ 640 | this.fps = 30; 641 | /** 642 | * ゲーム開始からのフレーム数. 643 | * @type {Number} 644 | */ 645 | this.frame = 0; 646 | /** 647 | * ゲームが実行可能な状態かどうか. 648 | * @type {Boolean} 649 | */ 650 | this.ready = null; 651 | /** 652 | * ゲームが実行状態かどうか. 653 | * @type {Boolean} 654 | */ 655 | this.running = false; 656 | /** 657 | * ロードされた画像をパスをキーとして保存するオブジェクト. 658 | * @type {Object.} 659 | */ 660 | this.assets = {}; 661 | var assets = this._assets = []; 662 | (function detectAssets(module) { 663 | if (module.assets instanceof Array) { 664 | [].push.apply(assets, module.assets); 665 | } 666 | for (var prop in module) if (module.hasOwnProperty(prop)) { 667 | if (Object.getPrototypeOf(module[prop]) == Object.prototype) { 668 | detectAssets(module[prop]); 669 | } 670 | } 671 | })(enchant); 672 | 673 | this._scenes = []; 674 | /** 675 | * 現在のScene. Sceneスタック中の一番上のScene. 676 | * @type {enchant.Scene} 677 | */ 678 | this.currentScene = null; 679 | /** 680 | * ルートScene. Sceneスタック中の一番下のScene. 681 | * @type {enchant.Scene} 682 | */ 683 | this.rootScene = new enchant.Scene(); 684 | this.pushScene(this.rootScene); 685 | /** 686 | * ローディング時に表示されるScene. 687 | * @type {enchant.Scene} 688 | */ 689 | this.loadingScene = new enchant.Scene(); 690 | this.loadingScene.backgroundColor = '#000'; 691 | var barWidth = this.width * 0.9 | 0; 692 | var barHeight = this.width * 0.3 | 0; 693 | var border = barWidth * 0.05 | 0; 694 | var bar = new enchant.Sprite(barWidth, barHeight); 695 | bar.x = (this.width - barWidth) / 2; 696 | bar.y = (this.height - barHeight) / 2; 697 | var image = new enchant.Surface(barWidth, barHeight); 698 | image.context.fillStyle = '#fff'; 699 | image.context.fillRect(0, 0, barWidth, barHeight); 700 | image.context.fillStyle = '#000'; 701 | image.context.fillRect(border, border, barWidth - border*2, barHeight - border*2); 702 | bar.image = image; 703 | var progress = 0, _progress = 0; 704 | this.addEventListener('progress', function(e) { 705 | progress = e.loaded / e.total; 706 | }); 707 | bar.addEventListener('enterframe', function() { 708 | _progress *= 0.9; 709 | _progress += progress * 0.1; 710 | image.context.fillStyle = '#fff'; 711 | image.context.fillRect(border, 0, (barWidth - border*2) * _progress, barHeight); 712 | }); 713 | this.loadingScene.addChild(bar); 714 | 715 | this._mousedownID = 0; 716 | this._surfaceID = 0; 717 | this._soundID = 0; 718 | this._intervalID = null; 719 | 720 | /** 721 | * ゲームに対する入力状態を保存するオブジェクト. 722 | * @type {Object.} 723 | */ 724 | this.input = {}; 725 | this._keybind = {}; 726 | this.keybind(37, 'left'); // Left Arrow 727 | this.keybind(38, 'up'); // Up Arrow 728 | this.keybind(39, 'right'); // Right Arrow 729 | this.keybind(40, 'down'); // Down Arrow 730 | 731 | var c = 0; 732 | ['left', 'right', 'up', 'down', 'a', 'b'].forEach(function(type) { 733 | this.addEventListener(type + 'buttondown', function(e) { 734 | if (!this.input[type]) { 735 | this.input[type] = true; 736 | this.dispatchEvent(new enchant.Event((c++) ? 'inputchange' : 'inputstart')); 737 | } 738 | this.currentScene.dispatchEvent(e); 739 | }); 740 | this.addEventListener(type + 'buttonup', function(e) { 741 | if (this.input[type]) { 742 | this.input[type] = false; 743 | this.dispatchEvent(new enchant.Event((--c) ? 'inputchange' : 'inputend')); 744 | } 745 | this.currentScene.dispatchEvent(e); 746 | }); 747 | }, this); 748 | 749 | if (initial) { 750 | document.addEventListener('keydown', function(e) { 751 | game.dispatchEvent(new enchant.Event('keydown')); 752 | if ((37 <= e.keyCode && e.keyCode <= 40) || e.keyCode == 32) { 753 | e.preventDefault(); 754 | e.stopPropagation(); 755 | } 756 | 757 | if (!game.running) return; 758 | var button = game._keybind[e.keyCode]; 759 | if (button) { 760 | var e = new enchant.Event(button + 'buttondown'); 761 | game.dispatchEvent(e); 762 | } 763 | }, true); 764 | document.addEventListener('keyup', function(e) { 765 | if (!game.running) return; 766 | var button = game._keybind[e.keyCode]; 767 | if (button) { 768 | var e = new enchant.Event(button + 'buttonup'); 769 | game.dispatchEvent(e); 770 | } 771 | }, true); 772 | if (TOUCH_ENABLED) { 773 | document.addEventListener('touchstart', function(e) { 774 | e.preventDefault(); 775 | }, true); 776 | document.addEventListener('touchmove', function(e) { 777 | e.preventDefault(); 778 | if (!game.running) e.stopPropagation(); 779 | }, true); 780 | document.addEventListener('touchend', function(e) { 781 | e.preventDefault(); 782 | if (!game.running) e.stopPropagation(); 783 | }, true); 784 | } else { 785 | document.addEventListener('mousedown', function(e) { 786 | e.preventDefault(); 787 | game._mousedownID++; 788 | if (!game.running) e.stopPropagation(); 789 | }, true); 790 | document.addEventListener('mousemove', function(e) { 791 | e.preventDefault(); 792 | if (!game.running) e.stopPropagation(); 793 | }, true); 794 | document.addEventListener('mouseup', function(e) { 795 | e.preventDefault(); 796 | if (!game.running) e.stopPropagation(); 797 | }, true); 798 | } 799 | } 800 | }, 801 | /** 802 | * ファイルのプリロードを行う. 803 | * 804 | * プリロードを行うよう設定されたファイルはenchant.Game#startが実行されるとき 805 | * ロードが行われる. 全てのファイルのロードが完了したときはGameオブジェクトからload 806 | * イベントが発行され, Gameオブジェクトのassetsプロパティから画像ファイルの場合は 807 | * Surfaceオブジェクトとして, 音声ファイルの場合はSoundオブジェクトとして, 808 | * その他の場合は文字列としてアクセスできるようになる. 809 | * 810 | * なおこのSurfaceオブジェクトはenchant.Surface.loadを使って作成されたものである 811 | * ため直接画像操作を行うことはできない. enchant.Surface.loadの項を参照. 812 | * 813 | * @example 814 | * game.preload('player.gif'); 815 | * game.onload = function() { 816 | * var sprite = new Sprite(32, 32); 817 | * sprite.image = game.assets['player.gif']; // パス名でアクセス 818 | * ... 819 | * }; 820 | * game.start(); 821 | * 822 | * @param {...String} assets プリロードする画像のパス. 複数指定できる. 823 | */ 824 | preload: function(assets) { 825 | if (!(assets instanceof Array)) { 826 | assets = Array.prototype.slice.call(arguments); 827 | } 828 | [].push.apply(this._assets, assets); 829 | }, 830 | /** 831 | * ファイルのロードを行う. 832 | * 833 | * @param {String} asset ロードするファイルのパス. 834 | * @param {Function} [callback] ファイルのロードが完了したときに呼び出される関数. 835 | */ 836 | load: function(src, callback) { 837 | if (callback == null) callback = function() {}; 838 | 839 | var ext = src.match(/\.\w+$/)[0]; 840 | if (ext) ext = ext.slice(1).toLowerCase(); 841 | switch (ext) { 842 | case 'jpg': 843 | case 'gif': 844 | case 'png': 845 | game.assets[src] = enchant.Surface.load(src); 846 | game.assets[src].addEventListener('load', callback); 847 | break; 848 | case 'mp3': 849 | case 'aac': 850 | case 'm4a': 851 | case 'wav': 852 | case 'ogg': 853 | game.assets[src] = enchant.Sound.load(src, 'audio/' + ext); 854 | game.assets[src].addEventListener('load', callback); 855 | break; 856 | default: 857 | var req = new XMLHttpRequest(); 858 | req.open('GET', src, true); 859 | req.onreadystatechange = function(e) { 860 | if (req.readyState == 4) { 861 | if (req.status != 200) { 862 | throw new Error('Cannot load an asset: ' + src); 863 | } 864 | 865 | var type = req.getResponseHeader('Content-Type') || ''; 866 | if (type.match(/^image/)) { 867 | game.assets[src] = enchant.Surface.load(src); 868 | game.assets[src].addEventListener('load', callback); 869 | } else if (type.match(/^audio/)) { 870 | game.assets[src] = enchant.Sound.load(src, type); 871 | game.assets[src].addEventListener('load', callback); 872 | } else { 873 | game.assets[asset] = req.responseText; 874 | callback(); 875 | } 876 | } 877 | }; 878 | req.send(null); 879 | } 880 | }, 881 | /** 882 | * ゲームを開始する. 883 | * 884 | * enchant.Game#fpsで設定されたフレームレートに従ってenchant.Game#currentSceneの 885 | * フレームの更新が行われるようになる. プリロードする画像が存在する場合はロードが 886 | * 始まりローディング画面が表示される. 887 | */ 888 | start: function() { 889 | if (this._intervalID) { 890 | window.clearInterval(this._intervalID); 891 | } else if (this._assets.length) { 892 | if (enchant.Sound.enabledInMobileSafari && !game._touched && 893 | VENDER_PREFIX == 'webkit' && TOUCH_ENABLED) { 894 | var scene = new Scene(); 895 | scene.backgroundColor = '#000'; 896 | var size = Math.round(game.width / 10); 897 | var sprite = new Sprite(game.width, size); 898 | sprite.y = (game.height - size) / 2; 899 | sprite.image = new Surface(game.width, size); 900 | sprite.image.context.fillStyle = '#fff'; 901 | sprite.image.context.font = (size-1) + 'px bold Helvetica,Arial,sans-serif'; 902 | var width = sprite.image.context.measureText('Touch to Start').width; 903 | sprite.image.context.fillText('Touch to Start', (game.width - width) / 2, size-1); 904 | scene.addChild(sprite); 905 | document.addEventListener('touchstart', function() { 906 | game._touched = true; 907 | game.removeScene(scene); 908 | game.start(); 909 | }, true); 910 | game.pushScene(scene); 911 | return; 912 | } 913 | 914 | var o = {}; 915 | var assets = this._assets.filter(function(asset) { 916 | return asset in o ? false : o[asset] = true; 917 | }); 918 | var loaded = 0; 919 | for (var i = 0, len = assets.length; i < len; i++) { 920 | this.load(assets[i], function() { 921 | var e = new enchant.Event('progress'); 922 | e.loaded = ++loaded; 923 | e.total = len; 924 | game.dispatchEvent(e); 925 | if (loaded == len) { 926 | game.removeScene(game.loadingScene); 927 | game.dispatchEvent(new enchant.Event('load')); 928 | } 929 | }); 930 | } 931 | this.pushScene(this.loadingScene); 932 | } else { 933 | this.dispatchEvent(new enchant.Event('load')); 934 | } 935 | this.currentTime = Date.now(); 936 | this._intervalID = window.setInterval(function() { 937 | game._tick() 938 | }, 1000 / this.fps); 939 | this.running = true; 940 | }, 941 | _tick: function() { 942 | var now = Date.now(); 943 | var e = new enchant.Event('enterframe'); 944 | e.elapsed = now - this.currentTime; 945 | this.currentTime = now; 946 | 947 | var nodes = this.currentScene.childNodes.slice(); 948 | var push = Array.prototype.push; 949 | while (nodes.length) { 950 | var node = nodes.pop(); 951 | node.dispatchEvent(e); 952 | if (node.childNodes) { 953 | push.apply(nodes, node.childNodes); 954 | } 955 | } 956 | 957 | this.currentScene.dispatchEvent(e); 958 | this.dispatchEvent(e); 959 | 960 | this.dispatchEvent(new enchant.Event('exitframe')); 961 | this.frame++; 962 | }, 963 | /** 964 | * ゲームを停止する. 965 | * 966 | * フレームは更新されず, プレイヤーの入力も受け付けなくなる. 967 | * enchant.Game#startで再開できる. 968 | */ 969 | stop: function() { 970 | if (this._intervalID) { 971 | window.clearInterval(this._intervalID); 972 | this._intervalID = null; 973 | } 974 | this.running = false; 975 | }, 976 | /** 977 | * ゲームを一時停止する. 978 | * 979 | * フレームは更新されず, プレイヤーの入力は受け付ける. 980 | * enchant.Game#startで再開できる. 981 | */ 982 | pause: function() { 983 | if (this._intervalID) { 984 | window.clearInterval(this._intervalID); 985 | this._intervalID = null; 986 | } 987 | }, 988 | /** 989 | * 新しいSceneに移行する. 990 | * 991 | * Sceneはスタック状に管理されており, 表示順序もスタックに積み上げられた順に従う. 992 | * enchant.Game#pushSceneを行うとSceneをスタックの一番上に積むことができる. スタックの 993 | * 一番上のSceneに対してはフレームの更新が行われる. 994 | * 995 | * @param {enchant.Scene} scene 移行する新しいScene. 996 | * @return {enchant.Scene} 新しいScene. 997 | */ 998 | pushScene: function(scene) { 999 | this._element.appendChild(scene._element); 1000 | if (this.currentScene) { 1001 | this.currentScene.dispatchEvent(new enchant.Event('exit')); 1002 | } 1003 | this.currentScene = scene; 1004 | this.currentScene.dispatchEvent(new enchant.Event('enter')); 1005 | return this._scenes.push(scene); 1006 | }, 1007 | /** 1008 | * 現在のSceneを終了させ前のSceneに戻る. 1009 | * 1010 | * Sceneはスタック状に管理されており, 表示順序もスタックに積み上げられた順に従う. 1011 | * enchant.Game#popSceneを行うとスタックの一番上のSceneを取り出すことができる. 1012 | * 1013 | * @return {enchant.Scene} 終了させたScene. 1014 | */ 1015 | popScene: function() { 1016 | if (this.currentScene == this.rootScene) { 1017 | return; 1018 | } 1019 | this._element.removeChild(this.currentScene._element); 1020 | this.currentScene.dispatchEvent(new enchant.Event('exit')); 1021 | this.currentScene = this._scenes[this._scenes.length-2]; 1022 | this.currentScene.dispatchEvent(new enchant.Event('enter')); 1023 | return this._scenes.pop(); 1024 | }, 1025 | /** 1026 | * 現在のSceneを別のSceneにおきかえる. 1027 | * 1028 | * enchant.Game#popScene, enchant.Game#pushSceneを同時に行う. 1029 | * 1030 | * @param {enchant.Scene} scene おきかえるScene. 1031 | * @return {enchant.Scene} 新しいScene. 1032 | */ 1033 | replaceScene: function(scene) { 1034 | this.popScene(); 1035 | return this.pushScene(scene); 1036 | }, 1037 | /** 1038 | * Scene削除する. 1039 | * 1040 | * Sceneスタック中からSceneを削除する. 1041 | * 1042 | * @param {enchant.Scene} scene 削除するScene. 1043 | * @return {enchant.Scene} 削除したScene. 1044 | */ 1045 | removeScene: function(scene) { 1046 | if (this.currentScene == scene) { 1047 | return this.popScene(); 1048 | } else { 1049 | var i = this._scenes.indexOf(scene); 1050 | if (i != -1) { 1051 | this._scenes.splice(i, 1); 1052 | this._element.removeChild(scene._element); 1053 | return scene; 1054 | } 1055 | } 1056 | }, 1057 | /** 1058 | * キーバインドを設定する. 1059 | * 1060 | * キー入力をleft, right, up, down, a, bいずれかのボタン入力として割り当てる. 1061 | * 1062 | * @param {Number} key キーバインドを設定するキーコード. 1063 | * @param {String} button 割り当てるボタン. 1064 | */ 1065 | keybind: function(key, button) { 1066 | this._keybind[key] = button; 1067 | } 1068 | }); 1069 | 1070 | /** 1071 | * 現在のGameインスタンス. 1072 | * @type {enchant.Game} 1073 | * @static 1074 | */ 1075 | enchant.Game.instance = null; 1076 | 1077 | /** 1078 | * @scope enchant.Node.prototype 1079 | */ 1080 | enchant.Node = enchant.Class.create(enchant.EventTarget, { 1081 | /** 1082 | * Sceneをルートとした表示オブジェクトツリーに属するオブジェクトの基底クラス. 1083 | * 直接使用することはない. 1084 | * @constructs 1085 | * @extends enchant.EventTarget 1086 | */ 1087 | initialize: function() { 1088 | enchant.EventTarget.call(this); 1089 | 1090 | this._x = 0; 1091 | this._y = 0; 1092 | this._offsetX = 0; 1093 | this._offsetY = 0; 1094 | 1095 | /** 1096 | * Nodeの親Node. 1097 | * @type {enchant.Group} 1098 | */ 1099 | this.parentNode = null; 1100 | /** 1101 | * Nodeが属しているScene. 1102 | * @type {enchant.Scene} 1103 | */ 1104 | this.scene = null; 1105 | 1106 | this.addEventListener('touchstart', function(e) { 1107 | if (this.parentNode && this.parentNode != this.scene) { 1108 | this.parentNode.dispatchEvent(e); 1109 | } 1110 | }); 1111 | this.addEventListener('touchmove', function(e) { 1112 | if (this.parentNode && this.parentNode != this.scene) { 1113 | this.parentNode.dispatchEvent(e); 1114 | } 1115 | }); 1116 | this.addEventListener('touchend', function(e) { 1117 | if (this.parentNode && this.parentNode != this.scene) { 1118 | this.parentNode.dispatchEvent(e); 1119 | } 1120 | }); 1121 | }, 1122 | /** 1123 | * Nodeを移動する. 1124 | * @param {Number} x 移動先のx座標. 1125 | * @param {Number} y 移動先のy座標. 1126 | */ 1127 | moveTo: function(x, y) { 1128 | this._x = x; 1129 | this._y = y; 1130 | this._updateCoordinate(); 1131 | }, 1132 | /** 1133 | * Nodeを移動する. 1134 | * @param {Number} x 移動するx軸方向の距離. 1135 | * @param {Number} y 移動するy軸方向の距離. 1136 | */ 1137 | moveBy: function(x, y) { 1138 | this._x += x; 1139 | this._y += y; 1140 | this._updateCoordinate(); 1141 | }, 1142 | /** 1143 | * Nodeのx座標. 1144 | * @type {Number} 1145 | */ 1146 | x: { 1147 | get: function() { 1148 | return this._x; 1149 | }, 1150 | set: function(x) { 1151 | this._x = x; 1152 | this._updateCoordinate(); 1153 | } 1154 | }, 1155 | /** 1156 | * Nodeのy座標. 1157 | * @type {Number} 1158 | */ 1159 | y: { 1160 | get: function() { 1161 | return this._y; 1162 | }, 1163 | set: function(y) { 1164 | this._y = y; 1165 | this._updateCoordinate(); 1166 | } 1167 | }, 1168 | _updateCoordinate: function() { 1169 | if (this.parentNode) { 1170 | this._offsetX = this.parentNode._offsetX + this._x; 1171 | this._offsetY = this.parentNode._offsetY + this._y; 1172 | } else { 1173 | this._offsetX = this._x; 1174 | this._offsetY = this._y; 1175 | } 1176 | } 1177 | }); 1178 | 1179 | /** 1180 | * @scope enchant.Entity.prototype 1181 | */ 1182 | enchant.Entity = enchant.Class.create(enchant.Node, { 1183 | /** 1184 | * DOM上で表示する実体を持ったクラス.直接使用することはない. 1185 | * @constructs 1186 | * @extends enchant.Node 1187 | */ 1188 | initialize: function() { 1189 | enchant.Node.call(this); 1190 | 1191 | this._element = document.createElement('div'); 1192 | this._style = this._element.style; 1193 | this._style.position = 'absolute'; 1194 | 1195 | this._width = 0; 1196 | this._height = 0; 1197 | this._backgroundColor = null; 1198 | this._opacity = 1; 1199 | this._visible = true; 1200 | this._buttonMode = null; 1201 | 1202 | /** 1203 | * Entityにボタンの機能を設定する. 1204 | * Entityに対するタッチ, クリックをleft, right, up, down, a, bいずれかの 1205 | * ボタン入力として割り当てる. 1206 | * @type {String} 1207 | */ 1208 | this.buttonMode = null; 1209 | /** 1210 | * Entityが押されているかどうか. 1211 | * buttonModeが設定されているときだけ機能する. 1212 | * @type {Boolean} 1213 | */ 1214 | this.buttonPressed = false; 1215 | this.addEventListener('touchstart', function() { 1216 | if (!this.buttonMode) return; 1217 | this.buttonPressed = true; 1218 | var e = new Event(button + 'buttondown'); 1219 | this.dispatchEvent(e); 1220 | game.dispatchEvent(e); 1221 | }); 1222 | this.addEventListener('touchend', function() { 1223 | if (!this.buttonMode) return; 1224 | this.buttonPressed = false; 1225 | var e = new Event(button + 'buttonup'); 1226 | this.dispatchEvent(e); 1227 | game.dispatchEvent(e); 1228 | }); 1229 | 1230 | var that = this; 1231 | var render = function() { 1232 | that.dispatchEvent(new enchant.Event('render')); 1233 | }; 1234 | this.addEventListener('addedtoscene', function() { 1235 | render(); 1236 | game.addEventListener('exitframe', render); 1237 | }); 1238 | this.addEventListener('removedfromscene', function() { 1239 | game.removeEventListener('exitframe', render); 1240 | }); 1241 | this.addEventListener('render', function() { 1242 | if (this._offsetX != this._previousOffsetX) { 1243 | this._style.left = this._offsetX + 'px'; 1244 | } 1245 | if (this._offsetY != this._previousOffsetY) { 1246 | this._style.top = this._offsetY + 'px'; 1247 | } 1248 | this._previousOffsetX = this._offsetX; 1249 | this._previousOffsetY = this._offsetY; 1250 | }); 1251 | 1252 | var that = this; 1253 | if (TOUCH_ENABLED) { 1254 | this._element.addEventListener('touchstart', function(e) { 1255 | var touches = e.touches; 1256 | for (var i = 0, len = touches.length; i < len; i++) { 1257 | e = new enchant.Event('touchstart'); 1258 | e.identifier = touches[i].identifier; 1259 | e._initPosition(touches[i].pageX, touches[i].pageY); 1260 | that.dispatchEvent(e); 1261 | } 1262 | }, false); 1263 | this._element.addEventListener('touchmove', function(e) { 1264 | var touches = e.touches; 1265 | for (var i = 0, len = touches.length; i < len; i++) { 1266 | e = new enchant.Event('touchmove'); 1267 | e.identifier = touches[i].identifier; 1268 | e._initPosition(touches[i].pageX, touches[i].pageY); 1269 | that.dispatchEvent(e); 1270 | } 1271 | }, false); 1272 | this._element.addEventListener('touchend', function(e) { 1273 | var touches = e.changedTouches; 1274 | for (var i = 0, len = touches.length; i < len; i++) { 1275 | e = new enchant.Event('touchend'); 1276 | e.identifier = touches[i].identifier; 1277 | e._initPosition(touches[i].pageX, touches[i].pageY); 1278 | that.dispatchEvent(e); 1279 | } 1280 | }, false); 1281 | } else { 1282 | this._element.addEventListener('mousedown', function(e) { 1283 | var x = e.pageX; 1284 | var y = e.pageY; 1285 | e = new enchant.Event('touchstart'); 1286 | e.identifier = game._mousedownID; 1287 | e._initPosition(x, y); 1288 | that.dispatchEvent(e); 1289 | that._mousedown = true; 1290 | }, false); 1291 | game._element.addEventListener('mousemove', function(e) { 1292 | if (!that._mousedown) return; 1293 | var x = e.pageX; 1294 | var y = e.pageY; 1295 | e = new enchant.Event('touchmove'); 1296 | e.identifier = game._mousedownID; 1297 | e._initPosition(x, y); 1298 | that.dispatchEvent(e); 1299 | }, false); 1300 | game._element.addEventListener('mouseup', function(e) { 1301 | if (!that._mousedown) return; 1302 | var x = e.pageX; 1303 | var y = e.pageY; 1304 | e = new enchant.Event('touchend'); 1305 | e.identifier = game._mousedownID; 1306 | e._initPosition(x, y); 1307 | that.dispatchEvent(e); 1308 | that._mousedown = false; 1309 | }, false); 1310 | } 1311 | }, 1312 | /** 1313 | * Entityの横幅. 1314 | * @type {Number} 1315 | */ 1316 | width: { 1317 | get: function() { 1318 | return this._width; 1319 | }, 1320 | set: function(width) { 1321 | this._style.width = (this._width = width) + 'px'; 1322 | } 1323 | }, 1324 | /** 1325 | * Entityの高さ. 1326 | * @type {Number} 1327 | */ 1328 | height: { 1329 | get: function() { 1330 | return this._height; 1331 | }, 1332 | set: function(height) { 1333 | this._style.height = (this._height = height) + 'px'; 1334 | } 1335 | }, 1336 | /** 1337 | * Entityの背景色. 1338 | * CSSの'color'プロパティと同様の形式で指定できる. 1339 | * @type {String} 1340 | */ 1341 | backgroundColor: { 1342 | get: function() { 1343 | return this._backgroundColor; 1344 | }, 1345 | set: function(color) { 1346 | this._element.style.backgroundColor = this._backgroundColor = color; 1347 | } 1348 | }, 1349 | /** 1350 | * Entityの透明度. 1351 | * 0から1までの値を設定する(0が完全な透明, 1が完全な不透明). 1352 | * @type {Number} 1353 | */ 1354 | opacity: { 1355 | get: function() { 1356 | return this._opacity; 1357 | }, 1358 | set: function(opacity) { 1359 | this._style.opacity = this._opacity = opacity; 1360 | } 1361 | }, 1362 | /** 1363 | * Entityを表示するかどうかを指定する. 1364 | * @type {Boolean} 1365 | */ 1366 | visible: { 1367 | get: function() { 1368 | return this._visible; 1369 | }, 1370 | set: function(visible) { 1371 | if (this._visible = visible) { 1372 | this._style.display = 'block'; 1373 | } else { 1374 | this._style.display = 'none'; 1375 | } 1376 | } 1377 | }, 1378 | /** 1379 | * Entityのタッチを有効にするかどうかを指定する. 1380 | * @type {Boolean} 1381 | */ 1382 | touchEnabled: { 1383 | get: function() { 1384 | return this._touchEnabled; 1385 | }, 1386 | set: function(enabled) { 1387 | if (this._touchEnabled = enabled) { 1388 | this._style.pointerEvents = 'all'; 1389 | } else { 1390 | this._style.pointerEvents = 'none'; 1391 | } 1392 | } 1393 | }, 1394 | /** 1395 | * Entityの矩形が交差しているかどうかにより衝突判定を行う. 1396 | * @param {*} other 衝突判定を行うEntityなどx, y, width, heightプロパティを持ったObject. 1397 | * @return {Boolean} 衝突判定の結果. 1398 | */ 1399 | intersect: function(other) { 1400 | return this.x < other.x + other.width && other.x < this.x + this.width && 1401 | this.y < other.y + other.height && other.y < this.y + this.height; 1402 | }, 1403 | /** 1404 | * Entityの中心点どうしの距離により衝突判定を行う. 1405 | * @param {*} other 衝突判定を行うEntityなどx, y, width, heightプロパティを持ったObject. 1406 | * @param {Number} [distance] 衝突したと見なす最大の距離. デフォルト値は二つのEntityの横幅と高さの平均. 1407 | * @return {Boolean} 衝突判定の結果. 1408 | */ 1409 | within: function(other, distance) { 1410 | if (distance == null) { 1411 | distance = (this.width + this.height + other.width + other.height) / 4; 1412 | } 1413 | var _; 1414 | return (_ = this.x - other.x + (this.width*this.scaleX - other.width*other.scaleY) / 2) * _ + 1415 | (_ = this.y - other.y + (this.height*this.scaleY - other.height*other.scaleY) / 2) * _ < distance * distance; 1416 | } 1417 | }); 1418 | 1419 | /** 1420 | * @scope enchant.Sprite.prototype 1421 | */ 1422 | enchant.Sprite = enchant.Class.create(enchant.Entity, { 1423 | /** 1424 | * 画像表示機能を持ったクラス. 1425 | * 1426 | * @example 1427 | * var bear = new Sprite(32, 32); 1428 | * bear.image = game.assets['chara1.gif']; 1429 | * 1430 | * @param {Number} [width] Spriteの横幅. 1431 | * @param {Number} [height] Spriteの高さ. 1432 | * @constructs 1433 | * @extends enchant.Entity 1434 | */ 1435 | initialize: function(width, height) { 1436 | enchant.Entity.call(this); 1437 | 1438 | this.width = width; 1439 | this.height = height; 1440 | this._scaleX = 1; 1441 | this._scaleY = 1; 1442 | this._rotation = 0; 1443 | this._dirty = false; 1444 | this._image = null; 1445 | this._frame = 0; 1446 | 1447 | this._style.overflow = 'hidden'; 1448 | 1449 | this.addEventListener('render', function() { 1450 | if (this._dirty) { 1451 | this._style[VENDER_PREFIX + 'Transform'] = [ 1452 | 'rotate(', this._rotation, 'deg)', 1453 | 'scale(', this._scaleX, ',', this._scaleY, ')' 1454 | ].join(''); 1455 | this._dirty = false; 1456 | } 1457 | }); 1458 | }, 1459 | /** 1460 | * Spriteで表示する画像. 1461 | * @type {enchant.Surface} 1462 | */ 1463 | image: { 1464 | get: function() { 1465 | return this._image; 1466 | }, 1467 | set: function(image) { 1468 | if (image == this._image) return; 1469 | 1470 | if (this._image != null) { 1471 | if (this._image.css) { 1472 | this._style.backgroundImage = ''; 1473 | } else if (this._element.firstChild) { 1474 | this._element.removeChild(this._element.firstChild); 1475 | if (this._dirtyListener) { 1476 | this.removeEventListener('render', this._dirtyListener); 1477 | this._dirtyListener = null; 1478 | } else { 1479 | this._image._parent = null; 1480 | } 1481 | } 1482 | } 1483 | 1484 | if (image != null) { 1485 | if (image._css) { 1486 | this._style.backgroundImage = image._css; 1487 | } else if (image._parent) { 1488 | var canvas = document.createElement('canvas'); 1489 | var context = canvas.getContext('2d'); 1490 | canvas.width = image.width; 1491 | canvas.height = image.height; 1492 | context.drawImage(image._element, 0, 0); 1493 | this._dirtyListener = function() { 1494 | if (image._dirty) { 1495 | context.drawImage(image._element); 1496 | image._dirty = false; 1497 | } 1498 | }; 1499 | this.addEventListener('render', this._dirtyListener); 1500 | this._element.appendChild(canvas); 1501 | } else { 1502 | image._parent = this; 1503 | this._element.appendChild(image._element); 1504 | } 1505 | } 1506 | 1507 | this._image = image; 1508 | } 1509 | }, 1510 | /** 1511 | * 表示するフレームのインデックス. 1512 | * Spriteと同じ横幅と高さを持ったフレームがimageプロパティの画像に左上から順に 1513 | * 配列されていると見て, 0から始まるインデックスを指定することでフレームを切り替える. 1514 | * @type {Number} 1515 | */ 1516 | frame: { 1517 | get: function() { 1518 | return this._frame; 1519 | }, 1520 | set: function(frame) { 1521 | this._frame = frame; 1522 | var row = this._image.width / this._width | 0; 1523 | if (this._image._css) { 1524 | this._style.backgroundPosition = [ 1525 | -(frame % row) * this._width, 'px ', 1526 | -(frame / row | 0) * this._height, 'px' 1527 | ].join(''); 1528 | } else if (this._element.firstChild) { 1529 | var style = this._element.firstChild.style; 1530 | style.left = -(frame % row) * this._width + 'px'; 1531 | style.top = -(frame / row | 0) * this._height + 'px'; 1532 | } 1533 | } 1534 | }, 1535 | /** 1536 | * Spriteを拡大縮小する. 1537 | * @param {Number} x 拡大するx軸方向の倍率. 1538 | * @param {Number} [y] 拡大するy軸方向の倍率. 1539 | */ 1540 | scale: function(x, y) { 1541 | if (y == null) y = x; 1542 | this._scaleX *= x; 1543 | this._scaleY *= y; 1544 | this._dirty = true; 1545 | }, 1546 | /** 1547 | * Spriteを回転する. 1548 | * @param {Number} deg 回転する角度 (度数法). 1549 | */ 1550 | rotate: function(deg) { 1551 | this._rotation += deg; 1552 | this._dirty = true; 1553 | }, 1554 | /** 1555 | * Spriteのx軸方向の倍率. 1556 | * @type {Number} 1557 | */ 1558 | scaleX: { 1559 | get: function() { 1560 | return this._scaleX; 1561 | }, 1562 | set: function(scaleX) { 1563 | this._scaleX = scaleX; 1564 | this._dirty = true; 1565 | } 1566 | }, 1567 | /** 1568 | * Spriteのy軸方向の倍率. 1569 | * @type {Number} 1570 | */ 1571 | scaleY: { 1572 | get: function() { 1573 | return this._scaleY; 1574 | }, 1575 | set: function(scaleY) { 1576 | this._scaleY = scaleY; 1577 | this._dirty = true; 1578 | } 1579 | }, 1580 | /** 1581 | * Spriteの回転角 (度数法). 1582 | * @type {Number} 1583 | */ 1584 | rotation: { 1585 | get: function() { 1586 | return this._rotation; 1587 | }, 1588 | set: function(rotation) { 1589 | this._rotation = rotation; 1590 | this._dirty = true; 1591 | } 1592 | } 1593 | }); 1594 | 1595 | /** 1596 | * @scope enchant.Label.prototype 1597 | */ 1598 | enchant.Label = enchant.Class.create(enchant.Entity, { 1599 | /** 1600 | * Labelオブジェクトを作成する. 1601 | * @constructs 1602 | * @extends enchant.Entity 1603 | */ 1604 | initialize: function(text) { 1605 | enchant.Entity.call(this); 1606 | 1607 | this.width = 300; 1608 | this.text = text; 1609 | }, 1610 | /** 1611 | * 表示するテキスト. 1612 | * @type {String} 1613 | */ 1614 | text: { 1615 | get: function() { 1616 | return this._element.innerHTML; 1617 | }, 1618 | set: function(text) { 1619 | this._element.innerHTML = text; 1620 | } 1621 | }, 1622 | /** 1623 | * フォントの指定. 1624 | * CSSの'font'プロパティと同様の形式で指定できる. 1625 | * @type {String} 1626 | */ 1627 | font: { 1628 | get: function() { 1629 | return this._style.font; 1630 | }, 1631 | set: function(font) { 1632 | this._style.font = font; 1633 | } 1634 | }, 1635 | /** 1636 | * 文字色の指定. 1637 | * CSSの'color'プロパティと同様の形式で指定できる. 1638 | * @type {String} 1639 | */ 1640 | color: { 1641 | get: function() { 1642 | return this._style.color; 1643 | }, 1644 | set: function(color) { 1645 | this._style.color = color; 1646 | } 1647 | } 1648 | }); 1649 | 1650 | /** 1651 | * @scope enchant.Map.prototype 1652 | */ 1653 | enchant.Map = enchant.Class.create(enchant.Entity, { 1654 | /** 1655 | * タイルセットからマップを生成して表示するクラス. 1656 | * 1657 | * @param {Number} tileWidth タイルの横幅. 1658 | * @param {Number} tileHeight タイルの高さ. 1659 | * @constructs 1660 | * @extends enchant.Entity 1661 | */ 1662 | initialize: function(tileWidth, tileHeight) { 1663 | enchant.Entity.call(this); 1664 | 1665 | var canvas = document.createElement('canvas'); 1666 | if (RETINA_DISPLAY && game.scale == 2) { 1667 | canvas.width = game.width * 2; 1668 | canvas.height = game.height * 2; 1669 | this._style.webkitTransformOrigin = '0 0'; 1670 | this._style.webkitTransform = 'scale(0.5)'; 1671 | } else { 1672 | canvas.width = game.width; 1673 | canvas.height = game.height; 1674 | } 1675 | this._element.appendChild(canvas); 1676 | this._context = canvas.getContext('2d'); 1677 | 1678 | this._tileWidth = tileWidth || 0; 1679 | this._tileHeight = tileHeight || 0; 1680 | this._image = null; 1681 | this._data = [[[]]]; 1682 | this._dirty = false; 1683 | this._tight = false; 1684 | 1685 | this.touchEnabled = false; 1686 | 1687 | /** 1688 | * タイルが衝突判定を持つかを表す値の二元配列. 1689 | * @type {Array.>} 1690 | */ 1691 | this.collisionData = null; 1692 | 1693 | this._listeners['render'] = null; 1694 | this.addEventListener('render', function() { 1695 | if (this._dirty || this._previousOffsetX == null) { 1696 | this._dirty = false; 1697 | this.redraw(0, 0, game.width, game.height); 1698 | } else if (this._offsetX != this._previousOffsetX || 1699 | this._offsetY != this._previousOffsetY) { 1700 | if (this._tight) { 1701 | var x = -this._offsetX; 1702 | var y = -this._offsetY; 1703 | var px = -this._previousOffsetX; 1704 | var py = -this._previousOffsetY; 1705 | var w1 = x - px + game.width; 1706 | var w2 = px - x + game.width; 1707 | var h1 = y - py + game.height; 1708 | var h2 = py - y + game.height; 1709 | if (w1 > this._tileWidth && w2 > this._tileWidth && 1710 | h1 > this._tileHeight && h2 > this._tileHeight) { 1711 | var sx, sy, dx, dy, sw, sh; 1712 | if (w1 < w2) { 1713 | sx = 0; 1714 | dx = px - x; 1715 | sw = w1; 1716 | } else { 1717 | sx = x - px; 1718 | dx = 0; 1719 | sw = w2; 1720 | } 1721 | if (h1 < h2) { 1722 | sy = 0; 1723 | dy = py - y; 1724 | sh = h1; 1725 | } else { 1726 | sy = y - py; 1727 | dy = 0; 1728 | sh = h2; 1729 | } 1730 | 1731 | if (game._buffer == null) { 1732 | game._buffer = document.createElement('canvas'); 1733 | game._buffer.width = this._context.canvas.width; 1734 | game._buffer.height = this._context.canvas.height; 1735 | } 1736 | var context = game._buffer.getContext('2d'); 1737 | if (this._doubledImage) { 1738 | context.clearRect(0, 0, sw*2, sh*2); 1739 | context.drawImage(this._context.canvas, 1740 | sx*2, sy*2, sw*2, sh*2, 0, 0, sw*2, sh*2); 1741 | context = this._context; 1742 | context.clearRect(dx*2, dy*2, sw*2, sh*2); 1743 | context.drawImage(game._buffer, 1744 | 0, 0, sw*2, sh*2, dx*2, dy*2, sw*2, sh*2); 1745 | } else { 1746 | context.clearRect(0, 0, sw, sh); 1747 | context.drawImage(this._context.canvas, 1748 | sx, sy, sw, sh, 0, 0, sw, sh); 1749 | context = this._context; 1750 | context.clearRect(dx, dy, sw, sh); 1751 | context.drawImage(game._buffer, 1752 | 0, 0, sw, sh, dx, dy, sw, sh); 1753 | } 1754 | 1755 | if (dx == 0) { 1756 | this.redraw(sw, 0, game.width - sw, game.height); 1757 | } else { 1758 | this.redraw(0, 0, game.width - sw, game.height); 1759 | } 1760 | if (dy == 0) { 1761 | this.redraw(0, sh, game.width, game.height - sh); 1762 | } else { 1763 | this.redraw(0, 0, game.width, game.height - sh); 1764 | } 1765 | } else { 1766 | this.redraw(0, 0, game.width, game.height); 1767 | } 1768 | } else { 1769 | this.redraw(0, 0, game.width, game.height); 1770 | } 1771 | } 1772 | this._previousOffsetX = this._offsetX; 1773 | this._previousOffsetY = this._offsetY; 1774 | }); 1775 | }, 1776 | /** 1777 | * データを設定する. 1778 | * タイルががimageプロパティの画像に左上から順に配列されていると見て, 0から始まる 1779 | * インデックスの二元配列を設定する.複数指定された場合は後のものから順に表示される. 1780 | * @param {...Array>} data タイルのインデックスの二元配列. 複数指定できる. 1781 | */ 1782 | loadData: function(data) { 1783 | this._data = Array.prototype.slice.apply(arguments); 1784 | this._dirty = true; 1785 | 1786 | this._tight = false; 1787 | for (var i = 0, len = this._data.length; i < len; i++) { 1788 | var c = 0; 1789 | var data = this._data[i]; 1790 | for (var y = 0, l = data.length; y < l; y++) { 1791 | for (var x = 0, ll = data[y].length; x < ll; x++) { 1792 | if (data[y][x] >= 0) c++; 1793 | } 1794 | } 1795 | if (c / (data.length * data[0].length) > 0.2) { 1796 | this._tight = true; 1797 | break; 1798 | } 1799 | } 1800 | }, 1801 | /** 1802 | * Map上に障害物があるかどうかを判定する. 1803 | * @param {Number} x 判定を行うマップ上の点のx座標. 1804 | * @param {Number} y 判定を行うマップ上の点のy座標. 1805 | * @return {Boolean} 障害物があるかどうか. 1806 | */ 1807 | hitTest: function(x, y) { 1808 | if (x < 0 || this.width <= x || y < 0 || this.height <= y) { 1809 | return false; 1810 | } 1811 | var width = this._image.width; 1812 | var height = this._image.height; 1813 | var tileWidth = this._tileWidth || width; 1814 | var tileHeight = this._tileHeight || height; 1815 | x = x / tileWidth | 0; 1816 | y = y / tileHeight | 0; 1817 | if (this.collisionData != null) { 1818 | return this.collisionData[y] && !!this.collisionData[y][x]; 1819 | } else { 1820 | for (var i = 0, len = this._data.length; i < len; i++) { 1821 | var data = this._data[i]; 1822 | var n; 1823 | if (data[y] != null && (n = data[y][x]) != null && 1824 | 0 <= n && n < (width / tileWidth | 0) * (height / tileHeight | 0)) { 1825 | return true; 1826 | } 1827 | } 1828 | return false; 1829 | } 1830 | }, 1831 | /** 1832 | * Mapで表示するタイルセット画像. 1833 | * @type {enchant.Surface} 1834 | */ 1835 | image: { 1836 | get: function() { 1837 | return this._image; 1838 | }, 1839 | set: function(image) { 1840 | this._image = image; 1841 | if (RETINA_DISPLAY && game.scale == 2) { 1842 | var img = new Surface(image.width * 2, image.height * 2); 1843 | var tileWidth = this._tileWidth || image.width; 1844 | var tileHeight = this._tileHeight || image.height; 1845 | var row = image.width / tileWidth | 0; 1846 | var col = image.height / tileHeight | 0; 1847 | for (var y = 0; y < col; y++) { 1848 | for (var x = 0; x < row; x++) { 1849 | img.draw(image, x * tileWidth, y * tileHeight, tileWidth, tileHeight, 1850 | x * tileWidth * 2, y * tileHeight * 2, tileWidth * 2, tileHeight * 2); 1851 | } 1852 | } 1853 | this._doubledImage = img; 1854 | } 1855 | this._dirty = true; 1856 | } 1857 | }, 1858 | /** 1859 | * Mapのタイルの横幅. 1860 | * @type {Number} 1861 | */ 1862 | tileWidth: { 1863 | get: function() { 1864 | return this._tileWidth; 1865 | }, 1866 | set: function(tileWidth) { 1867 | this._tileWidth = tileWidth; 1868 | this._dirty = true; 1869 | } 1870 | }, 1871 | /** 1872 | * Mapのタイルの高さ. 1873 | * @type {Number} 1874 | */ 1875 | tileHeight: { 1876 | get: function() { 1877 | return this._tileHeight; 1878 | }, 1879 | set: function(tileHeight) { 1880 | this._tileHeight = tileHeight; 1881 | this._dirty = true; 1882 | } 1883 | }, 1884 | /** 1885 | * @private 1886 | */ 1887 | width: { 1888 | get: function() { 1889 | return this._tileWidth * this._data[0][0].length 1890 | } 1891 | }, 1892 | /** 1893 | * @private 1894 | */ 1895 | height: { 1896 | get: function() { 1897 | return this._tileHeight * this._data[0].length 1898 | } 1899 | }, 1900 | /** 1901 | * @private 1902 | */ 1903 | redraw: function(x, y, width, height) { 1904 | if (this._image == null) { 1905 | return; 1906 | } 1907 | 1908 | var image, tileWidth, tileHeight, dx, dy; 1909 | if (this._doubledImage) { 1910 | image = this._doubledImage; 1911 | tileWidth = this._tileWidth * 2; 1912 | tileHeight = this._tileHeight * 2; 1913 | dx = -this._offsetX * 2; 1914 | dy = -this._offsetY * 2; 1915 | x *= 2; 1916 | y *= 2; 1917 | width *= 2; 1918 | height *= 2; 1919 | } else { 1920 | image = this._image; 1921 | tileWidth = this._tileWidth; 1922 | tileHeight = this._tileHeight; 1923 | dx = -this._offsetX; 1924 | dy = -this._offsetY; 1925 | } 1926 | var row = image.width / tileWidth | 0; 1927 | var col = image.height / tileHeight | 0; 1928 | var left = Math.max((x + dx) / tileWidth | 0, 0); 1929 | var top = Math.max((y + dy) / tileHeight | 0, 0); 1930 | var right = Math.ceil((x + dx + width) / tileWidth); 1931 | var bottom = Math.ceil((y + dy + height) / tileHeight); 1932 | 1933 | var source = image._element; 1934 | var context = this._context; 1935 | var canvas = context.canvas; 1936 | context.clearRect(x, y, width, height); 1937 | for (var i = 0, len = this._data.length; i < len; i++) { 1938 | var data = this._data[i]; 1939 | var r = Math.min(right, data[0].length); 1940 | var b = Math.min(bottom, data.length); 1941 | for (y = top; y < b; y++) { 1942 | for (x = left; x < r; x++) { 1943 | var n = data[y][x]; 1944 | if (0 <= n && n < row * col) { 1945 | var sx = (n % row) * tileWidth; 1946 | var sy = (n / row | 0) * tileHeight; 1947 | context.drawImage(source, sx, sy, tileWidth, tileHeight, 1948 | x * tileWidth - dx, y * tileHeight - dy, tileWidth, tileHeight); 1949 | } 1950 | } 1951 | } 1952 | } 1953 | } 1954 | }); 1955 | 1956 | /** 1957 | * @scope enchant.Group.prototype 1958 | */ 1959 | enchant.Group = enchant.Class.create(enchant.Node, { 1960 | /** 1961 | * 複数のNodeを子に持つことができるクラス. 1962 | * 1963 | * @example 1964 | * var stage = new Group(); 1965 | * stage.addChild(player); 1966 | * stage.addChild(enemy); 1967 | * stage.addChild(map); 1968 | * stage.addEventListener('enterframe', function() { 1969 | * // playerの座標に従って全体をスクロールする 1970 | * if (this.x > 64 - player.x) { 1971 | * this.x = 64 - player.x; 1972 | * } 1973 | * }); 1974 | * 1975 | * @constructs 1976 | * @extends enchant.Node 1977 | */ 1978 | initialize: function() { 1979 | enchant.Node.call(this); 1980 | 1981 | /** 1982 | * 子のNode. 1983 | * @type {Array.} 1984 | */ 1985 | this.childNodes = []; 1986 | 1987 | this._x = 0; 1988 | this._y = 0; 1989 | }, 1990 | /** 1991 | * GroupにNodeを追加する. 1992 | * @param {enchant.Node} node 追加するNode. 1993 | */ 1994 | addChild: function(node) { 1995 | this.childNodes.push(node); 1996 | node.parentNode = this; 1997 | node.dispatchEvent(new enchant.Event('added')); 1998 | if (this.scene) { 1999 | var e = new enchant.Event('addedtoscene'); 2000 | node.scene = this.scene; 2001 | node.dispatchEvent(e); 2002 | node._updateCoordinate(); 2003 | 2004 | var fragment = document.createDocumentFragment(); 2005 | var nodes; 2006 | var push = Array.prototype.push; 2007 | if (node._element) { 2008 | fragment.appendChild(node._element); 2009 | } else if (node.childNodes) { 2010 | nodes = node.childNodes.slice().reverse(); 2011 | while (nodes.length) { 2012 | node = nodes.pop(); 2013 | node.scene = this.scene; 2014 | node.dispatchEvent(e); 2015 | if (node._element) { 2016 | fragment.appendChild(node._element); 2017 | } else if (node.childNodes) { 2018 | push.apply(nodes, node.childNodes.reverse()); 2019 | } 2020 | } 2021 | } 2022 | if (!fragment.childNodes.length) return; 2023 | 2024 | var nextSibling, thisNode = this; 2025 | while (thisNode.parentNode) { 2026 | nodes = thisNode.parentNode.childNodes; 2027 | nodes = nodes.slice(nodes.indexOf(thisNode) + 1).reverse(); 2028 | while (nodes.length) { 2029 | node = nodes.pop(); 2030 | if (node._element) { 2031 | nextSibling = node._element; 2032 | break; 2033 | } else if (node.childNodes) { 2034 | push.apply(nodes, node.childNodes.slice().reverse()); 2035 | } 2036 | } 2037 | thisNode = thisNode.parentNode; 2038 | } 2039 | if (nextSibling) { 2040 | this.scene._element.insertBefore(fragment, nextSibling); 2041 | } else { 2042 | this.scene._element.appendChild(fragment); 2043 | } 2044 | } 2045 | }, 2046 | /** 2047 | * GroupにNodeを挿入する. 2048 | * @param {enchant.Node} node 挿入するNode. 2049 | * @param {enchant.Node} reference 挿入位置の前にあるNode. 2050 | */ 2051 | insertBefore: function(node, reference) { 2052 | var i = this.childNodes.indexOf(reference); 2053 | if (i != -1) { 2054 | this.childNodes.splice(i, 0, node); 2055 | node.parentNode = this; 2056 | node.dispatchEvent(new enchant.Event('added')); 2057 | if (this.scene) { 2058 | var e = new enchant.Event('addedtoscene'); 2059 | node.scene = this.scene; 2060 | node.dispatchEvent(e); 2061 | node._updateCoordinate(); 2062 | 2063 | var fragment = document.createDocumentFragment(); 2064 | var nodes; 2065 | var push = Array.prototype.push; 2066 | if (node._element) { 2067 | fragment.appendChild(node._element); 2068 | } else if (node.childNodes) { 2069 | nodes = node.childNodes.slice().reverse(); 2070 | while (nodes.length) { 2071 | node = nodes.pop(); 2072 | node.scene = this.scene; 2073 | node.dispatchEvent(e); 2074 | if (node._element) { 2075 | fragment.appendChild(node._element); 2076 | } else if (node.childNodes) { 2077 | push.apply(nodes, node.childNodes.reverse()); 2078 | } 2079 | } 2080 | } 2081 | if (!fragment.childNodes.length) return; 2082 | 2083 | var nextSibling, thisNode = reference; 2084 | while (thisNode.parentNode) { 2085 | if (i != null) { 2086 | nodes = this.childNodes.slice(i+1).reverse(); 2087 | i = null; 2088 | } else { 2089 | nodes = thisNode.parentNode.childNodes; 2090 | nodes = nodes.slice(nodes.indexOf(thisNode) + 1).reverse(); 2091 | } 2092 | while (nodes.length) { 2093 | node = nodes.pop(); 2094 | if (node._element) { 2095 | nextSibling = node._element; 2096 | break; 2097 | } else if (node.childNodes) { 2098 | push.apply(nodes, node.childNodes.slice().reverse()); 2099 | } 2100 | } 2101 | thisNode = thisNode.parentNode; 2102 | } 2103 | if (nextSibling) { 2104 | this.scene._element.insertBefore(fragment, nextSibling); 2105 | } else { 2106 | this.scene._element.appendChild(fragment); 2107 | } 2108 | } 2109 | } else { 2110 | this.addChild(node); 2111 | } 2112 | }, 2113 | /** 2114 | * GroupからNodeを削除する. 2115 | * @param {enchant.Node} node 削除するNode. 2116 | */ 2117 | removeChild: function(node) { 2118 | var i = this.childNodes.indexOf(node); 2119 | if (i != -1) { 2120 | this.childNodes.splice(i, 1); 2121 | } else { 2122 | return; 2123 | } 2124 | node.parentNode = null; 2125 | node.dispatchEvent(new enchant.Event('removed')); 2126 | if (this.scene) { 2127 | var e = new enchant.Event('removedfromscene'); 2128 | node.scene = null; 2129 | node.dispatchEvent(e); 2130 | if (node._element) { 2131 | this.scene._element.removeChild(node._element); 2132 | } else if (node.childNodes) { 2133 | var nodes = node.childNodes.slice(); 2134 | var push = Array.prototype.push; 2135 | while (nodes.length) { 2136 | node = nodes.pop(); 2137 | node.scene = null; 2138 | node.dispatchEvent(e); 2139 | if (node._element) { 2140 | this.scene._element.removeChild(node._element); 2141 | } else if (node.childNodes) { 2142 | push.apply(nodes, node.childNodes); 2143 | } 2144 | } 2145 | } 2146 | } 2147 | }, 2148 | /** 2149 | * 最初の子Node. 2150 | * @type {enchant.Node} 2151 | */ 2152 | firstChild: { 2153 | get: function() { 2154 | return this.childNodes[0]; 2155 | } 2156 | }, 2157 | /** 2158 | * 最後の子Node. 2159 | * @type {enchant.Node} 2160 | */ 2161 | lastChild: { 2162 | get: function() { 2163 | return this.childNodes[this.childNodes.length-1]; 2164 | } 2165 | }, 2166 | _updateCoordinate: function() { 2167 | if (this.parentNode) { 2168 | this._offsetX = this.parentNode._offsetX + this._x; 2169 | this._offsetY = this.parentNode._offsetY + this._y; 2170 | } else { 2171 | this._offsetX = this._x; 2172 | this._offsetY = this._y; 2173 | } 2174 | for (var i = 0, len = this.childNodes.length; i < len; i++) { 2175 | this.childNodes[i]._updateCoordinate(); 2176 | } 2177 | } 2178 | }); 2179 | 2180 | /** 2181 | * @scope enchant.Scene.prototype 2182 | */ 2183 | enchant.Scene = enchant.Class.create(enchant.Group, { 2184 | /** 2185 | * 表示オブジェクトツリーのルートになるクラス. 2186 | * 2187 | * @example 2188 | * var scene = new Scene(); 2189 | * scene.addChild(player); 2190 | * scene.addChild(enemy); 2191 | * game.pushScene(scene); 2192 | * 2193 | * @constructs 2194 | * @extends enchant.Group 2195 | */ 2196 | initialize: function() { 2197 | enchant.Group.call(this); 2198 | 2199 | this._element = document.createElement('div'); 2200 | this._element.style.position = 'absolute'; 2201 | this._element.style.overflow = 'hidden'; 2202 | this._element.style.width = (this.width = game.width) + 'px'; 2203 | this._element.style.height = (this.height = game.height) + 'px'; 2204 | this._element.style[VENDER_PREFIX + 'TransformOrigin'] = '0 0'; 2205 | this._element.style[VENDER_PREFIX + 'Transform'] = 'scale(' + game.scale + ')'; 2206 | 2207 | this.scene = this; 2208 | 2209 | var that = this; 2210 | if (TOUCH_ENABLED) { 2211 | this._element.addEventListener('touchstart', function(e) { 2212 | var touches = e.touches; 2213 | for (var i = 0, len = touches.length; i < len; i++) { 2214 | e = new enchant.Event('touchstart'); 2215 | e.identifier = touches[i].identifier; 2216 | e._initPosition(touches[i].pageX, touches[i].pageY); 2217 | that.dispatchEvent(e); 2218 | } 2219 | }, false); 2220 | this._element.addEventListener('touchmove', function(e) { 2221 | var touches = e.touches; 2222 | for (var i = 0, len = touches.length; i < len; i++) { 2223 | e = new enchant.Event('touchmove'); 2224 | e.identifier = touches[i].identifier; 2225 | e._initPosition(touches[i].pageX, touches[i].pageY); 2226 | that.dispatchEvent(e); 2227 | } 2228 | }, false); 2229 | this._element.addEventListener('touchend', function(e) { 2230 | var touches = e.changedTouches; 2231 | for (var i = 0, len = touches.length; i < len; i++) { 2232 | e = new enchant.Event('touchend'); 2233 | e.identifier = touches[i].identifier; 2234 | e._initPosition(touches[i].pageX, touches[i].pageY); 2235 | that.dispatchEvent(e); 2236 | } 2237 | }, false); 2238 | } else { 2239 | this._element.addEventListener('mousedown', function(e) { 2240 | var x = e.pageX; 2241 | var y = e.pageY; 2242 | e = new enchant.Event('touchstart'); 2243 | e.identifier = game._mousedownID; 2244 | e._initPosition(x, y); 2245 | that.dispatchEvent(e); 2246 | that._mousedown = true; 2247 | }, false); 2248 | game._element.addEventListener('mousemove', function(e) { 2249 | if (!that._mousedown) return; 2250 | var x = e.pageX; 2251 | var y = e.pageY; 2252 | e = new enchant.Event('touchmove'); 2253 | e.identifier = game._mousedownID; 2254 | e._initPosition(x, y); 2255 | that.dispatchEvent(e); 2256 | }, false); 2257 | game._element.addEventListener('mouseup', function(e) { 2258 | if (!that._mousedown) return; 2259 | var x = e.pageX; 2260 | var y = e.pageY; 2261 | e = new enchant.Event('touchend'); 2262 | e.identifier = game._mousedownID; 2263 | e._initPosition(x, y); 2264 | that.dispatchEvent(e); 2265 | that._mousedown = false; 2266 | }, false); 2267 | } 2268 | }, 2269 | /** 2270 | * Sceneの背景色. 2271 | * CSSの'color'プロパティと同様の形式で指定できる. 2272 | * @type {String} 2273 | */ 2274 | backgroundColor: { 2275 | get: function() { 2276 | return this._backgroundColor; 2277 | }, 2278 | set: function(color) { 2279 | this._element.style.backgroundColor = this._backgroundColor = color; 2280 | } 2281 | }, 2282 | _updateCoordinate: function() { 2283 | this._offsetX = this._x; 2284 | this._offsetY = this._y; 2285 | for (var i = 0, len = this.childNodes.length; i < len; i++) { 2286 | this.childNodes[i]._updateCoordinate(); 2287 | } 2288 | } 2289 | }); 2290 | 2291 | var CANVAS_DRAWING_METHODS = [ 2292 | 'putImageData', 'drawImage', 'drawFocusRing', 'fill', 'stroke', 2293 | 'clearRect', 'fillRect', 'strokeRect', 'fillText', 'strokeText' 2294 | ]; 2295 | 2296 | /** 2297 | * @scope enchant.Surface.prototype 2298 | */ 2299 | enchant.Surface = enchant.Class.create(enchant.EventTarget, { 2300 | /** 2301 | * canvas要素をラップしたクラス. 2302 | * 2303 | * SpriteやMapのimageプロパティに設定して表示させることができる. 2304 | * Canvas APIにアクセスしたいときはcontextプロパティを用いる. 2305 | * 2306 | * @example 2307 | * // 円を表示するSpriteを作成する 2308 | * var ball = new Sprite(50, 50); 2309 | * var surface = new Surface(50, 50); 2310 | * surface.context.beginPath(); 2311 | * surface.context.arc(25, 25, 25, 0, Math.PI*2, true); 2312 | * surface.context.fill(); 2313 | * ball.image = surface; 2314 | * 2315 | * @param {Number} width Surfaceの横幅. 2316 | * @param {Number} height Surfaceの高さ. 2317 | * @constructs 2318 | */ 2319 | initialize: function(width, height) { 2320 | enchant.EventTarget.call(this); 2321 | 2322 | /** 2323 | * Surfaceの横幅. 2324 | * @type {Number} 2325 | */ 2326 | this.width = width; 2327 | /** 2328 | * Surfaceの高さ. 2329 | * @type {Number} 2330 | */ 2331 | this.height = height; 2332 | /** 2333 | * Surfaceの描画コンテクスト. 2334 | * @type {CanvasRenderingContext2D} 2335 | */ 2336 | this.context = null; 2337 | 2338 | var id = 'enchant-surface' + game._surfaceID++; 2339 | if (document.getCSSCanvasContext) { 2340 | this.context = document.getCSSCanvasContext('2d', id, width, height); 2341 | this._element = this.context.canvas; 2342 | this._css = '-webkit-canvas(' + id + ')'; 2343 | var context = this.context; 2344 | } else if (document.mozSetImageElement) { 2345 | this._element = document.createElement('canvas'); 2346 | this._element.width = width; 2347 | this._element.height = height; 2348 | this._css = '-moz-element(#' + id + ')'; 2349 | this.context = this._element.getContext('2d'); 2350 | document.mozSetImageElement(id, this._element) 2351 | } else { 2352 | this._element = document.createElement('canvas'); 2353 | this._element.width = width; 2354 | this._element.height = height; 2355 | this._element.style.position = 'absolute'; 2356 | this.context = this._element.getContext('2d'); 2357 | 2358 | CANVAS_DRAWING_METHODS.forEach(function(name) { 2359 | var method = this.context[name]; 2360 | this.context[name] = function() { 2361 | method.apply(this, arguments); 2362 | this._dirty = true; 2363 | } 2364 | }, this); 2365 | } 2366 | }, 2367 | /** 2368 | * Surfaceから1ピクセル取得する. 2369 | * @param {Number} x 取得するピクセルのx座標. 2370 | * @param {Number} y 取得するピクセルのy座標. 2371 | * @return {Array.} ピクセルの情報を[r, g, b, a]の形式で持つ配列. 2372 | */ 2373 | getPixel: function(x, y) { 2374 | return this.context.getImageData(x, y, 1, 1).data; 2375 | }, 2376 | /** 2377 | * Surfaceに1ピクセル設定する. 2378 | * @param {Number} x 設定するピクセルのx座標. 2379 | * @param {Number} y 設定するピクセルのy座標. 2380 | * @param {Number} r 設定するピクセルのrの値. 2381 | * @param {Number} g 設定するピクセルのgの値. 2382 | * @param {Number} b 設定するピクセルのbの値. 2383 | * @param {Number} a 設定するピクセルの透明度. 2384 | */ 2385 | setPixel: function(x, y, r, g, b, a) { 2386 | var pixel = this.context.createImageData(1, 1); 2387 | pixel.data[0] = r; 2388 | pixel.data[1] = g; 2389 | pixel.data[2] = b; 2390 | pixel.data[3] = a; 2391 | this.context.putImageData(pixel, x, y, 1, 1); 2392 | }, 2393 | /** 2394 | * Surfaceの全ピクセルをクリアし透明度0の黒に設定する. 2395 | */ 2396 | clear: function() { 2397 | this.context.clearRect(0, 0, this.width, this.height); 2398 | }, 2399 | /** 2400 | * Surfaceに対して引数で指定されたSurfaceを描画する. 2401 | * 2402 | * Canvas APIのdrawImageをラップしており, 描画する矩形を同様の形式で指定できる. 2403 | * 2404 | * @example 2405 | * var src = game.assets['src.gif']; 2406 | * var dst = new Surface(100, 100); 2407 | * dst.draw(src); // ソースを(0, 0)に描画 2408 | * dst.draw(src, 50, 50); // ソースを(50, 50)に描画 2409 | * // ソースを(50, 50)に縦横30ピクセル分だけ描画 2410 | * dst.draw(src, 50, 50, 30, 30); 2411 | * // ソースの(10, 10)から縦横40ピクセルの領域を(50, 50)に縦横30ピクセルに縮小して描画 2412 | * dst.draw(src, 10, 10, 40, 40, 50, 50, 30, 30); 2413 | * 2414 | * @param {enchant.Surface} image 描画に用いるSurface. 2415 | */ 2416 | draw: function(image) { 2417 | arguments[0] = image = image._element; 2418 | if (arguments.length == 1) { 2419 | this.context.drawImage(image, 0, 0); 2420 | } else { 2421 | this.context.drawImage.apply(this.context, arguments); 2422 | } 2423 | }, 2424 | /** 2425 | * Surfaceを複製する. 2426 | * @return {enchant.Surface} 複製されたSurface. 2427 | */ 2428 | clone: function() { 2429 | var clone = new enchant.Surface(this.width, this.height); 2430 | clone.draw(this); 2431 | return clone; 2432 | } 2433 | }); 2434 | 2435 | /** 2436 | * 画像ファイルを読み込んでSurfaceオブジェクトを作成する. 2437 | * 2438 | * このメソッドによって作成されたSurfaceはimg要素のラップしておりcontextプロパティに 2439 | * アクセスしたりdraw, clear, getPixel, setPixelメソッドなどの呼び出しでCanvas API 2440 | * を使った画像操作を行うことはできない. ただしdrawメソッドの引数とすることはでき, 2441 | * ほかのSurfaceに描画した上で画像操作を行うことはできる(クロスドメインでロードした 2442 | * 場合はピクセルを取得するなど画像操作の一部が制限される). 2443 | * 2444 | * @param {String} src ロードする画像ファイルのパス. 2445 | * @static 2446 | */ 2447 | enchant.Surface.load = function(src) { 2448 | var image = new Image(); 2449 | var surface = Object.create(Surface.prototype, { 2450 | context: { value: null }, 2451 | _css: { value: 'url(' + src + ')' }, 2452 | _element: { value: image } 2453 | }); 2454 | enchant.EventTarget.call(surface); 2455 | image.src = src; 2456 | image.onerror = function() { 2457 | throw new Error('Cannot load an asset: ' + image.src); 2458 | }; 2459 | image.onload = function() { 2460 | surface.width = image.width; 2461 | surface.height = image.height; 2462 | surface.dispatchEvent(new enchant.Event('load')); 2463 | }; 2464 | return surface; 2465 | }; 2466 | 2467 | /** 2468 | * @scope enchant.Sound.prototype 2469 | */ 2470 | enchant.Sound = enchant.Class.create(enchant.EventTarget, { 2471 | /** 2472 | * audio要素をラップしたクラス. 2473 | * 2474 | * MP3ファイルの再生はSafari, Chrome, Firefox, Opera, IEが対応 2475 | * (Firefox, OperaではFlashを経由して再生). WAVEファイルの再生は 2476 | * Safari, Chrome, Firefox, Operaが対応している. ブラウザが音声ファイル 2477 | * のコーデックに対応していない場合は再生されない. 2478 | * 2479 | * コンストラクタではなくenchant.Sound.loadを通じてインスタンスを作成する. 2480 | * 2481 | * @constructs 2482 | */ 2483 | initialize: function() { 2484 | enchant.EventTarget.call(this); 2485 | throw new Error("Illegal Constructor"); 2486 | 2487 | /** 2488 | * Soundの再生時間 (秒). 2489 | * @type {Number} 2490 | */ 2491 | this.duration = 0; 2492 | }, 2493 | /** 2494 | * 再生を開始する. 2495 | */ 2496 | play: function() { 2497 | if (this._element) this._element.play(); 2498 | }, 2499 | /** 2500 | * 再生を中断する. 2501 | */ 2502 | pause: function() { 2503 | if (this._element) this._element.pause(); 2504 | }, 2505 | /** 2506 | * 再生を停止する. 2507 | */ 2508 | stop: function() { 2509 | this.pause(); 2510 | this.currentTime = 0; 2511 | }, 2512 | /** 2513 | * Soundを複製する. 2514 | * @return {enchant.Sound} 複製されたSound. 2515 | */ 2516 | clone: function() { 2517 | var clone; 2518 | if (this._element instanceof Audio) { 2519 | clone = Object.create(enchant.Sound.prototype, { 2520 | _element: { value: this._element.cloneNode(false) }, 2521 | duration: { value: this.duration } 2522 | }); 2523 | } else { 2524 | clone = Object.create(enchant.Sound.prototype); 2525 | } 2526 | enchant.EventTarget.call(clone); 2527 | return clone; 2528 | }, 2529 | /** 2530 | * 現在の再生位置 (秒). 2531 | * @type {Number} 2532 | */ 2533 | currentTime: { 2534 | get: function() { 2535 | return this._element ? this._element.currentTime : 0; 2536 | }, 2537 | set: function(time) { 2538 | if (this._element) this._element.currentTime = time; 2539 | } 2540 | }, 2541 | /** 2542 | * ボリューム. 0 (無音) ~ 1 (フルボリューム). 2543 | * @type {Number} 2544 | */ 2545 | volume: { 2546 | get: function() { 2547 | return this._element ? this._element.volume : 1; 2548 | }, 2549 | set: function(volume) { 2550 | if (this._element) this._element.volume = volume; 2551 | } 2552 | } 2553 | }); 2554 | 2555 | /** 2556 | * 音声ファイルを読み込んでSurfaceオブジェクトを作成する. 2557 | * 2558 | * @param {String} src ロードする音声ファイルのパス. 2559 | * @param {String} [type] 音声ファイルのMIME Type. 2560 | * @static 2561 | */ 2562 | enchant.Sound.load = function(src, type) { 2563 | if (type == null) { 2564 | var ext = src.match(/\.\w+$/)[0]; 2565 | if (ext) { 2566 | type = 'audio/' + ext.slice(1).toLowerCase(); 2567 | } else { 2568 | type = ''; 2569 | } 2570 | } 2571 | type = type.replace('mp3', 'mpeg'); 2572 | 2573 | var sound = Object.create(enchant.Sound.prototype); 2574 | enchant.EventTarget.call(sound); 2575 | var audio = new Audio(); 2576 | if (!enchant.Sound.enabledInMobileSafari && 2577 | VENDER_PREFIX == 'webkit' && TOUCH_ENABLED) { 2578 | window.setTimeout(function() { 2579 | sound.dispatchEvent(new enchant.Event('load')); 2580 | }, 0); 2581 | } else { 2582 | if (audio.canPlayType(type)) { 2583 | audio.src = src; 2584 | audio.load(); 2585 | audio.autoplay = false; 2586 | audio.onerror = function() { 2587 | throw new Error('Cannot load an asset: ' + audio.src); 2588 | }; 2589 | audio.addEventListener('canplaythrough', function() { 2590 | sound.duration = audio.duration; 2591 | sound.dispatchEvent(new enchant.Event('load')); 2592 | }, false); 2593 | sound._element = audio; 2594 | } else if (type == 'audio/mpeg') { 2595 | var embed = document.createElement('embed'); 2596 | var id = 'enchant-audio' + game._soundID++; 2597 | embed.width = embed.height = 1; 2598 | embed.name = id; 2599 | embed.src = 'sound.swf?id=' + id + '&src=' + src; 2600 | embed.allowscriptaccess = 'always'; 2601 | embed.style.position = 'absolute'; 2602 | embed.style.left = '-1px'; 2603 | sound.addEventListener('load', function() { 2604 | Object.defineProperties(embed, { 2605 | currentTime: { 2606 | get: function() { return embed.getCurrentTime() }, 2607 | set: function(time) { embed.setCurrentTime(time) } 2608 | }, 2609 | volume: { 2610 | get: function() { return embed.getVolume() }, 2611 | set: function(volume) { embed.setVolume(volume) } 2612 | } 2613 | }); 2614 | sound._element = embed; 2615 | sound.duration = embed.getDuration(); 2616 | }); 2617 | game._element.appendChild(embed); 2618 | enchant.Sound[id] = sound; 2619 | } else { 2620 | window.setTimeout(function() { 2621 | sound.dispatchEvent(new enchant.Event('load')); 2622 | }, 0); 2623 | } 2624 | } 2625 | return sound; 2626 | }; 2627 | 2628 | enchant.Sound.enabledInMobileSafari = false; 2629 | 2630 | })(); 2631 | --------------------------------------------------------------------------------