Search code examples
javascriptjsonpphaser-framework

How to load assets via JSONP into Phaser


I am trying to load data (including images) via a JSONP feed into Phaser 3.60.0. I know how to load image asset normally as per my GameScene.js code below:

class GameScene extends Phaser.Scene {
  constructor() {
    super({ key: 'GameScene' });
  }

  preload() {
    this.load.image('oranges', './assets/Ambersweet_oranges.jpg');
  }

  create() {
    this.add.image(240, 320, 'oranges')
  }
}

The JSONP feed is located on a different server and is structured like this:

/**/ typeof handleItems === 'function' && handleItems([
{"offerWeek":"kw17","id":"1028377","name":"Bananen","price":"1.79","url":"https://www.rewe.de/angebote/nationale-angebote/#1028377","image":"https://upload.wikimedia.org/wikipedia/commons/5/5d/Pl%C3%A1tanos_de_Canarias.JPG"},
{"offerWeek":"kw17","id":"2670547","name":"Big City Pizza","price":"1.99","url":"https://www.rewe.de/angebote/nationale-angebote/#2670547","image":"https://upload.wikimedia.org/wikipedia/commons/a/a5/Pizza_20.jpg"},
{"offerWeek":"kw17","id":"3663221","name":"Die Thüringer Bratwurst","price":"4.59","url":"https://www.rewe.de/angebote/nationale-angebote/#3663221","image":"https://upload.wikimedia.org/wikipedia/commons/9/91/Bratwurst_on_the_grill.jpg"},
{"offerWeek":"kw17","id":"3919250","name":"Orangen","price":"2.99","url":"https://www.rewe.de/angebote/nationale-angebote/#3919250","image":"https://upload.wikimedia.org/wikipedia/commons/4/43/Ambersweet_oranges.jpg"}]
);

How do I access the product names and images and display them in Phaser? Also how do I wait for the request to finish before create() is called? All advice appreciated!


Solution

  • There is probably a better solution, but my first idea would be:

    1. create a global variabel, like this

       var globalItemsStore = { isLoaded: false, inGame: false,  items: []};
      
    2. create the jsonp callback function, like this

       function handleItems(loadedItems){
           globalItemsStore = {
               isLoaded: true,
               inGame: false, 
               items: loadedItems
           };
       }
      
    3. Now in your game in the update method of the scene, you can check for the finished load, like this
      (Or alternative just use the global variable)

       ...
       update(){
           ...
           if(!globalItemsStore.inGame && globalItemsStore.isLoaded){
               // Do your thing ...
               globalItemsStore.inGame = true;
           }
       }
       ...
      

    OR if you want to use events and emitters in phaser, here is a small example:

    document.body.style = 'margin:10px;';
    
    var button = document.querySelector('#button');
    const DEMO_ITEMS = [1,2,3,4,5,6];
    
    button.addEventListener('click', () => handleItems(DEMO_ITEMS));
    
    function handleItems (items){
        game.scene.scenes[0].events.emit('DATA-LOADED', items);
    }
    
    var config = {
        type: Phaser.AUTO,
        width: 536,
        height: 143,
        scene: {
            create
        },
        banner: false
    }; 
    
    function create () {
        this.add.text(10,10, 'Click the HTML Button to simulate,\nloading Data.')
            .setScale(1.5)
            .setOrigin(0)
            .setStyle({fontStyle: 'bold', fontFamily: 'Arial'});
            
        // you can use any string for the event Name
        this.events.on('DATA-LOADED', args => {
          console.info(args);
          this.add.text(10, 80, `Data loaded!\nItemCount: ${args.length}`)
            .setScale(1.5)
            .setOrigin(0)
            .setStyle({fontStyle: 'bold', fontFamily: 'Arial', color: 'green'});
        });
    }
    
    var game = new Phaser.Game(config);
    <button id="button" style="margin-bottom:5px;"> Simulate Load Data </button> <br>
    
    <script src="//cdn.jsdelivr.net/npm/phaser/dist/phaser.min.js"></script>

    Just:

    1. create an event listener in the create method, with this.events.on(...); (link to the documentation)
    2. In the jsonp callback function, call the scene.events.emit(...) function. (link to the documentation)

    You just have to select the correct scene. If you only have one it is easy

    If you only want to start the game after loading the data, the easy solution would be to create the game in the handleItems function. Something like this

        function handleItems(loadedItems){
           var game = new Phaser.Game(config)
           game._myLoadedItems = loadedItems;
        }
    

    and in the create function you could get the data like this:

       create(){
           console.info(this.game._myLoadedItems);
           ...
       }