How to preload a huge scene in background ? not preloading the assets

Hi,

After multiple tests, i have identify my problem and i could reproduce it.

# The problem

My problem in my game is using a lot of text (+/-20 text_objects). When i run this game in browser everything is fast. But when i run it from my mobile trough cordova the main scene require a lot of time to launch and after the splash screen the game wait about 2-3 seconds to launch the main scene.

# test example to reproduce the problem

Scene configuration :

-boot (preload my assets)
-splash (splash screen)
-main (my huge scene, i use 500 text_objects to reveal the problem)

Here is the codepen link :
https://codepen.io/ogames/pen/pmOZWm?editors=0011

i post also the apk if you want to test at homeapp-debug.apk (2.0 MB)

# Result
in browser i wait about 2-3 seconds to see my splash scene
in mobile i wait about 10 seconds to see my splash scene

# What i want to do
i would like to preload my main scene during my splash screen, i have try this :

this.tweens.add({
    targets: splash_screen,
    duration: 15000,
    ease: 'Linear',
    alpha: 0,
  onStart: () => {
      this.scene.launch('main')
    },
    onComplete: () => {
      this.scene.setVisible(false, 'splash')
    },

But the result is the same. Is it not possible to launch the main scene in the background and when the main scene is ready switch to her ? When i launch the main scene,it seems like the phone calculates the main scene, slow down the game and then switch to main scene, with a lot of times.

Have you a solution for me ?

The problem is “drawing” all the text. It can be done while the scene is hidden but that won’t make a difference, it’s still going to lock up the browser.

Is it any trick to avoid this ? The major games launch a progress bar at the beginning (like the preloader for the assets) and when it’s ready the game is launching. Not possible to do that without feels the lag of calculating ?

Really not possible ?

Depending on your needs, you could try another approach:

  • Draw text manually to one CanvasTexture
  • Use a texture atlas instead of Text
  • Use BitmapText instead of Text

You could try a similar approach I use in my 2d car example where the loading screen is an separate html element.

The black loading screen you see at the beginning will be removed as soon as the mainScene is ready.

inside index.html

// https://github.com/yandeu/phaser3-matter-car-on-curved-terrain/blob/master/src/index.html
<style>
  html,
  body {
    height: 100%;
    margin: 0;
    background-color: #000000;
  }

  #loading-screen {
    display: flex;
    position: absolute;
    width: 100%;
    height: 100%;
    justify-content: center;
    align-items: center;
    background: black;
    overflow: visible;
  }

  h2 {
    font-family: 'Franklin Gothic Medium', 'Arial Narrow', Arial, sans-serif;
    color: #ffffff;
  }
</style>

<div id="loading-screen">
  <h2 id="item">loading...</h2>
</div>

<!-- custom element for phaser game -->
<canvas id="myCustomCanvas"></canvas>

inside mainScene.ts

// https://github.com/yandeu/phaser3-matter-car-on-curved-terrain/blob/master/src/scripts/scenes/mainScene.ts
// hide the loading screen when at the end of create()
let loadingScreen = document.getElementById('loading-screen')
if (loadingScreen) loadingScreen.style.display = 'none'

You could also use some css to smoothly fade away the loading screen, like I did in the platformer example.

The advantage of this approach, is that the user will immediately see the loading screen. Even before the JavaScript for the Phaser games has been loaded by the browser.

1 Like

This is definitely possible. The cause of your issue is that the construction of the text and setting a stroke on each text is super processor intensive. To avoid this, create your text in your preloader/splash screen and set the ignoreDestroy property to true. Then save your text array to a global variable.

In your game scene, you can now just add the text with this.add.existing.

Preloader/Splash screen:

for (var i = 0; i < 500; i++) {
    this.time.delayedCall(i * 10, () => {
        let random_x = Math.random() * 600
        let random_y = Math.random() * 600
        let text = this.add.text(random_x, random_y, 'test', {
            fontFamily: 'Fredoka',
            fontSize: 70,
            color: '#ffffff',
        }).setStroke('#62270c', 12);
        text.ignoreDestroy = true;
        GLOBAL_ARRAY.push(text);
    });
}
this.time.delayedCall(5000, ()=>{
    //GO TO MAIN SCENE
});

Main Scene:

for(let i = 0; i < GLOBAL_ARRAY.length; i++){
    this.add.existing(GLOBAL_ARRAY[i]);
}
1 Like

Cool it works perfectly. But sorry but i don’t understand how it really works.

if (loadingScreen) loadingScreen.style.display = 'none'

if loadingScreen , you disappear…how it detect that my main.scene is ready ?

i have not yet tested but thanks in advance.

It does not “detect” if the scene is ready. But since create() is execute synchronous by default and you remove the loading screen at the end of create(), the scene will be ready when you set display to none.

This does only work if you use synchronous code. Else you would maybe remove the loading screen in the first update() loop.