UNPKG

15.5 kBJavaScriptView Raw
1"use strict";
2var __extends = (this && this.__extends) || (function () {
3 var extendStatics = Object.setPrototypeOf ||
4 ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
5 function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
6 return function (d, b) {
7 extendStatics(d, b);
8 function __() { this.constructor = d; }
9 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
10 };
11})();
12var __assign = (this && this.__assign) || Object.assign || function(t) {
13 for (var s, i = 1, n = arguments.length; i < n; i++) {
14 s = arguments[i];
15 for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
16 t[p] = s[p];
17 }
18 return t;
19};
20Object.defineProperty(exports, "__esModule", { value: true });
21var SideralObject_1 = require("./../SideralObject");
22var Module_1 = require("./../Module");
23var index_1 = require("./../index");
24var Tool_1 = require("./../Tool");
25/**
26 * The engine of the game
27 * @class Game
28 * @extends SideralObject
29 */
30var Game = (function (_super) {
31 __extends(Game, _super);
32 /* LIFECYCLE */
33 /**
34 * @constructor
35 * @param props - Properties to set to the game
36 * @param theme - Theme to set to the game
37 */
38 function Game(props, theme) {
39 if (props === void 0) { props = {}; }
40 if (theme === void 0) { theme = {}; }
41 var _this = _super.call(this) || this;
42 /**
43 * Properties of the game
44 */
45 _this.props = {
46 width: 10,
47 height: 10,
48 fullSize: false,
49 backgroundColor: Tool_1.Color.transparent
50 };
51 /**
52 * Dom container of the game
53 */
54 _this.container = document.getElementById("sideral");
55 /**
56 * Set all scenes into one container to display all
57 */
58 _this.renderContainer = new index_1.PIXI.Container();
59 /**
60 * The current frame per second of the game
61 * @readonly
62 */
63 _this.fps = 60;
64 /**
65 * The current latency of the game (in ms)
66 * @readonly
67 */
68 _this.latency = 0;
69 /**
70 * The factor of time to avoid framerate dependance
71 * @readonly
72 */
73 _this.tick = 1;
74 /**
75 * The date of the current update in timestamp
76 * @readonly
77 */
78 _this.currentUpdate = 0;
79 /**
80 * The date of the last update in timestamp
81 * @readonly
82 */
83 _this.lastUpdate = 0;
84 /**
85 * Know if the game is currently running or not
86 */
87 _this.stopped = true;
88 /**
89 * Get the time to perform the "update" and "nextcycle" function
90 */
91 _this.timeToUpdate = 0;
92 /**
93 * Get the time to perform the render into the canvas
94 */
95 _this.timeToRender = 0;
96 /**
97 * Know if the game is loaded
98 * @readonly
99 */
100 _this.loaded = false;
101 /**
102 * Object to add when Game is ready
103 * @private
104 */
105 _this._addQueue = [];
106 /**
107 * Scenes to swap
108 */
109 _this._swapScenes = [];
110 _this.setProps({
111 backgroundColor: Tool_1.Color.black
112 });
113 _this.setProps(props);
114 _this.signals.start = new SideralObject_1.Signal(_this);
115 _this.context.game = _this;
116 _this.context.theme = Object.assign({
117 primaryColor: Tool_1.Color.cyan400,
118 secondaryColor: Tool_1.Color.pinkA400
119 }, theme || {});
120 _this.setSceneLoading(new Module_1.SceneLoading());
121 _this.connect(_this.signals.propChange, ["width", "height", "fullSize"], _this._resizeGame.bind(_this))
122 .connect(_this.signals.propChange, "backgroundColor", _this._onBackgroundColorChange.bind(_this));
123 return _this;
124 }
125 /**
126 * @override
127 */
128 Game.prototype.kill = function () {
129 _super.prototype.kill.call(this);
130 this.removeRenderer();
131 if (this._debounceResize) {
132 window.removeEventListener("resize", this._debounceResize);
133 this._debounceResize = null;
134 }
135 };
136 /**
137 * Update loop
138 * @param performance - Performance returned by the navigator
139 */
140 Game.prototype.update = function (performance) {
141 var _this = this;
142 if (this.stopped) {
143 return null;
144 }
145 performance = performance || window.performance.now();
146 requestAnimationFrame(this.update.bind(this));
147 // 100ms latency max
148 this.currentUpdate = performance;
149 this.latency = Tool_1.Util.limit(performance - this.lastUpdate, 8, 100);
150 this.fps = Math.floor(1000 / this.latency);
151 this.tick = 1000 / (this.fps * 1000);
152 this.tick = this.tick < 0 ? 0 : this.tick;
153 this.timeToUpdate = Tool_1.Util.measure(function () {
154 _this.children.forEach(function (child) { return child.update(_this.tick); });
155 _this.nextCycle();
156 });
157 this.timeToRender = Tool_1.Util.measure(function () {
158 if (_this.renderer) {
159 _this.renderer.render(_this.renderContainer);
160 }
161 });
162 this.lastUpdate = window.performance.now();
163 };
164 /**
165 * @override
166 */
167 Game.prototype.nextCycle = function () {
168 var _this = this;
169 _super.prototype.nextCycle.call(this);
170 if (this.loaded) {
171 var queue = this._addQueue.slice(0);
172 this._addQueue = [];
173 queue.forEach(function (q) { return _this._addScene(q.item, q.props); });
174 this._swapScenes.forEach(function (swap) { return _this._swapScene(swap); });
175 this._swapScenes = [];
176 if (this._loadingScene) {
177 this._loadingScene.kill();
178 this._loadingScene = null;
179 }
180 }
181 };
182 /* METHODS */
183 /**
184 * Get the position of the mouse relative to the canvas
185 * @returns The position x and y of the mouse
186 */
187 Game.prototype.getMousePosition = function () {
188 return this.renderer ? __assign({}, this.renderer.plugins.interaction.mouse.global) : { x: 0, y: 0 };
189 };
190 /**
191 * @override
192 */
193 Game.prototype.add = function (item, props) {
194 if (props === void 0) { props = {}; }
195 this._addQueue.push({ item: item, props: props });
196 return item;
197 };
198 /**
199 * Set a scene used for loading screen
200 * @param loadingScene - Scene to run when the game is loading
201 * @param props - Properties to pass to the loading Scene
202 * @return The LoadingScene
203 */
204 Game.prototype.setSceneLoading = function (loadingScene, props) {
205 if (props === void 0) { props = {}; }
206 if (loadingScene && !(loadingScene instanceof Module_1.Scene)) {
207 throw new Error("Game.setLoadingScene: The loadingScene must be an instance of Scene.");
208 }
209 if (this._loadingScene) {
210 this._loadingScene.kill();
211 }
212 if (!loadingScene) {
213 this._loadingScene = null;
214 return null;
215 }
216 this._loadingScene = this._addScene(loadingScene, props);
217 return this._loadingScene;
218 };
219 /**
220 * Load assets
221 * @param env - The environment to load
222 * @param loadingScene - Scene to use for loading
223 * @param onLoad - Callback to be called when assets is loaded
224 */
225 Game.prototype.loadAssets = function (env, loadingScene, onLoad) {
226 var _this = this;
227 Tool_1.Assets.load(env, function () {
228 var done = function () {
229 _this.loaded = true;
230 _this.signals.start.dispatch();
231 if (onLoad) {
232 onLoad();
233 }
234 };
235 if (_this._loadingScene) {
236 _this._loadingScene.onAssetsLoaded(done);
237 }
238 else {
239 done();
240 }
241 }, function (progress) { return _this._loadingScene && _this._loadingScene.signals.progress.dispatch(progress); });
242 };
243 /**
244 * Start the game loop
245 * @acess public
246 * @param width - width of the game
247 * @param height - height of the game
248 * @param container - container to attach the game
249 * @param onLoad - Callback to be called when the game is loaded
250 * @returns current instance
251 */
252 Game.prototype.start = function (width, height, container, onLoad) {
253 this.setProps({
254 width: width || this.props.width,
255 height: height || this.props.height
256 });
257 if (!this.props.fullSize && (!this.props.width || !this.props.height)) {
258 throw new Error("Engine.start: You must set 'width', 'height' and a 'container' container");
259 }
260 this.container = container || this.container;
261 this._swapRenderer();
262 this._resizeGame();
263 this._onBackgroundColorChange();
264 this.loadAssets(null, null, onLoad);
265 this.update();
266 return this;
267 };
268 /**
269 * Stop the current game
270 */
271 Game.prototype.stop = function () {
272 this.stopped = true;
273 this.removeRenderer();
274 };
275 /**
276 * resize the current canvas
277 */
278 Game.prototype.resize = function () {
279 if (!this.renderer) {
280 return null;
281 }
282 this.renderer.resize(this.props.width, this.props.height);
283 };
284 /**
285 * Remove the PIXI Renderer
286 * @param killCanvas - If true, the canvas DOM will be destroyed too
287 */
288 Game.prototype.removeRenderer = function (killCanvas) {
289 if (killCanvas === void 0) { killCanvas = true; }
290 if (!this.renderer) {
291 return null;
292 }
293 this.renderer.destroy(killCanvas);
294 this.renderer = null;
295 };
296 /**
297 * Enable keyboard events
298 * @param preventInputPropagation - If true, the event provided by the keyboard will not be propaged outside the Sideral engine
299 * @returns The current instance of Keyboard
300 */
301 Game.prototype.enableKeyboard = function (preventInputPropagation) {
302 this.keyboard = this.add(new SideralObject_1.Keyboard());
303 this.keyboard.preventInputPropagation = preventInputPropagation;
304 return this.keyboard;
305 };
306 /**
307 * Disable keyboard events
308 */
309 Game.prototype.disableKeyboard = function () {
310 if (this.keyboard) {
311 this.keyboard.kill();
312 }
313 this.keyboard = null;
314 };
315 /**
316 * Get all scenes from the current game
317 * @returns Scenes
318 */
319 Game.prototype.getScenes = function () {
320 return this.children.filter(function (child) { return child instanceof Module_1.Scene; }).filter(function (child) { return !child.killed; });
321 };
322 /**
323 * Attach the game to a dom
324 */
325 Game.prototype.attach = function (container) {
326 this.container = container || this.container;
327 if (!this.container) {
328 throw new Error("Game: before start your game, you must set a dom container.");
329 }
330 this.container.appendChild(this.renderer.view);
331 };
332 /**
333 * Swap a scene to the next Scene
334 * @param currentScene - The current scene to swap
335 * @param nextScene - The next Scene to swap
336 * @returns The next scene
337 */
338 Game.prototype.swapScene = function (currentScene, nextScene, color, duration, onComplete) {
339 if (duration === void 0) { duration = 500; }
340 if (currentScene && nextScene) {
341 this._swapScenes.push({ currentScene: currentScene, nextScene: nextScene, color: color, duration: duration, onComplete: onComplete });
342 }
343 return nextScene;
344 };
345 /* PRIVATE */
346 /**
347 * Method for kill the first scene and add the second
348 * @private
349 */
350 Game.prototype._swapScene = function (_a) {
351 var _this = this;
352 var currentScene = _a.currentScene, nextScene = _a.nextScene, color = _a.color, duration = _a.duration, onComplete = _a.onComplete;
353 if (!color) {
354 currentScene.kill();
355 this._addScene(nextScene);
356 if (onComplete) {
357 onComplete(nextScene);
358 }
359 }
360 else {
361 currentScene.fade("out", color, duration, function () {
362 currentScene.kill();
363 _this._addScene(nextScene);
364 nextScene.fade("in", color, duration, function () {
365 if (onComplete) {
366 onComplete(nextScene);
367 }
368 });
369 });
370 }
371 };
372 /**
373 * Add a new scene into the global PIXI Container. If the scene is not an instance of a scene,
374 * this function will just call the "add" method.
375 * @param scene - The scene to add
376 * @private
377 */
378 Game.prototype._addScene = function (scene, props) {
379 if (props === void 0) { props = {}; }
380 _super.prototype.add.call(this, scene, props);
381 if (scene instanceof Module_1.Scene) {
382 this.renderContainer.addChild(scene.container);
383 }
384 return scene;
385 };
386 /**
387 * When width or height attributes change
388 * @private
389 */
390 Game.prototype._resizeGame = function () {
391 var _this = this;
392 if (!this.renderer) {
393 return null;
394 }
395 if (this.props.fullSize) {
396 this.setProps({
397 width: this.renderer.view.parentElement.clientWidth,
398 height: this.renderer.view.parentElement.clientHeight
399 });
400 }
401 this.getScenes().filter(function (scene) { return scene.props.sizeAuto; }).forEach(function (scene) {
402 scene.props.width = _this.props.width;
403 scene.props.height = _this.props.height;
404 });
405 if (!this.props.fullSize && this._debounceResize) {
406 window.removeEventListener("resize", this._debounceResize);
407 this._debounceResize = null;
408 }
409 if (this.props.fullSize && !this._debounceResize) {
410 window.addEventListener("resize", this._debounceResize = Tool_1.Util.debounce(this._resizeGame.bind(this), 250));
411 }
412 this.renderer.resize(this.props.width, this.props.height);
413 };
414 /**
415 * When background attribute changes
416 * @private
417 */
418 Game.prototype._onBackgroundColorChange = function () {
419 var color = Tool_1.Util.colorToDecimal(this.props.backgroundColor), transparent = Boolean(isNaN(color));
420 if (!transparent) {
421 this.renderer.backgroundColor = color;
422 }
423 if (transparent !== this.renderer.transparent) {
424 this._swapRenderer();
425 }
426 };
427 Game.prototype._swapRenderer = function () {
428 var transparent = !this.props.backgroundColor || this.props.backgroundColor === Tool_1.Color.transparent;
429 var view = null;
430 this.removeRenderer();
431 this.renderer = index_1.PIXI.autoDetectRenderer(this.props.width, this.props.height, {
432 autoResize: true,
433 transparent: transparent,
434 backgroundColor: transparent ? 0 : Tool_1.Util.colorToDecimal(this.props.backgroundColor),
435 roundPixels: false,
436 antialias: true
437 });
438 this.attach();
439 this.stopped = false;
440 };
441 return Game;
442}(SideralObject_1.SideralObject));
443exports.Game = Game;
444index_1.PIXI.utils.skipHello();