Develop and Download Open Source Software

Browse Subversion Repository

Contents of /trunk/src/ts/Game.ts

Parent Directory Parent Directory | Revision Log Revision Log


Revision 170 - (show annotations) (download) (as text)
Mon May 4 03:43:41 2015 UTC (9 years ago) by tsugehara
File MIME type: text/texmacs
File size: 28946 byte(s)


1 ///<reference path="all.ts"/>
2 module jg {
3 /**
4 * メインループなどを管理するゲームエンジン。本クラスを基点にjgame.jsのゲームは実行される。
5 * オーバーライドすることも想定しており、https://github.com/tsugehara/jgengine にいくつかサンプルがある。
6 */
7 export class Game {
8 /** 終了フラグ。_がついているが直接操作してもいい */
9 _exit: boolean;
10 /** 前回の実行時間 */
11 tick: number;
12 /** 前回の描画時間 */
13 renderTick: number;
14 /** KeytypeとkeyCodeの関連付けを行うマップ。JGUtilへの移行を検討中 */
15 keymap: any;
16 /** ドラッグ中情報。マルチタッチサポート後にインターフェース変更の可能性がある */
17 dragParam: InputPointEvent;
18
19 /** 描画オブジェクト */
20 renderer: GameRenderer;
21 /** 管理しているシーン */
22 scenes: Scene[];
23 /** 現在のシーン */
24 scene: Scene;
25 /** 管理しているリソース。リソースは単一インスタンスであるため、Resource.getInstanceと等価であることが保証されている */
26 resource: Resource;
27 /** ゲームの横幅 */
28 width:number;
29 /** ゲームの縦幅 */
30 height:number;
31 /** 表示上の拡大比率 */
32 scale: number;
33 /** 読み込み中シーンのコンストラクタ */
34 loadingSceneClass: any; //これの型指定方法わかんない
35 /** 読み込み中シーン。読み込み中の場合のみ値が設定される */
36 loadingScene: LoadingScene;
37 /** このゲームの内部ID。単一ページに複数のゲームを表示するような場合以外、特に利用する機会は無い */
38 id: number;
39
40 /** 画面描画間隔。指定するとこのFPS以下に画面描画が抑制される */
41 targetFps: number;
42
43 /** FPS表示用のDOM要素。将来的に変更される可能性がある */
44 fps: HTMLElement;
45
46 /** preload処理の完了時に呼び出されるイベント */
47 loaded: Trigger;
48 /**
49 * ゲーム内時間の更新時に呼び出されるイベント。
50 * 基本的なゲーム内更新処理はすべてこのイベントのハンドラで実行するが、精度が不要でかつ定期的に実行するアニメーションのような処理は、addTimerメソッドでの実行でもよい。
51 * 引数として経過時間がミリ秒で渡されるため、その経過時間に則った処理を行う必要がある。
52 * また、解放漏れに注意。
53 */
54 update: Trigger;
55 /**
56 * ゲーム内タイマー
57 */
58 timers: GameTimer[];
59 /**
60 * 描画時に呼び出されるイベント。利用は想定されていないので、通常時はundefined。利用する際は自前でnewする必要がある。
61 * updateとは異なり、経過時間は取得出来ない。
62 */
63 render: Trigger;
64 /**
65 * キーが押された時に呼び出されるイベント。InputKeyboardEventをパラメータとして持つ。
66 * 将来的にインターフェース変更の可能性あり。
67 */
68 keyDown: Trigger;
69 /**
70 * キーが離された時に呼び出されるイベント。InputKeyboardEventをパラメータとして持つ。
71 * 将来的にインターフェース変更の可能性あり。
72 */
73 keyUp: Trigger;
74 /**
75 * ポインティングデバイスが押された時に呼び出されるイベント。InputPointEventをパラメータとして持つ。
76 * 将来的にインターフェース変更の可能性あり。
77 */
78 pointDown: Trigger;
79 /**
80 * ポインティングデバイスが離された時に呼び出されるイベント。InputPointEventをパラメータとして持つ。
81 * 将来的にインターフェース変更の可能性あり。
82 */
83 pointUp: Trigger;
84 /**
85 * ポインティングデバイスが移動された時に呼び出されるイベント。ただし移動だけでは発生せず、必ずpointDownが事前に発生している必要がある。InputPointEventをパラメータとして持つ。
86 * 将来的にインターフェース変更の可能性あり。
87 */
88 pointMove: Trigger;
89 /**
90 * 現在ゲームがフォーカスを持っているかどうか
91 */
92 focus: boolean;
93
94 /**
95 * 発生済みのユーザ入力イベント群。
96 * jgame.jsではメインループ内で入力処理を発火させるため、keydownなどのDOMイベントでいったんここにプールしてから、メインループでイベント発火という手順を踏む。
97 */
98 eventQueue: InputEvent[];
99 /** Enumと各種イベント名のマップ */
100 inputEventMap: any;
101 /** pointDown発生済みかどうかのフラグ */
102 isPointDown: boolean;
103
104 /** このゲームの乱数シード */
105 seed: number;
106 /** このゲームの乱数エンジン */
107 mt: MT;
108 /** 独自イベント */
109 userEvent: jg.Trigger;
110
111 /**
112 * 新しいゲームを生成する。
113 * 現在引数をwidth, heightを廃止しargsのみにする変更が検討されている。
114 * @param width ゲームの横幅
115 * @param height ゲームの縦幅
116 * @param args RenderTransferModeを指定すると、このゲームのRenderTransferModeの変更が可能。HTMLElementを指定すると、DOMコンテナを指定可能。文字列を指定すると、window[文字列]のコンストラクタをRendererに指定する
117 */
118 constructor(width:number, height:number, ...args:any[]) {
119 this._exit = false;
120 this.id = JGUtil.generateId();
121 this.width = width;
122 this.height = height;
123 this.targetFps = 0;
124 this.loadingSceneClass = LoadingScene;
125 this.keymap = {
126 13: Keytype.Enter,
127 27: Keytype.Esc,
128 37: Keytype.Left,
129 38: Keytype.Up,
130 39: Keytype.Right,
131 40: Keytype.Down
132 }
133
134 this.loaded = new Trigger();
135 this.update = new Trigger();
136 this.pointDown = new Trigger();
137 this.pointUp = new Trigger();
138 this.pointMove = new Trigger();
139 this.keyDown = new Trigger();
140 this.keyUp = new Trigger();
141 this.userEvent = new jg.Trigger();
142 this.timers = [];
143
144 this.scene = new Scene(this);
145 this.scenes = [];
146 this.scenes.push(this.scene);
147
148 this.resource = Resource.getInstance();
149
150 var container:HTMLElement, transferMode:RenderTransferMode;
151 for (var i=2; i<arguments.length; i++) {
152 if (arguments[i] instanceof HTMLElement)
153 container = <HTMLElement>arguments[i];
154 else if (typeof arguments[i] == "string") {
155 this.renderer = new (<any>window[arguments[i]])(this, container, transferMode);
156 this.renderer.changeScene(this.scene);
157 } else
158 transferMode = <RenderTransferMode>arguments[i]
159 }
160 if (! this.renderer) {
161 this.renderer = new GameRenderer(this, container, transferMode);
162 this.renderer.changeScene(this.scene);
163 }
164
165 this.eventQueue = [];
166 this.inputEventMap = {}
167 this.inputEventMap[InputEventType.Keyboard] = {};
168 this.inputEventMap[InputEventType.Keyboard][InputEventAction.Down] = "keyDown";
169 this.inputEventMap[InputEventType.Keyboard][InputEventAction.Up] = "keyUp";
170 this.inputEventMap[InputEventType.Point] = {};
171 this.inputEventMap[InputEventType.Point][InputEventAction.Down] = "pointDown";
172 this.inputEventMap[InputEventType.Point][InputEventAction.Move] = "pointMove";
173 this.inputEventMap[InputEventType.Point][InputEventAction.Up] = "pointUp";
174 //this.enableKeyboardHandler();
175 //this.enablePointHandler();
176
177 if (document.getElementById("fps_show"))
178 this.fps = document.getElementById("fps_show");
179
180 this.renderer.handler.addEventListener("focus", () => {
181 this.focus = true;
182 }, false)
183 this.renderer.handler.addEventListener("blur", () => {
184 this.focus = false;
185 }, false)
186 this.focus = true;
187
188 this.setSeed();
189
190 this.main()
191 }
192
193 /**
194 * 乱数シードを指定する。
195 * 再現性のあるゲーム以外で明示的に呼び出す必要はなく、通常は初期化時に自動的に設定される。
196 * @param seed 乱数シード。省略時は自動設定
197 */
198 setSeed(seed?:number) {
199 this.seed = seed === undefined ? new Date().getTime() : seed;
200 this.mt = new MT(seed);
201 }
202
203 /**
204 * 乱数を取得する。再現性のあるゲームを作る場合、Math.randomではなくこちらのrandomを利用する必要がある
205 * @param min 最小値
206 * @param max 最大値
207 */
208 random(min:number, max:number) {
209 return this.mt.nextInt(min, max-min+1);
210 }
211
212 /**
213 * windowのサイズを取得する
214 */
215 getWindowSize() {
216 return {
217 width: document.documentElement.clientWidth,
218 height: document.documentElement.clientHeight
219 };
220 }
221
222 /**
223 * 現在の画面の大きさに合わせて拡大する。
224 * @param no_center trueに設定すると中央寄せにしない
225 */
226 fitToWindow(no_center?:boolean) {
227 var elem = this.renderer.container.parentElement;
228 elem.style.margin = "0";
229 elem.style.padding = "0";
230 elem.style.overflow = "hidden";
231 this.renderer.container.style.margin = "0";
232 this.renderer.container.style.padding = "0";
233
234 var size = this.getWindowSize();
235 this.renderer.container.style.width = size.width+"px";
236 this.renderer.container.style.height = size.height+"px";
237
238 this.scale = Math.min(
239 size.width / this.width,
240 size.height / this.height
241 );
242 var size2 = {
243 width: Math.floor(this.width * this.scale),
244 height: Math.floor(this.height * this.scale)
245 }
246 this.renderer.changeFrontCanvasSize(size2, no_center ? undefined : {
247 x:Math.floor((size.width-size2.width) / 2),
248 y:Math.floor((size.height-size2.height) / 2)
249 });
250 }
251
252 /**
253 * 背景色を設定する。
254 * このメソッドは廃止が検討されている。
255 * @param r 0~255の範囲で赤色値を指定
256 * @param g 0~255の範囲で緑色値を指定
257 * @param b 0~255の範囲で青色値を指定
258 * @param a 0~255の範囲で透明度を指定
259 */
260 setBgColor(r:number, g:number, b:number, a:number) {
261 for (var i=0; i<this.renderer.bg.data.length; i+=4) {
262 this.renderer.bg.data[i] = r;
263 this.renderer.bg.data[i+1] = g;
264 this.renderer.bg.data[i+2] = b;
265 this.renderer.bg.data[i+3] = a;
266 }
267 }
268
269 /**
270 * ゲーム内のすべてのオブジェクトをリフレッシュする。
271 * スタンバイからの復帰時などでcanvasが壊れていても本メソッドでの復旧が可能だが、BufferedRendererなど、破壊されるオブジェクトもある点に注意。
272 */
273 refresh() {
274 this.renderer.refresh();
275 for (var i=0; i<this.scenes.length; i++)
276 this.scenes[i].refresh();
277 }
278
279 /**
280 * 現在実行されている環境でタッチイベントが有効化を判定する。
281 * copied by enchant.js (enchant.ENV.TOUCH_ENABLED)
282 */
283 isTouchEnable() {
284 var div:any = document.createElement('div');
285 div.setAttribute('ontouchstart', 'return');
286 return typeof div.ontouchstart === 'function';
287 }
288
289 /**
290 * マウスなどのDOMイベントからjgame.jsが利用可能なoffset値を取得する
291 */
292 getOffsetByEvent(e:any):CommonOffset {
293 e.offset = {
294 x: e.pageX - this.renderer._pageX,
295 y: e.pageY - this.renderer._pageY
296 }
297 return {
298 x: this.scale ? e.offset.x / this.scale : e.offset.x,
299 y: this.scale ? e.offset.y / this.scale : e.offset.y
300 }
301 }
302
303 /**
304 * DOMのmousedownに対するイベントハンドラ
305 * @param e DOMのMouseEvent
306 */
307 onmousedown(e:MouseEvent) {
308 this.isPointDown = true;
309 this.eventQueue.push(new InputPointEvent(
310 InputEventAction.Down, e, this.getOffsetByEvent(e)
311 ));
312 if (!this.focus)
313 this.renderer.handler.focus();
314 e.preventDefault();
315 }
316 /**
317 * DOMのtouchstartに対するイベントハンドラ。
318 * 現状lib.d.tsに型情報が定義されていないようなので、anyになっている。
319 * @param e DOMのTouchEvent
320 */
321 ontouchstart(e:any) {
322 var touches = e.changedTouches;
323 this.isPointDown = true;
324 for (var i = 0, l = touches.length; i < l; i++)
325 this.eventQueue.push(new InputPointEvent(
326 InputEventAction.Down, touches[i], this.getOffsetByEvent(touches[i])
327 ));
328
329 if (!this.focus)
330 this.renderer.handler.focus();
331
332 e.preventDefault();
333 }
334
335 /**
336 * DOMのmousemoveに対するイベントハンドラ
337 * @param e DOMのMouseEvent
338 */
339 onmousemove(e:MouseEvent) {
340 if (! this.isPointDown)
341 return;
342
343 this.eventQueue.push(new InputPointEvent(
344 InputEventAction.Move, e, this.getOffsetByEvent(e)
345 ));
346
347 e.preventDefault();
348 }
349 /**
350 * DOMのtouchmoveに対するイベントハンドラ。
351 * 現状lib.d.tsに型情報が定義されていないようなので、anyになっている。
352 * @param e DOMのTouchEvent
353 */
354 ontouchmove(e:any) {
355 if (! this.isPointDown)
356 return;
357
358 var touches = e.changedTouches;
359 for (var i = 0, l = touches.length; i < l; i++)
360 this.eventQueue.push(new InputPointEvent(
361 InputEventAction.Move, touches[i], this.getOffsetByEvent(touches[i])
362 ));
363
364 e.preventDefault();
365 }
366
367 /**
368 * DOMのmouseupに対するイベントハンドラ
369 * @param e DOMのMouseEvent
370 */
371 onmouseup(e:MouseEvent) {
372 if (! this.isPointDown)
373 return;
374
375 this.eventQueue.push(new InputPointEvent(
376 InputEventAction.Up, e, this.getOffsetByEvent(e)
377 ));
378
379 this.isPointDown = false;
380
381 e.preventDefault();
382 }
383 /**
384 * DOMのtouchendに対するイベントハンドラ。
385 * 現状lib.d.tsに型情報が定義されていないようなので、anyになっている。
386 * @param e DOMのTouchEvent
387 */
388 ontouchend(e:any) {
389 if (! this.isPointDown)
390 return;
391
392 var touches = e.changedTouches;
393 for (var i = 0, l = touches.length; i < l; i++)
394 this.eventQueue.push(new InputPointEvent(
395 InputEventAction.Up, touches[i], this.getOffsetByEvent(touches[i])
396 ));
397
398 this.isPointDown = false;
399
400 e.preventDefault();
401 }
402
403
404 /**
405 * ポインティングイベントを有効にする。
406 * 無効化処理も実行するため、何度も切り替えるアプリケーションの場合pointDown, pointMove, pointUpのイベントハンドラを独自に復旧する必要がある点に注意。
407 */
408 enablePointHandler() {
409 this.disablePointHandler();
410
411 try {
412 if (this.isTouchEnable()) {
413 this.renderer.handler.addEventListener("touchstart", JGUtil.createIdProxy(this.id, this.ontouchstart, this), false);
414 this.renderer.handler.addEventListener("touchmove" , JGUtil.createIdProxy(this.id, this.ontouchmove, this) , false);
415 this.renderer.handler.addEventListener("touchend" , JGUtil.createIdProxy(this.id, this.ontouchend, this) , false);
416 } else {
417 this.renderer.handler.addEventListener("mousedown" , JGUtil.createIdProxy(this.id, this.onmousedown, this) , false);
418 this.renderer.handler.addEventListener("mousemove" , JGUtil.createIdProxy(this.id, this.onmousemove, this) , false);
419 this.renderer.handler.addEventListener("mouseup" , JGUtil.createIdProxy(this.id, this.onmouseup, this) , false);
420 }
421 } catch (ex) {
422 // ignore error of addEventListener
423 }
424 }
425 /**
426 * ポインティングイベントを無効化する。
427 */
428 disablePointHandler() {
429 this.dragParam = null;
430 try {
431 if (this.isTouchEnable()) {
432 if (JGUtil.getIdProxy(this.id, this.ontouchstart, this)) {
433 this.renderer.handler.removeEventListener("touchstart", JGUtil.getIdProxy(this.id, this.ontouchstart, this), false);
434 this.renderer.handler.removeEventListener("touchmove" , JGUtil.getIdProxy(this.id, this.ontouchmove, this) , false);
435 this.renderer.handler.removeEventListener("touchend" , JGUtil.getIdProxy(this.id, this.ontouchend, this) , false);
436 JGUtil.deleteIdProxy(this.id, this.ontouchstart, this);
437 JGUtil.deleteIdProxy(this.id, this.ontouchmove, this);
438 JGUtil.deleteIdProxy(this.id, this.ontouchend, this);
439 }
440 } else {
441 if (JGUtil.getIdProxy(this.id, this.onmousedown, this)) {
442 this.renderer.handler.removeEventListener("mousedown" , JGUtil.getIdProxy(this.id, this.onmousedown, this) , false);
443 this.renderer.handler.removeEventListener("mousemove" , JGUtil.getIdProxy(this.id, this.onmousemove, this) , false);
444 this.renderer.handler.removeEventListener("mouseup" , JGUtil.getIdProxy(this.id, this.onmouseup, this) , false);
445 JGUtil.deleteIdProxy(this.id, this.onmousedown, this);
446 JGUtil.deleteIdProxy(this.id, this.onmousemove, this);
447 JGUtil.deleteIdProxy(this.id, this.onmouseup, this);
448 }
449 }
450 } catch (ex) {
451 // ignore error of removeEventListener
452 }
453 }
454
455 /**
456 * DOMのkeydownイベントに対するイベントハンドラ。
457 * lib.d.tsにおいて型情報が不明なためanyになっている。KeyboardEventExtensionsでいいのだろうか。
458 * @param e DOMのキーボードイベントパラメータ。内部的にはkeyCodeしか利用していない
459 */
460 onkeydown(e:any) {
461 var keyParam = new InputKeyboardEvent(InputEventAction.Down,this.keymap[e.keyCode], e);
462 this.eventQueue.push(keyParam);
463 if (this.keymap[e.keyCode] != undefined)
464 e.preventDefault();
465 }
466
467 /**
468 * DOMのkeyupイベントに対するイベントハンドラ。
469 * lib.d.tsにおいて型情報が不明なためanyになっている。KeyboardEventExtensionsでいいのだろうか。
470 * @param e DOMのキーボードイベントパラメータ。内部的にはkeyCodeしか利用していない
471 */
472 onkeyup(e:any) {
473 var keyParam = new InputKeyboardEvent(InputEventAction.Up,this.keymap[e.keyCode], e);
474 this.eventQueue.push(keyParam);
475 if (this.keymap[e.keyCode] != undefined)
476 e.preventDefault();
477 }
478
479 /**
480 * キーボードイベントを有効化する。
481 * このイベントを有効にしてしまうと、keymapに登録されているキーコードにpreventDefaultが動くため、textareaなどが存在するページで実行する場合は注意が必要。
482 * また無効化処理も実行するため、何度も切り替えるアプリケーションの場合keyDown, keyUpのイベントハンドラを独自に復旧する必要がある点に注意。
483 */
484 enableKeyboardHandler() {
485 this.disableKeyboardHandler();
486 try {
487 this.renderer.handler.addEventListener("keydown", JGUtil.createIdProxy(this.id, this.onkeydown, this), false);
488 this.renderer.handler.addEventListener("keyup" , JGUtil.createIdProxy(this.id, this.onkeyup, this) , false);
489 } catch(ex) {
490 //ignore error of addEventListener
491 }
492 }
493 /**
494 * キーボードイベントを無効化する。
495 */
496 disableKeyboardHandler() {
497 if (JGUtil.getIdProxy(this.id, this.onkeydown, this)) {
498 this.renderer.handler.removeEventListener("keydown", JGUtil.getIdProxy(this.id, this.onkeydown, this), false);
499 this.renderer.handler.removeEventListener("keyup" , JGUtil.getIdProxy(this.id, this.onkeyup, this) , false);
500 JGUtil.deleteIdProxy(this.id, this.onkeydown, this);
501 JGUtil.deleteIdProxy(this.id, this.onkeyup, this);
502 }
503 }
504
505 /**
506 * 指定した時間間隔で実行するタイマーを追加する。
507 * このタイマーは大体のタイマーであるため、アニメーションなど正確性の不要な作業のみの利用に限定するべきである。
508 * @param wait 実行時間間隔をミリ秒で指定する
509 * @param owner タイマーのコールバックに対するthis
510 * @param handler コールバック
511 */
512 addTimer(wait:number, owner:any, handler:Function) {
513 var timer:GameTimer = null;
514 for (var i=0; i<this.timers.length; i++) {
515 if (this.timers[i].wait == wait) {
516 timer = this.timers[i];
517 break;
518 }
519 }
520 if (timer == null) {
521 timer = new GameTimer(wait);
522 this.timers.push(timer);
523 }
524 timer.trigger.handle(owner, handler);
525 }
526
527 /**
528 * 指定した時間間隔で実行するタイマーのコールバックを削除する。
529 * 一つもコールバックが存在しないタイマー自体の削除処理も行っている。
530 * @param wait 実行時間間隔をミリ秒で指定する
531 * @param owner タイマーのコールバックに対するthis
532 * @param handler コールバック
533 */
534 removeTimer(wait:number, owner:any, handler:Function) {
535 var timer:GameTimer = null;
536 for (var i=0; i<this.timers.length; i++) {
537 if (this.timers[i].wait == wait) {
538 timer = this.timers[i];
539 break;
540 }
541 }
542 if (timer == null)
543 throw "error removeTimer: dont have "+wait+" timer";
544
545 timer.trigger.remove(owner, handler);
546 }
547
548 /**
549 * 指定したオーナーのタイマーに対するコールバックをすべて削除する
550 * @param owner タイマーのコールバックに対するthis
551 */
552 removeTimerAll(owner:any) {
553 for (var i=0; i<this.timers.length; i++) {
554 this.timers[i].trigger.removeAll(owner);
555 }
556 }
557
558 /**
559 * シーンを変更する
560 * @param scene 変更後のシーン
561 * @param effect 変更時にかけるエフェクト。省略時はエフェクト無しになる。通常、EffectTypeの値を指定する
562 * @param endOldScene trueを指定すると、切り替え前に前のシーンを削除する。
563 */
564 changeScene(scene:Scene, effect?:any, endOldScene?:boolean) {
565 if (effect) {
566 var currentScene = this.scene;
567 Effect.sceneEffect(this, currentScene, scene, effect, () => {
568 this.endScene();
569 this.changeScene(scene);
570 }, endOldScene);
571 return;
572 }
573 this.scenes.push(scene);
574 scene.game = this;
575 this.scene.hid.fire();
576 this.scene = scene;
577 this.renderer.changeScene(this.scene);
578 this.scene.started.fire();
579 }
580
581 /**
582 * シーンを終了する
583 * @param effect 変更時にかけるエフェクト。省略時はエフェクト無しになる。通常、EffectTypeの値を指定する
584 */
585 endScene(effect?:any) {
586 if (this.scenes.length == 1) {
587 this.end();
588 return;
589 }
590 if (effect) {
591 Effect.sceneEffect(this, this.scene, this.scenes[this.scenes.length-2], effect, () => {
592 this.endScene();
593 }, true);
594 return;
595 }
596 this.scene.destroy();
597 this.scenes.pop();
598 this.scene.ended.fire();
599 this.scene = this.scenes[this.scenes.length-1];
600 this.renderer.changeScene(this.scene);
601 this.scene.showed.fire();
602 }
603
604 /**
605 * 指定した名前のリソースを取得する。サウンドはsメソッドでの取得である点に注意
606 * @param name リソース名
607 */
608 r(name:string) {
609 return this.resource.get(name);
610 }
611
612 /**
613 * 指定した名前のサウンドリソースを取得する。画像などはrメソッドの取得である点に注意
614 * @param name サウンドリソース名
615 */
616 s(name:string) {
617 return this.resource.sound(name);
618 }
619
620 /**
621 * 事前の読み込み処理を行う。
622 * 配列、オブジェクト、文字列のいずれかが指定可能で、複数回の呼び出しも可能。
623 * 配列の場合、リソース名はURLと同じ扱いになる。
624 * game.preload(["a.png", "b.png", "c.png"])
625 * オブジェクトの場合、リソース名はキーで値がURLとなる。
626 * game.preload({a: "a.png", b: "b.png", c: "c.png"})
627 * 文字列の場合、第二、第三引数などを配列と同じように処理する。
628 * game.preload("a.png", "b.png", "c.png")
629 * @param ary 配列、オブジェクト、文字列のいずれかが指定可能。
630 */
631 preload(ary: any) {
632 if (ary instanceof Array) {
633 for (var i=0; i<ary.length; i++)
634 this.resource.load(ary[i], ary[i]);
635 } else if (typeof ary == "string") {
636 for (var i=0; i<arguments.length; i++)
637 this.resource.load(arguments[i], arguments[i]);
638 } else {
639 for (var j in ary)
640 this.resource.load(j, ary[j]);
641 }
642
643 if (this.loadingSceneClass && !this.loadingScene)
644 this.setLoadingScene(this.loadingSceneClass);
645 }
646
647 /**
648 * 他のライブラリで読み込み中のリソースをjgame.jsに登録する。
649 * @param identity リソースの識別名
650 */
651 preloadOther(identity:string) {
652 this.resource.loadManual(identity);
653 }
654
655 /**
656 * 他のライブラリで読み込み中のリソースが読み込み完了となった事をjgame.jsに通知する
657 * @param identity リソースの識別名
658 */
659 preloadCompleteOther(identity:string) {
660 this.resource.completeManual(identity);
661 }
662
663 /**
664 * 読み込み中シーンを設定し、現在のシーンを切り替える。
665 * 現在既に読み込み中である場合、本メソッドは処理を行わない。
666 */
667 setLoadingScene(scene:any) {
668 if (! this.loadingScene) {
669 if (scene instanceof LoadingScene)
670 this.loadingScene = scene;
671 else
672 this.loadingScene = new scene(this, this.resource);
673
674 this.loadingScene.finished.handle(this, this.preloadComplete);
675 this.changeScene(this.loadingScene);
676 }
677 }
678
679 /**
680 * preloadの完了処理として、loadingSceneフィールドの削除、loadedイベントの発火を行う
681 */
682 preloadComplete() {
683 if (this.loadingScene)
684 delete this.loadingScene;
685 this.loaded.fire();
686 }
687
688 /**
689 * ゲームを終了する。
690 * 実態はメインループの終了のみであり、本処理実行後でも_exitフラグの削除とmainメソッドの再実行によりゲームは再開可能
691 */
692 end() {
693 if (this._exit)
694 return false;
695 this.renderer.render();
696 this._exit = true;
697 return true;
698 }
699
700 /**
701 * ゲームを再開する。
702 * endによって行われたメインループの再起動。
703 */
704 resume() {
705 if (!this._exit)
706 return false;
707 this._exit = false;
708 this.main();
709 return true;
710 }
711
712 /**
713 * ポインティングされたEntityを設定する
714 * @param param 対象のポインティングイベント
715 */
716 setPointingEntity(param:InputPointEvent) {
717 var layers = this.scene.getLayerArray();
718 var layer;
719 var offset = param.point;
720 while (layer = layers.pop()) { //上のレイヤーから先に処理
721 if (! layer.pointCapture)
722 continue;
723
724 var dragObj = layer.getEntityByPoint(offset);
725 if (! dragObj)
726 dragObj = layer;
727
728 param.set(dragObj);
729 this.dragParam = param;
730
731 break;
732 }
733 }
734
735 /**
736 * 入力イベントを実行する
737 */
738 raiseInputEvent() {
739 var e:InputEvent;
740 while (e = this.eventQueue.shift()) {
741 if (! this.inputEventMap[e.type]) {
742 this.userEvent.fire(e);
743 continue;
744 }
745
746 var n = this.inputEventMap[e.type][e.action];
747 if (e.type == InputEventType.Keyboard) {
748 if (this.scene[n])
749 this.scene[n].fire(e);
750 this[n].fire(e);
751 } else {
752 if (e.action == InputEventAction.Down)
753 this.setPointingEntity(<InputPointEvent>e);
754 else if (!this.dragParam)
755 continue;
756 else
757 (<InputPointEvent>e).set(this.dragParam.entity);
758
759 if ((<InputPointEvent>e).entity && (<InputPointEvent>e).entity[n])
760 (<InputPointEvent>e).entity[n].fire(e);
761 if (this.scene[n])
762 this.scene[n].fire(e);
763
764 this[n].fire(e);
765 }
766 }
767 }
768
769 /**
770 * メインループ
771 */
772 main() {
773 var fps_stack:number[] = [];
774 var _main = (t:number) => {
775 if (this._exit)
776 return;
777
778 if (t === undefined)
779 t = Date.now ? Date.now() : new Date().getTime();
780 if ((this.tick+500) < t || this.tick > t) {
781 //this.tick > t自体はタブ切り替え程度でも結構頻発する
782 if ((this.tick+10000) < t || (this.tick > t+500))
783 this.refresh();
784 this.tick = t - 1000 / 60;
785 this.renderTick = t;
786 }
787
788 var time = t - this.tick;
789 this.raiseInputEvent();
790 this.update.fire(time);
791 this.tick = t;
792
793 for (var i=0; i<this.timers.length; i++)
794 this.timers[i].tryFire(time);
795
796 if (this.renderTick <= t) {
797 if (this.render)
798 this.render.fire();
799
800 this.renderer.render();
801 if (this.targetFps)
802 this.renderTick = t+this.targetFps;
803 if (this.fps) {
804 if (fps_stack.length == 19) {
805 this.fps.innerHTML = Math.round(20000 / (t-fps_stack[0])).toString();
806 fps_stack = [];
807 } else {
808 fps_stack.push(t);
809 }
810 }
811 }
812
813 window.requestAnimationFrame(_main);
814 }
815
816 this.tick = 0;
817 this.renderTick = 0;
818 window.requestAnimationFrame(_main);
819 }
820
821 /**
822 * フルスクリーン化を行う
823 */
824 fullscreen() {
825 var t = this.renderer.container;
826 if (t["requestFullScreen"])
827 t["requestFullScreen"]();
828 else if (t["webkitRequestFullScreen"])
829 t["webkitRequestFullScreen"]();
830 else if (t["mozRequestFullScreen"])
831 t["mozRequestFullScreen"]();
832 else
833 return false;
834 return true;
835 }
836
837 /**
838 * フルスクリーンを終了する
839 */
840 exitFullscreen() {
841 var t = this.renderer.container;
842 if (t["exitFullscreen"])
843 t["exitFullscreen"]();
844 else if (t["webkitCancelFullScreen"])
845 t["webkitCancelFullScreen"]();
846 else if (t["mozCancelFullScreen"])
847 t["mozCancelFullScreen"]();
848 else
849 return false;
850 return true;
851 }
852 }
853 }

Back to OSDN">Back to OSDN
ViewVC Help
Powered by ViewVC 1.1.26