소스 검색

refactor loading function

lairaalmas 1 년 전
부모
커밋
2fd48fd6e2

BIN
src/assets/img/fallback.png


+ 184 - 122
src/js/gameMechanics.js

@@ -14,15 +14,6 @@ const game = {
   sprite: {}, // [Not directly used] Holds cached reference to media.
   audio: {}, // Holds cached reference to media - game.audio.<name>.play() plays that audio once.
   lang: {}, // Holds language dictionary in a key-value format - game.lang.<key> returns <value>.
-  loadedCur: 0, // [Not directly used] CURRENT number of cached media (on current state)
-  loadedMax: 0, // [Not directly used] EXPECTED number of cached media (on current state)
-  loadManager: {
-    // [Not directly used] <mediaCategory> : [ <isLoading?> , <#CurrentlyCached> ]
-    lang: [false, 0],
-    audio: [false, 0],
-    image: [false, 0],
-    sprite: [false, 0],
-  },
 
   /** 
    * Handles game states. <br>
@@ -43,6 +34,9 @@ const game = {
     list: [],
     // [Not directly used]
     name: undefined,
+    // progressBar
+    progressBar: undefined,
+    progressBarLabel: undefined,
     /**
      * Create new state. <br>
      *
@@ -85,16 +79,23 @@ const game = {
           colors.blueBg,
           1
         );
-        self.progressBar = game.add.geom.rect(
+        game.state.progressBar = game.add.geom.rect(
           context.canvas.width / 2,
           context.canvas.height / 2,
-          20,
-          20,
+          40,
+          40,
           undefined,
           0,
-          colors.white
+          colors.blueBgOff
+        );
+        game.state.progressBar.anchor(0.5, 0.5);
+
+        game.state.progressBarLabel = game.add.text(
+          context.canvas.width / 2,
+          context.canvas.height / 2 + 100,
+          '0/0',
+          textStyles.h2_blue
         );
-        self.progressBar.anchor(0.5, 0.5);
         // Calls state's preload() to load the state's media
         self.preload();
       } else {
@@ -123,6 +124,87 @@ const game = {
     },
   },
 
+  loadHandler: {
+    cur: 0, // [Not directly used] CURRENT number of cached media (on current state)
+    max: 0, // [Not directly used] EXPECTED number of cached media (on current state)
+    // [Not directly used] media type and status information
+    type: {
+      lang: { isLoading: false, cached: 0, length: 0 },
+      audio: { isLoading: false, cached: 0, length: 0 },
+      image: { isLoading: false, cached: 0, length: 0 },
+      sprite: { isLoading: false, cached: 0, length: 0 },
+    },
+    /** [Not directly used] Removes the urls that are already in the cache.
+     *
+     * @param {string[]} unfilteredUrls array of urls
+     * @param {object} mediaCategory media category
+     *
+     * @returns {string[]} array of uncached urls
+     */
+    getUncachedUrlsOnly: function (unfilteredUrls, mediaCategory) {
+      const uncachedUrls = [];
+      unfilteredUrls.forEach((url) => {
+        if (mediaCategory[url[0]] === undefined) uncachedUrls.push(url);
+      });
+      return uncachedUrls;
+    },
+    /** [Not directly used] Informs ONE media file was loaded to cache. <br>
+     *
+     * After ALL FILES of the SAME CATEGORY are cached, calls game.load.cachedAllInOneMediaType()
+     *
+     * @param {String} mediaType media category (to update the cached files from that category)
+     */
+    cachedOneFile: function (mediaType) {
+      const length = game.loadHandler.type[mediaType].length;
+
+      // Update progress bar on loading screen
+      if (length - 1 !== 0) {
+        // if (game.state..progressBarLabel?.name) {
+        //   game.state..progressBarLabel.name = `${game.loadHandler.cur + 1}/${
+        //     game.loadHandler.max
+        //   }`;
+        // }
+        game.state.progressBar.width =
+          (500 / game.loadHandler.max) * game.loadHandler.cur;
+        game.render.all();
+        game.loadHandler.cur++;
+        // console.log(game.loadHandler.cur, game.loadHandler.max);
+      }
+      // If reached last index of current media array
+      if (game.loadHandler.type[mediaType].cached >= length - 1) {
+        // Resets load manager
+        game.loadHandler.type[mediaType].isLoading = false;
+        game.loadHandler.type[mediaType].cached = 0;
+        game.loadHandler.type[mediaType].length = 0;
+        // Informs
+        game.loadHandler.cachedAllInOneMediaType();
+      } else {
+        // Updates
+        game.loadHandler.type[mediaType].cached++;
+      }
+    },
+    /** [Not directly used] Informs ALL MEDIA files from the SAME CATEGORY are cached. <br>
+     *
+     * After ALL CATEGORIES of media are cached, calls create() via game.state. <br>
+     * ATTENTION: Do not call create() directly.
+     */
+    cachedAllInOneMediaType: function () {
+      // Checks if finished loading ALL media categories
+      let endPreload = true;
+      for (let key in game.loadHandler.type) {
+        if (game.loadHandler.type[key].isLoading) {
+          endPreload = false;
+          break;
+        }
+      }
+      // If flag doesnt change, all media is cached
+      if (endPreload) {
+        game.loadHandler.cur = 0;
+        game.loadHandler.max = 0;
+        game.state.create();
+      }
+    },
+  },
   /**
    * Loads media files to cache. <br>
    *
@@ -141,26 +223,31 @@ const game = {
      * @param {string} url url for the selected language
      */
     lang: function (url) {
-      game.loadManager.lang[0] = true;
-      game.loadManager.lang[1] = 0;
+      game.loadHandler.type.lang.isLoading = true;
+      game.loadHandler.type.lang.cached = 0;
+
       game.lang = {}; // Clear previously loaded language
-      const init = { mode: 'same-origin' };
-      fetch(url, init)
-        .then((response) => {
-          return response.text();
-        })
+
+      fetch(url, { mode: 'same-origin' })
+        .then((response) => response.text())
         .then((text) => {
-          let msg = text.split('\n');
-          game.loadedMax += msg.length - 1;
-          msg.forEach((cur) => {
+          const lines = text.split('\n');
+          game.loadHandler.type.lang.length = lines.length;
+          game.loadHandler.max += lines.length;
+          lines.forEach((line) => {
             try {
-              let msg = cur.split('=');
+              const msg = line.split('=');
+              if (msg.length !== 2)
+                throw Error('Game error: sintax error in i18y file');
               game.lang[msg[0].trim()] = msg[1].trim();
-            } catch (Error) {
-              if (isDebugMode) console.log('Sintax error fixed');
+            } catch (error) {
+              console.error(error.message);
             }
-            game.load.finishedOneMediaElement(msg.length - 1, 'lang');
+            game.loadHandler.cachedOneFile('lang');
           });
+        })
+        .catch((error) => {
+          console.error(error);
         });
     },
     /**
@@ -170,20 +257,27 @@ const game = {
      * @param {string[]} urls audio urls for the current state
      */
     audio: function (urls) {
-      game.loadManager.audio[0] = true;
-      game.loadManager.audio[1] = 0;
-      urls = this.getUncachedUrls(urls, game.audio);
+      game.loadHandler.type.audio.isLoading = true;
+      game.loadHandler.type.audio.cached = 0;
+
+      urls = game.loadHandler.getUncachedUrlsOnly(urls, game.audio);
+
+      game.loadHandler.type.audio.length = urls.length;
+
       if (urls.length == 0) {
-        game.load.finishedOneMediaElement(0, 'audio');
+        game.loadHandler.cachedOneFile('audio');
       } else {
-        game.loadedMax += urls.length - 1;
-        const init = { mode: 'same-origin' };
-        urls.forEach((cur) => {
-          fetch(cur[1][1], init)
+        game.loadHandler.max += urls.length;
+
+        urls.forEach((url) => {
+          fetch(url[1][1], { mode: 'same-origin' })
             .then((response) => response.blob())
-            .then((myBlob) => {
-              game.audio[cur[0]] = new Audio(URL.createObjectURL(myBlob));
-              game.load.finishedOneMediaElement(urls.length - 1, 'audio');
+            .then((data) => {
+              game.audio[url[0]] = new Audio(URL.createObjectURL(data));
+              game.loadHandler.cachedOneFile('audio');
+            })
+            .catch((error) => {
+              console.error(error);
             });
         });
       }
@@ -195,20 +289,31 @@ const game = {
      * @param {string[]} urls image urls for the current state
      */
     image: function (urls) {
-      game.loadManager.image[0] = true;
-      game.loadManager.image[1] = 0;
-      urls = this.getUncachedUrls(urls, game.image);
+      game.loadHandler.type.image.isLoading = true;
+      game.loadHandler.type.image.cached = 0;
+
+      urls = game.loadHandler.getUncachedUrlsOnly(urls, game.image);
+
+      game.loadHandler.type.image.length = urls.length;
+
       if (urls.length == 0) {
-        game.load.finishedOneMediaElement(0, 'image');
+        game.loadHandler.cachedOneFile('image');
       } else {
-        game.loadedMax += urls.length - 1;
-        urls.forEach((cur) => {
+        game.loadHandler.max += urls.length;
+
+        urls.forEach((url) => {
           const img = new Image();
           img.onload = () => {
-            game.image[cur[0]] = img;
-            game.load.finishedOneMediaElement(urls.length - 1, 'image');
+            game.image[url[0]] = img;
+            game.loadHandler.cachedOneFile('image');
+          };
+          img.onerror = () => {
+            console.error('Game error: image not found');
+            game.image[url[0]] = img;
+            img.src = fallbackImgUrl;
+            game.loadHandler.cachedOneFile('image');
           };
-          img.src = cur[1];
+          img.src = url[1];
         });
       }
     },
@@ -219,83 +324,40 @@ const game = {
      * @param {string[]} urls spritesheet urls for the current state
      */
     sprite: function (urls) {
-      game.loadManager.sprite[0] = true;
-      game.loadManager.sprite[1] = 0;
-      urls = this.getUncachedUrls(urls, game.sprite);
+      game.loadHandler.type.sprite.isLoading = true;
+      game.loadHandler.type.sprite.cached = 0;
+
+      urls = game.loadHandler.getUncachedUrlsOnly(urls, game.sprite);
+
+      game.loadHandler.type.sprite.length = urls.length;
+
       if (urls.length == 0) {
-        game.load.finishedOneMediaElement(0, 'sprite');
+        game.loadHandler.cachedOneFile('sprite');
       } else {
-        game.loadedMax += urls.length - 1;
-        urls.forEach((cur) => {
-          const img = new Image();
-          img.onload = () => {
-            game.sprite[cur[0]] = img;
-            game.load.finishedOneMediaElement(urls.length - 1, 'sprite');
-          };
-          img.src = cur[1];
-          img.frames = cur[2];
+        game.loadHandler.max += urls.length;
+
+        urls.forEach((url) => {
+          try {
+            const img = new Image();
+            img.onload = () => {
+              game.sprite[url[0]] = img;
+              game.loadHandler.cachedOneFile('sprite');
+            };
+            img.onerror = () => {
+              console.error('Game error: sprite not found');
+              game.sprite[url[0]] = img;
+              img.src = fallbackImgUrl;
+              img.frames = 1;
+              game.loadHandler.cachedOneFile('sprite');
+            };
+            img.src = url[1];
+            img.frames = url[2];
+          } catch (error) {
+            console.error(error);
+          }
         });
       }
     },
-    /** [Not directly used] Removes the urls that are already in the cache.
-     *
-     * @param {string[]} urls array of urls
-     * @param {object} media media category
-     *
-     * @returns {string[]} array of uncached urls
-     */
-    getUncachedUrls: function (urls, media) {
-      const newUrls = [];
-      urls.forEach((cur) => {
-        if (media[cur[0]] == undefined) newUrls.push(cur);
-      });
-      return newUrls;
-    },
-    /** [Not directly used] Informs ONE media file was loaded to cache. <br>
-     *
-     * After ALL FILES of the SAME CATEGORY are cached, calls game.load.finishedOneMediaType()
-     *
-     * @param {number} lastIndex last index of the media array (to check if is finished)
-     * @param {String} mediaType media category (to update the cached files from that category)
-     */
-    finishedOneMediaElement: function (lastIndex, mediaType) {
-      // Updates progress bar
-      if (lastIndex != 0) {
-        self.progressBar.width = (200 / game.loadedMax) * game.loadedCur;
-        game.render.all();
-        game.loadedCur++;
-      }
-      // If reached last index of current media array
-      if (lastIndex == game.loadManager[mediaType][1]) {
-        // Resets load manager
-        game.loadManager[mediaType][0] = false;
-        game.loadManager[mediaType][1] = 0;
-        // Informs
-        game.load.finishedOneMediaType();
-      } else {
-        // Updates
-        game.loadManager[mediaType][1]++;
-      }
-    },
-    /** [Not directly used] Informs ALL MEDIA files from the SAME CATEGORY are cached. <br>
-     *
-     * After ALL CATEGORIES of media are cached, calls create() via game.state. <br>
-     * ATTENTION: Do not call create() directly.
-     */
-    finishedOneMediaType: function () {
-      // Checks if finished loading ALL media categories
-      let endPreload = true;
-      for (let key in game.loadManager) {
-        if (game.loadManager[key][0] == true) {
-          endPreload = false;
-          break;
-        }
-      }
-      // If flag doesnt change, all media is cached
-      if (endPreload) {
-        game.state.create();
-      }
-    },
   },
 
   /**
@@ -432,9 +494,9 @@ const game = {
         y == undefined ||
         text == undefined ||
         style == undefined
-      )
+      ) {
         console.error('Game error: missing parameters.');
-      else {
+      } else {
         const med = {
           typeOfMedia: 'text',
           name: text,

+ 18 - 6
src/js/globals/globals_control.js

@@ -249,12 +249,24 @@ const gameList = [
             ? ['move', [0, 1, 2, 3, 4], 4]
             : ['move', [10, 11, 12, 13, 14], 4],
         character: (operation) => {
-          const char = game.add.sprite(0, 490, 'tractor', 0, 0.7);
+          const char = game.add.sprite(
+            0,
+            context.canvas.height - 170,
+            'tractor',
+            0,
+            1.05
+          );
           char.anchor(0.5, 0.5);
           if (operation === 'plus') char.curFrame = 10;
           return char;
         },
-        building: () => game.add.image(650, 260, 'farm', 1.1),
+        building: () =>
+          game.add.image(
+            context.canvas.width - 400,
+            context.canvas.height - 400,
+            'farm',
+            1.155
+          ),
       },
     },
   },
@@ -357,11 +369,11 @@ const gameList = [
       end: {
         characterAnimation: ['move', [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 3],
         character: () => {
-          const char = game.add.sprite(0, -152, 'kid_running', 0, 0.7);
+          const char = game.add.sprite(0, -152, 'kid_running', 0, 1.05);
           char.anchor(0.5, 0.5);
           return char;
         },
-        building: () => game.add.image(600, 222, 'school', 0.7),
+        building: () => game.add.image(600, 222, 'school', 1.05),
       },
     },
   },
@@ -450,11 +462,11 @@ const gameList = [
       end: {
         characterAnimation: ['move', [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 3],
         character: () => {
-          const char = game.add.sprite(0, 460, 'kid_running', 6, 0.7);
+          const char = game.add.sprite(0, 460, 'kid_running', 6, 1.05);
           char.anchor(0.5, 0.5);
           return char;
         },
-        building: () => game.add.image(600, 222, 'school', 0.7),
+        building: () => game.add.image(600, 222, 'school', 1.05),
       },
     },
   },

+ 5 - 5
src/js/globals/globals_debug.js

@@ -1,16 +1,16 @@
 const isDebugMode = true;
 
 const debugState = {
-  lang: { status: false, lang: 'pt_BR' },
-  name: { status: false, name: 'Username' },
-  menu: { status: false, id: 1 },
+  lang: { status: true, lang: 'it_IT' },
+  name: { status: true, name: 'Username' },
+  menu: { status: true, id: 2 },
   customMenu: {
-    status: false,
+    status: true,
     getData: () => {
       return { mode: 'a', operation: 'plus', difficulty: 1, label: true };
     },
   },
-  map: { status: false, stop: false },
+  map: { status: false, stop: true },
   end: { status: false, stop: false },
 };
 

+ 2 - 0
src/js/globals/globals_tokens.js

@@ -1,4 +1,5 @@
 const baseUrl = 'src/assets/img/'; // Base directory for media
+const fallbackImgUrl = 'src/assets/img/fallback.png';
 
 /**
  * Preset colors for graphic elements.
@@ -199,6 +200,7 @@ const url = {
      * audio: [ [name, [source, alternative source] ], ... ]
      */
     image: [
+      ['fallback', fallbackImgUrl],
       // scene new level
       ['floor_stone_left', baseUrl + 'scene/new_level/floor_stone_left.png'],
       ['floor_stone_right', baseUrl + 'scene/new_level/floor_stone_right.png'],

+ 5 - 0
src/js/menus/preMenu_boot.js

@@ -37,6 +37,11 @@ const bootState = {
       game.load.lang('src/assets/lang/' + langString);
     }
     // LOADING MEDIA
+    if (isDebugMode) {
+      console.log(url.boot.audio.length + ' audio files to cache');
+      console.log(url.boot.image.length + ' images to cache');
+      console.log(url.boot.sprite.length + ' sprites to cache');
+    }
     game.load.audio(url.boot.audio);
     game.load.image(url.boot.image);
     game.load.sprite(url.boot.sprite);

+ 7 - 3
src/js/menus/preMenu_name.js

@@ -28,11 +28,15 @@ const nameState = {
       textStyles.h4_brown
     );
 
-    this.okBtn = game.add.image(
+    this.okBtn = game.add.geom.rect(
       context.canvas.width / 2,
       context.canvas.height / 2 + 93 + 44,
-      'btn',
-      1.5
+      285,
+      93.5,
+      undefined,
+      undefined,
+      colors.blueDark,
+      1
     );
     this.okBtn.anchor(0.5, 0.5);
 

+ 29 - 23
src/js/screens/end.js

@@ -14,24 +14,13 @@ const endState = {
     self.preAnimate = false;
     self.animate = true;
 
-    // Background color
-    game.add.geom.rect(
-      0,
-      0,
-      context.canvas.width,
-      context.canvas.height,
-      undefined,
-      0,
-      colors.blueBg,
-      1
-    );
-
     renderBackground();
 
     // Progress bar
+    const y = 20;
     game.add.geom.rect(
-      660,
-      10,
+      context.canvas.width - 300,
+      y,
       4 * 37.5,
       35,
       undefined,
@@ -39,17 +28,32 @@ const endState = {
       colors.greenNeon,
       0.5
     );
-    // Progress
-    game.add.geom.rect(661, 11, 149, 34, colors.blue, 3, undefined, 1); // Box
-    game.add.text(820, 38, '100%', textStyles.h2_blueDark).align = 'left';
+    game.add.geom.rect(
+      context.canvas.width - 300 + 1,
+      y + 1,
+      149,
+      34,
+      colors.blue,
+      3,
+      undefined,
+      1
+    );
+    game.add.text(
+      context.canvas.width - 300 + 160,
+      y + 33,
+      '100%',
+      textStyles.h2_blueDark
+    ).align = 'left';
     game.add.text(
-      650,
-      38,
+      context.canvas.width - 300 - 10,
+      y + 33,
       game.lang.difficulty + ' ' + gameDifficulty,
       textStyles.h2_blueDark
     ).align = 'right';
 
-    game.add.image(360, 545, 'tree4', 0.7).anchor(0, 1);
+    game.add
+      .image(360 + 400, context.canvas.height - 100, 'tree_4', 1.05)
+      .anchor(0, 1);
 
     gameList[gameId].assets.end.building();
 
@@ -61,16 +65,18 @@ const endState = {
       this.animate = false;
 
       // Balloon
-      this.balloon = game.add.image(0, -260, 'balloon');
+      this.balloon = game.add.image(0, -260, 'balloon', 1.5);
       this.balloon.anchor(0.5, 0.5);
 
-      this.basket = game.add.image(0, -150, 'balloon_basket');
+      this.basket = game.add.image(0, -150, 'balloon_basket', 1.5);
       this.basket.anchor(0.5, 0.5);
     }
 
     if (this.animate) game.animation.play(this.character.animation[0]);
 
-    game.add.image(30, 585, 'tree4', 0.85).anchor(0, 1);
+    game.add
+      .image(30 + 200, context.canvas.height - 20, 'tree_4', 1.275)
+      .anchor(0, 1);
   },
 
   /**