FastFoodie step 29

Hello,

I’m currently going through the final project in the Phaser.js, and I’m stuck on step 26 - 29 (placeFood method)

I got to step 29 which I cannot get past in my head… Below you can see steps 26 to 28 completed-ish… and need some help completing step 29…

Players will want visual feedback to know if they’ve served enough food. We can color the customer’s fullness capacity bars in different colors based on how filling the meal is.

Set the scaffolding for this by iterating through each of gameState.currentMeal.fullnessValue . Inside the loop add a check to see if the counter used in the iteration is less than gameState.currentCustomer.fullnessCapacity .

placeFood(food, fullnessValue) {
    if (gameState.currentMeal.children.entries.length < 3 && gameState.customerIsReady === true) {
      let Xposition = gameState.currentMeal.length;

      switch(food) {
        case 'Burger':
          gameState.currentMeal.create(Xposition, customer.y, 'Burger');
          break;
        case 'Fries':
          gameState.currentMeal.create(Xposition, customer.y, 'Fries');
          break; 
        case 'Shake':
          gameState.currentMeal.create(Xposition, customer.y, 'Shake');
          break;   
      }
      //step 28
      gameState.currentMeal.fullnessValue = fullnessValue;

      //step 29.. help!
      for (let i = 0; i < gameState.currentCustomer.fullnessCapacity; i++) {
        gameState.currentMeal.fullnessValue.forEach();
      }

    }
  }

Hello, and welcome to the forums!

This part can be a little confusing because it’s laying the groundwork for what you’ll be doing in the next steps. Without knowing what you’ll be doing, it’s much harder to interpret exactly what/why it’s asking you to do something. I found that reading ahead to get a feel for what’s coming helped a lot with the way this project was written.

In this case, you’ll be coloring fullness indicator blocks to let the player know how close (or over) they are.
gameState.currentCustomer.fullnessCapacity is the total number of blocks there will be for the customer.
gameState.currentMeal.fullnessValue is the total number of blocks the current combination of food items could occupy. Since it’s possible to add too much food for the customer, this number may be greater than how many blocks the customer has for their fullnessCapacity.

What it wants you to do is create a loop that counts up to the gameState.currentMeal.fullnessValue of the meal, and then inside the loop add a condition to make sure the count isn’t higher than the number of blocks the customer has (fullnessCapacity) because you can’t color in the blocks that don’t exist if the meal is greater than the customer’s fullnessCapacity.

Hopefully I didn’t just confuse you further. To be clearer, right now your loop isn’t counting up to the correct variable. It needs to count towards gameState.currentMeal.fullnessValue. Then inside the loop, you need to add an if statement to make sure that i is still less than gameState.currentCustomer.fullnessCapacity to prevent trying to set the coloring of blocks that don’t exist if the customer is overfed.

I’ll leave you with one final hint that will hopefully save you a lot of frustration during future testing. Your interpretation of Step #28 is slightly off. They wanted you to add the fullnessValue to gameState.currentMeal.fullnessValue, but you’re currently setting the value without addition.

1 Like

Hi Selecall,

That’s great, I do understand what you’re saying… I think :sweat_smile: … However, I’ve followed the instructions down to step 34, so the keys work, the food sprites get added in and all appears well until it’s not…

Let me explain, if a customers capacity is for example 6, and I add a burger worth 5 points, then it works perfectly. The appropriate blocks change colour to a different shade of yellow as intended. However, if I choose a food that would make it go beyond the capacity, the game break and comes up with this error:
undefined is not an object (evaluating 'gameState.currentCustomer.fullnessMeterBlocks[i].setFillStyle')

The below is the full code, and the above error relates to the first Else If statement, which is strange because it should not execute this line unless the customer is perfectly full.

Also, no changes occur to the meter blocks if the customer is purposely fed the right amount…

      //step 29
      for (let i = 0; i < gameState.currentMeal.fullnessValue; i++) {
        if (i < gameState.currentCustomer.fullnessCapacity) { 
          gameState.currentCustomer.fullnessMeterBlocks[i].setFillStyle(0xFFFA81);

        } else if (i === gameState.currentCustomer.fullnessCapacity) {
          gameState.currentCustomer.fullnessMeterBlocks[i].setFillStyle(0x3ADB40);
          gameState.currentCustomer.fullnessMeterBlocks[i].setStrokeStyle(2, 0x2EB94E);

        } else if (i > gameState.currentCustomer.fullnessCapacity) {
          gameState.currentCustomer.fullnessMeterBlocks[i].setFillStyle(0xDB533A);
          gameState.currentCustomer.fullnessMeterBlocks[i].setStrokeStyle(2, 0xB92E2E);

        }
      }

Full placeFood() for reference I guess?

Summary
placeFood(food, fullnessValue) {
    if (gameState.currentMeal.children.entries.length < 3 && gameState.customerIsReady === true) {
      let Xposition = gameState.currentMeal.length;

      switch(food) {
        case 'Burger':
          gameState.currentMeal.create(Xposition, gameState.cam.midPoint.y, 'Burger');
          break;
        case 'Fries':
          gameState.currentMeal.create(Xposition, gameState.cam.midPoint.y, 'Fries');
          break; 
        case 'Shake':
          gameState.currentMeal.create(Xposition, gameState.cam.midPoint.y, 'Shake');
          break;   
      }
      //step 28
      gameState.currentMeal.fullnessValue += fullnessValue;

      //step33
      gameState.sfx.placeFood.play();

      //step 29
      for (let i = 0; i < gameState.currentMeal.fullnessValue; i++) {
        if (i < gameState.currentCustomer.fullnessCapacity) { 
          gameState.currentCustomer.fullnessMeterBlocks[i].setFillStyle(0xFFFA81);

        } else if (i === gameState.currentCustomer.fullnessCapacity) {
          gameState.currentCustomer.fullnessMeterBlocks[i].setFillStyle(0x3ADB40);
          gameState.currentCustomer.fullnessMeterBlocks[i].setStrokeStyle(2, 0x2EB94E);

        } else if (i > gameState.currentCustomer.fullnessCapacity) {
          gameState.currentCustomer.fullnessMeterBlocks[i].setFillStyle(0xDB533A);
          gameState.currentCustomer.fullnessMeterBlocks[i].setStrokeStyle(2, 0xB92E2E);

        }
      }
    }
  }

Please help… :expressionless:

Hello,

The issue is that i can be larger than gameState.currentCustomer.fullnessCapacity so there aren’t enough gameState.currentCustomer.fullnessMeterBlocks[i] to cover it in that case.

Once i is larger than gameState.currentCustomer.fullnessCapacity, then the next time you try to use the .setFillStyle() method on gameState.currentCustomer.fullnessMeterBlocks[i], it will produce an error. That block doesn’t exist so it doesn’t have the method.

There are a few ways you can deal with it, but the original way that the instructions intended was something like this:

for loop like you have now
  if condition to make sure i is less than the capacity
    if / else if / else conditions here based on comparisons of gameState.currentMeal.fullnessValue and gameState.currentCustomer.fullnessCapacity
      so you can determine the block colors
    end if / else if / else.
  end if
end for loop

I wrote it like this to give you a better idea of what I meant before. The first if will act as a guard to make sure the inner if else if else conditions are only evaluated if i is in the right range.

As I said, this strategy is just how they originally wrote the instructions, but there are different ways to accomplish the same thing.

I mentioned the inner if conditions were based on comparisons of gameState.currentMeal.fullnessValue and gameState.currentCustomer.fullnessCapacity because my interpretation of what they want is if the customer is still under capacity, they want all the blocks to be a certain color and not just the ones under. Same if the meal was perfect - all the blocks should be the same nice color to indicate success. Likewise if they are overfed, then everything would be red.

Selectall… you are an absolute legend!

Rewrote it, and it appears to work perfectly fine!

I will no doubt contact you or more help as I progress down these steps… :smiley:

placeFood(food, fullnessValue) {
    if (gameState.currentMeal.children.entries.length < 3 && gameState.customerIsReady === true) {
      let Xposition = gameState.cam.midPoint.x;

      switch(food) {
        case 'Burger':
          gameState.currentMeal.create(Xposition - 90, gameState.cam.midPoint.y, 'Burger').setScale(0.5);
          break;
        case 'Fries':
          gameState.currentMeal.create(Xposition, gameState.cam.midPoint.y, 'Fries').setScale(0.5);
          break; 
        case 'Shake':
          gameState.currentMeal.create(Xposition + 90, gameState.cam.midPoint.y, 'Shake').setScale(0.5);
          break;   
      }
      //step 28
      gameState.currentMeal.fullnessValue += fullnessValue;

      //step33
      gameState.sfx.placeFood.play();

      //step 29
      for (let i = 0; i < gameState.currentMeal.fullnessValue; i++) {
        if (i < gameState.currentCustomer.fullnessCapacity) { 
          if (gameState.currentMeal.fullnessValue < gameState.currentCustomer.fullnessCapacity) {
          gameState.currentCustomer.fullnessMeterBlocks[i].setFillStyle(0xFFFA81);

        } else if (gameState.currentMeal.fullnessValue === gameState.currentCustomer.fullnessCapacity) {
          gameState.currentCustomer.fullnessMeterBlocks[i].setFillStyle(0x3ADB40);
          gameState.currentCustomer.fullnessMeterBlocks[i].setStrokeStyle(2, 0x2EB94E);

        } else if (gameState.currentMeal.fullnessValue > gameState.currentCustomer.fullnessCapacity) {
          gameState.currentCustomer.fullnessMeterBlocks[i].setFillStyle(0xDB533A);
          gameState.currentCustomer.fullnessMeterBlocks[i].setStrokeStyle(2, 0xB92E2E);
          }
        }
      }
    }
  }
1 Like

Nicely done! This is a really tough project and you’re making great progress!

Sounds good :laughing:

My man… I feel like I need your help once more… :sweat_smile:
Now I’m stuck at step 36…
So I’m asked to create a check for Enter, which I did as you can see below, and “move customer line”, which I assume means to call on the method I created in the previous step called moveCustomerLine.

 if (Phaser.Input.Keyboard.JustDown(gameState.keys.Enter)) {
        if (gameState.readyForNextOrder === false && gameState.customerIsReady === true) {
          console.log('enter');
          this.moveCustomerLine();
        }
      }

However, I get an error:

TypeError: undefined is not an object (evaluating ‘gameState.currentCustomer.meterContainer.visible = true;’)

And it relates to the onComplete from a tween from step 10, and my code for that is:

 if (gameState.readyForNextOrder === true) {
        gameState.readyForNextOrder = false;
        gameState.customerIsReady = false;
        gameState.currentCustomer = gameState.customers.children.entries[gameState.customersServedCount];
        this.add.tween(
          {
            targets: gameState.currentCustomer,
            duration: 100, 
            delay: 100, 
            angle: 90,
            x: gameState.player.x,
            ease: 'Power2',
            onComplete: function() {
              gameState.customerIsReady = true;
              gameState.currentCustomer.meterContainer.visible = true;
            }
          }
        );
      }

I feel like I’m moving at snail pace now… and it doesn’t help that the step descriptions have become a little less descriptive and therefore a bit more confusing…

Thanks for any help on this selectall!

That definitely happens, especially on this project because there are so many moving parts.

Before you add the tween and after you set the new value of gameState.currentCustomer, try adding a console.log() of it to see what you’re dealing with. Does it have the information you expect right now?

If it doesn’t, then you’ll need to figure out why gameState.customers.children.entries[gameState.customersServedCount] doesn’t have the value you expect. In that case, you could console.log() the entries array and customersServedCount and go from there if value is off.

Assuming the snippet you posted is in the update() function, I’m not seeing anything obvious from this, so hopefully the logs will help the investigation.

Hi Selectall,

I appreciate your help…

Logging customerServedCount works fine, it goes from 0 to 1. However, logging the entries array just gives me a huge list of things, and I’m unsure of what to look at really…

My code up to this point, if you feel like you want to inspect, and maybe find something…:

Summary
const gameState = {
  score: 0,
  starRating: 5,
  currentWaveCount: 1,
  customerIsReady: false,
  cam: {},
  gameSpeed: 3,
  currentMusic: {},
  totalWaveCount: 3,
  countdownTimer: 1500,
  readyForNextOrder: true,
  customersServedCount: 0
}

// Gameplay scene
class GameScene extends Phaser.Scene {
  constructor() {
    super({ key: 'GameScene' })
  }

  preload() {
    // Preload images
    const baseURL = 'https://content.codecademy.com/courses/learn-phaser/fastfoodie/';
    this.load.image('Chef', `${baseURL}art/Chef.png`);
    this.load.image('Customer-1', `${baseURL}art/Customer-1.png`);
    this.load.image('Customer-2', `${baseURL}art/Customer-2.png`);
    this.load.image('Customer-3', `${baseURL}art/Customer-3.png`);
    this.load.image('Customer-4', `${baseURL}art/Customer-4.png`);
    this.load.image('Customer-5', `${baseURL}art/Customer-5.png`);
    this.load.image('Floor-Server', `${baseURL}art/Floor-Server.png`);
    this.load.image('Floor-Customer', `${baseURL}art/Floor-Customer.png`);
    this.load.image('Tray', `${baseURL}art/Tray.png`);
    this.load.image('Barrier', `${baseURL}art/Barrier.png`);
    this.load.image('Star-full', `${baseURL}art/Star-full.png`);
    this.load.image('Star-half', `${baseURL}art/Star-half.png`);
    this.load.image('Star-empty', `${baseURL}art/Star-empty.png`);

    // Preload song
    this.load.audio('gameplayTheme', [
      `${baseURL}audio/music/2-gameplayTheme.ogg`,
      `${baseURL}audio/music/2-gameplayTheme.mp3`
    ]); // Credit: "Pixel Song #18" by hmmm101: https://freesound.org/people/hmmm101

    // Preload SFX
    this.load.audio('placeFoodSFX', [
      `${baseURL}audio/sfx/placeFood.ogg`,
      `${baseURL}audio/sfx/placeFood.mp3`
    ]); // Credit: "action_02.wav" by dermotte: https://freesound.org/people/dermotte

    this.load.audio('servingCorrectSFX', [
      `${baseURL}audio/sfx/servingCorrect.ogg`,
      `${baseURL}audio/sfx/servingCorrect.mp3`
    ]); // Credit: "Video Game SFX Positive Action Long Tail" by rhodesmas: https://freesound.org/people/djlprojects

    this.load.audio('servingIncorrectSFX', [
      `${baseURL}audio/sfx/servingIncorrect.ogg`,
      `${baseURL}audio/sfx/servingIncorrect.mp3`
    ]); // Credit: "Incorrect 01" by rhodesmas: https://freesound.org/people/rhodesmas

    this.load.audio('servingEmptySFX', [
      `${baseURL}audio/sfx/servingEmpty.ogg`,
      `${baseURL}audio/sfx/servingEmpty.mp3`
    ]); // Credit: "Computer Error Noise [variants of KevinVG207's Freesound#331912].wav" by Timbre: https://freesound.org/people/Timbre

    this.load.audio('fiveStarsSFX', [
      `${baseURL}audio/sfx/fiveStars.ogg`,
      `${baseURL}audio/sfx/fiveStars.mp3`
    ]); // Credit: "Success 01" by rhodesmas: https://freesound.org/people/rhodesmas

    this.load.audio('nextWaveSFX', [
      `${baseURL}audio/sfx/nextWave.ogg`,
      `${baseURL}audio/sfx/nextWave.mp3`
    ]); // Credit: "old fashion radio jingle 2.wav" by rhodesmas: https://freesound.org/people/chimerical
  }

  create() {
    // Stop, reassign, and play the new music
    gameState.currentMusic.stop();
    gameState.currentMusic = this.sound.add('gameplayTheme');
    gameState.currentMusic.play({ loop: true });

    // Assign SFX
    gameState.sfx = {};
    gameState.sfx.placeFood = this.sound.add('placeFoodSFX');
    gameState.sfx.servingCorrect = this.sound.add('servingCorrectSFX');
    gameState.sfx.servingIncorrect = this.sound.add('servingIncorrectSFX');
    gameState.sfx.servingEmpty = this.sound.add('servingEmptySFX');
    gameState.sfx.fiveStars = this.sound.add('fiveStarsSFX');
    gameState.sfx.nextWave = this.sound.add('nextWaveSFX');

    // Create environment sprites
    gameState.floorServer = this.add.sprite(gameState.cam.midPoint.x, 0, 'Floor-Server').setScale(0.5).setOrigin(0.5, 0);
    gameState.floorCustomer = this.add.sprite(gameState.cam.midPoint.x, gameState.cam.worldView.bottom, 'Floor-Customer').setScale(0.5).setOrigin(0.5, 1);
    gameState.table = this.add.sprite(gameState.cam.midPoint.x, gameState.cam.midPoint.y, 'Barrier').setScale(0.5);

    // Create player and tray sprites
    gameState.tray = this.add.sprite(gameState.cam.midPoint.x, gameState.cam.midPoint.y, 'Tray').setScale(0.5);
    gameState.player = this.add.sprite(gameState.cam.midPoint.x, 200, 'Chef').setScale(0.5);

    // Display the score
    gameState.scoreTitleText = this.add.text(gameState.cam.midPoint.x, 30, 'Score', { fontSize: '15px', fill: '#666666' }).setOrigin(0.5);
    gameState.scoreText = this.add.text(gameState.cam.midPoint.x, gameState.scoreTitleText.y + gameState.scoreTitleText.height + 20, gameState.score, { fontSize: '30px', fill: '#000000' }).setOrigin(0.5);

    // Display the wave count
    gameState.waveTitleText = this.add.text(gameState.cam.worldView.right - 20, 30, 'Wave', { fontSize: '64px', fill: '#666666' }).setOrigin(1, 1).setScale(0.25);
    gameState.waveCountText = this.add.text(gameState.cam.worldView.right - 20, 30, gameState.currentWaveCount + '/' + gameState.totalWaveCount, { fontSize: '120px', fill: '#000000' }).setOrigin(1, 0).setScale(0.25);

    // Display number of customers left
    gameState.customerCountText = this.add.text(gameState.cam.worldView.right - 20, 80, `Customers left: ${gameState.customersLeftCount}`, { fontSize: '15px', fill: '#000000' }).setOrigin(1);
    
    // Generate wave group
    gameState.customers = this.add.group();
    this.generateWave();

    //step 18
    gameState.currentMeal = this.add.group();
    //step 19
    gameState.currentMeal.fullnessValue = 0;

    //step 24
    gameState.keys.Enter = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.ENTER);
    gameState.keys.A = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.A);
    gameState.keys.S = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.S);
    gameState.keys.D = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.D);

  }

  update() {
    //step 10
    if (gameState.readyForNextOrder === true) {
        gameState.readyForNextOrder = false;
        gameState.customerIsReady = false;
        gameState.currentCustomer = gameState.customers.children.entries[gameState.customersServedCount];
        this.add.tween(
          {
            targets: gameState.currentCustomer,
            duration: 100, 
            delay: 100, 
            angle: 90,
            x: gameState.player.x,
            ease: 'Power2',
            onComplete: function() {
              gameState.customerIsReady = true;
              //step 23
              gameState.currentCustomer.meterContainer.visible = true;
              
            }
          }
        );
      }
      //step 34
      if (Phaser.Input.Keyboard.JustDown(gameState.keys.A)) {
        this.placeFood('Burger', 5);
      } else if (Phaser.Input.Keyboard.JustDown(gameState.keys.S)) {
        this.placeFood('Fries', 3);
      } else if (Phaser.Input.Keyboard.JustDown(gameState.keys.D)) {
        this.placeFood('Shake', 1);
      } else if (Phaser.Input.Keyboard.JustDown(gameState.keys.Enter)) {
        if (gameState.readyForNextOrder === false && gameState.customerIsReady === true) {
          console.log('enter');
          this.moveCustomerLine();
        }
      }
    }


  /* WAVES */
  // Generate wave
  generateWave() {
    // Add the total number of customers per wave here:
    gameState.totalCustomerCount = Math.ceil(Math.random() * gameState.currentWaveCount);

    this.updateCustomerCountText();

    for (let i = 0; i < gameState.totalCustomerCount; i++) {
      // Create your container below and add your customers to it below:
      let customerContainer = this.add.container(gameState.cam.worldView.right + (200 * i), gameState.cam.worldView.bottom - 140);
      gameState.customers.add(customerContainer);


      // Customer sprite randomizer
      let customerImageKey = Math.ceil(Math.random() * 5);

      // Draw customers here!
      //step 6
      let customer = this.add.sprite(0, 0, `Customer-${customerImageKey}`).setScale(0.5);

      //step 7
      customerContainer.add(customer);
      
      // Fullness meter container
      customerContainer.fullnessMeter = this.add.group();

      // Define capacity
      customerContainer.fullnessCapacity = Math.ceil(Math.random() * 5 * gameState.totalWaveCount);

      // If capacity is an impossible number, reshuffle it until it isn't
      while (customerContainer.fullnessCapacity === 12 || customerContainer.fullnessCapacity === 14) {
        customerContainer.fullnessCapacity = Math.ceil(Math.random() * 5) * gameState.totalWaveCount;
      }

      // Edit the meterWidth
      //step 20
      //let meterWidth = 200;
      let meterWidth = customerContainer.fullnessCapacity * 10;
      customerContainer.meterContainer = this.add.container(0, customer.y + (meterWidth / 2));
      
      // Add the customerContainer.meterContainer to customerContainer
      //step 21
      customerContainer.add(customerContainer.meterContainer);

      // Add meter base
      customerContainer.meterBase = this.add.rectangle(-130, customer.y, meterWidth, 33, 0x707070).setOrigin(0);
      customerContainer.meterBase.setStrokeStyle(6, 0x707070);
      customerContainer.meterBase.angle = -90;
      customerContainer.meterContainer.add(customerContainer.meterBase);

      // Add timer countdown meter body
      customerContainer.timerMeterBody = this.add.rectangle(customerContainer.meterBase.x + 22, customer.y + 1, meterWidth + 4, 12, 0x3ADB40).setOrigin(0);
      customerContainer.timerMeterBody.angle = -90;
      customerContainer.meterContainer.add(customerContainer.timerMeterBody);

      // Create container for individual fullness blocks
      customerContainer.fullnessMeterBlocks = [];

      // Create fullness meter blocks
      for (let j = 0; j < customerContainer.fullnessCapacity; j++) {
        customerContainer.fullnessMeterBlocks[j] = this.add.rectangle(customerContainer.meterBase.x, customer.y - (10 * j), 10, 20, 0xDBD53A).setOrigin(0);
        customerContainer.fullnessMeterBlocks[j].setStrokeStyle(2, 0xB9B42E);
        customerContainer.fullnessMeterBlocks[j].angle = -90;
        customerContainer.fullnessMeter.add(customerContainer.fullnessMeterBlocks[j]);
        customerContainer.meterContainer.add(customerContainer.fullnessMeterBlocks[j]);
      }

      // Hide meters
      customerContainer.meterContainer.visible = false;
    }
  }

  updateCustomerCountText() {
    gameState.customersLeftCount = gameState.totalCustomerCount - gameState.customersServedCount;
    gameState.customerCountText.setText(`Customers left: ${gameState.customersLeftCount}`);
  }

  placeFood(food, fullnessValue) {
    if (gameState.currentMeal.children.entries.length < 3 && gameState.customerIsReady === true) {
      let Xposition = gameState.cam.midPoint.x;

      switch(food) {
        case 'Burger':
          gameState.currentMeal.create(Xposition - 90, gameState.cam.midPoint.y, 'Burger').setScale(0.5);
          break;
        case 'Fries':
          gameState.currentMeal.create(Xposition, gameState.cam.midPoint.y, 'Fries').setScale(0.5);
          break; 
        case 'Shake':
          gameState.currentMeal.create(Xposition + 90, gameState.cam.midPoint.y, 'Shake').setScale(0.5);
          break;   
      }
      //step 28
      gameState.currentMeal.fullnessValue += fullnessValue;

      //step33
      gameState.sfx.placeFood.play();

      //step 29
      for (let i = 0; i < gameState.currentMeal.fullnessValue; i++) {
        if (i < gameState.currentCustomer.fullnessCapacity) { 
          if (gameState.currentMeal.fullnessValue < gameState.currentCustomer.fullnessCapacity) {
          gameState.currentCustomer.fullnessMeterBlocks[i].setFillStyle(0xFFFA81);
        } else if (gameState.currentMeal.fullnessValue === gameState.currentCustomer.fullnessCapacity) {
          gameState.currentCustomer.fullnessMeterBlocks[i].setFillStyle(0x3ADB40);
          gameState.currentCustomer.fullnessMeterBlocks[i].setStrokeStyle(2, 0x2EB94E);
        } else if (gameState.currentMeal.fullnessValue > gameState.currentCustomer.fullnessCapacity) {
          gameState.currentCustomer.fullnessMeterBlocks[i].setFillStyle(0xDB533A);
          gameState.currentCustomer.fullnessMeterBlocks[i].setStrokeStyle(2, 0xB92E2E);
          }
        }
      }
    }
  }

  moveCustomerLine() {
    gameState.currentMeal.clear(true);
    gameState.currentMeal.fullnessValue = 0;
    gameState.customersServedCount += 1;
    gameState.readyForNextOrder = true;

    
  }
}

I tried out your code and there were more clues hidden in those logs.

gameState.currentCustomer may have looked good for the first customer, but as soon as you got to the second customer, it logs as undefined.

As you mentioned, customerServedCount goes from 0 to 1, but there isn’t another customer in gameState.customers.children.entries to get. You may not have gotten to the part of the game where you’re checking for end state since it was being done in waves, but it is curious that you only have one customer.

If we log gameState to see what properties we have, there is one called totalCustomerCount, which also shows as 1, so it makes sense there is only 1 entry, even if it isn’t what you want.

Since we know totalCustomerCount gets set to 1 and there is only 1 customer created, you need to double check where that gets set and they get generated, which is in generateWave(). You generate a random number for the number of customers for the wave, but it will always be 1 for wave 1 with your current formula. Consider adjusting this

You’ll be adding logic to handle when you finished the wave later, but right now your ability to test is limited since you only have 1 customer.

Selectall, you are a god send!
I’ve adjusted the formula to determine the number of customers in a wave to:

  generateWave() {
    // Add the total number of customers per wave here:
    gameState.totalCustomerCount = Math.ceil((Math.random() * 10) * gameState.currentWaveCount);

I think this is how it was meant to look, but the step and the hint had me a little confused. I think it worked as intended but limiting the amount of customer per wave to not be higher than the wave level, however it meant wave 1 would always cause problems, until I reached the later steps where waves are destroyed etc.

Again, thank you very much for your help, you gave me an idea where to look!

Hi Selectall,

Still trying to progress trough this believe it or not… :sweat_smile:

Got down to step 39, so not long left now, and just struggling with some logic.

I’m asked to create tweens for the customers to be served, however I don’t see a way of storing that to use as a target for the tween. I’m also not entirely sure if I’m placing it correctly within the code…

Below is my update method

Summary
  update() {
    //step 10
    if (gameState.readyForNextOrder === true) {
        gameState.readyForNextOrder = false;
        gameState.customerIsReady = false;


          for(let i = 0; i < gameState.customersServedCount; i++){
            this.tweens.add(
              {
                targets: gameState.currentCustomer,
                duration: 750,
                x: '-=300',
                angle: 0,
                onStart: () => {
                    gameState.customers.children.entries[i].meterContainer.visible = false;
                }
              });
              //step 28
            this.tweens.add({
                targets: gameState.customers.children.entries[i],
                duration: 750,
                x: '-=300',
                angle: 0
            }); 
          }

        gameState.currentCustomer = gameState.customers.children.entries[gameState.customersServedCount];
        this.add.tween(
          {
            targets: gameState.currentCustomer,
            duration: 100, 
            delay: 100, 
            angle: 90,
            x: gameState.player.x,
            ease: 'Power2',
            onComplete: function() {
              gameState.customerIsReady = true;
              //step 23
              gameState.currentCustomer.meterContainer.visible = true;   
            }
          }
        );
        //step 39
        for (let i = 0; i < gameState.customersLeftCount; i++) {
          this.tweens.add(
            {
              targets: ,
              delay: 200,
              x: '-=200',
              duration: 1500
            }
          )
        }
      }

Any help would be appreciated.

You’re getting really close now. Some hints for handling this part:

  • The loop runs once for each customer remaining
  • You have access to the customers via the gameState.customers.children.entries array, but you’ll need to use some math to figure out the right index to use
  • There are different methods to calculate the right index, but consider some of the variables you have and decide how you want to go about it. Some of them to consider
    gameState.customersServedCount
    gameState.customersLeftCount
    gameState.customers.children.entries.length
    i

Once you have the right index, then you can use that to get the target that you need for the tween. Maybe even store the customer object in a locally scoped variable for convenience. Something like

for loop that you have
  let nextCustomer = gameState.customers.children.entries[magic_index_calculation_here];
  this.tweens.add, etc. etc.
end for

I don’t recall if the instructions tell you exactly how they want you to calculate the index to use, but hopefully the hints above will get you going in the right direction to calculate it.

Here is how I did it

You shouldn’t dig deeper in here until you’ve already tried it yourself.

Only open if you're truly stuck

Are you sure?

let nextCustomer = gameState.customers.children.entries[gameState.customersServedCount + i + 1];

Haha I like your spoiler derail…

I ended up having to look up your spoiler anyway as it appears I didn’t quite understand the pre-written code a 100% in relation to how the customer container was created, and so couldn’t understand my way around the tween targets… I think I get it now though!

Thanks a lot, once again :sweat_smile: :sweat_smile: :sweat_smile:

Oh Selectall! I summon thee!.. ekhm… Hi, I need your guidance.

I’m on step 45… now… first two points a bit confusing, as there is pre-written code that already does that? - So I skipped that and I’m on the point 3 and 4, creating the timer, and shrinking the timer.

So I thought I would start the timer within the tween to when a customer is facing the chef with onStart, code below… but I don’t really know how to go about the whole ‘timer’, because I initially thought I could change the gameState.currentCustomer.timerMeterBody.width. But I’ve not had luck, and looking at documentation it’s not possible? So yea… don’t know how to create a timer, and shrink the bar…

this.add.tween(
          {
            targets: gameState.currentCustomer,
            duration: 1000, 
            delay: 100, 
            angle: 90,
            x: gameState.player.x,
            ease: 'Power2',
            onStart: () => {
              console.log(gameState.currentCustomer.timerMeterBody.width);
            },
            onComplete: function() {
              gameState.customerIsReady = true;
              //step 23
              gameState.currentCustomer.meterContainer.visible = true;   
            }
          }
        );

Customers won’t wait forever to be served. Express the concept of customers’ patience in your game using a timed meter that goes down to zero.

  • Draw a background for each customer’s timer meter under the fullness meter
  • Draw a bar to represent the timer
  • Create a timer once the customer has moved up to the chef.
  • Shrink the timer bar as the customer loses patience
  • Change the color of the timer as the customer is closer to leaving: green ( 0x3ADB40 ) when there’s lots of time, orange ( 0xFF9D00 ) at the 25% mark, and red ( 0xDB533A ) if the timer is 75% complete.
  • Automatically run moveCustomerLine() when the timer reaches zero.
  • Remove the timer if the customer has been manually served before time runs out.

Full code for reference if you need…

Summary
const gameState = {
  score: 0,
  starRating: 0,
  currentWaveCount: 1,
  customerIsReady: false,
  cam: {},
  gameSpeed: 3,
  currentMusic: {},
  totalWaveCount: 3,
  countdownTimer: 1500,
  readyForNextOrder: true,
  customersServedCount: 0
}

// Gameplay scene
class GameScene extends Phaser.Scene {
  constructor() {
    super({ key: 'GameScene' })
  }

  preload() {
    // Preload images
    const baseURL = 'https://content.codecademy.com/courses/learn-phaser/fastfoodie/';
    this.load.image('Chef', `${baseURL}art/Chef.png`);
    this.load.image('Customer-1', `${baseURL}art/Customer-1.png`);
    this.load.image('Customer-2', `${baseURL}art/Customer-2.png`);
    this.load.image('Customer-3', `${baseURL}art/Customer-3.png`);
    this.load.image('Customer-4', `${baseURL}art/Customer-4.png`);
    this.load.image('Customer-5', `${baseURL}art/Customer-5.png`);
    this.load.image('Floor-Server', `${baseURL}art/Floor-Server.png`);
    this.load.image('Floor-Customer', `${baseURL}art/Floor-Customer.png`);
    this.load.image('Tray', `${baseURL}art/Tray.png`);
    this.load.image('Barrier', `${baseURL}art/Barrier.png`);
    this.load.image('Star-full', `${baseURL}art/Star-full.png`);
    this.load.image('Star-half', `${baseURL}art/Star-half.png`);
    this.load.image('Star-empty', `${baseURL}art/Star-empty.png`);

    // Preload song
    this.load.audio('gameplayTheme', [
      `${baseURL}audio/music/2-gameplayTheme.ogg`,
      `${baseURL}audio/music/2-gameplayTheme.mp3`
    ]); // Credit: "Pixel Song #18" by hmmm101: https://freesound.org/people/hmmm101

    // Preload SFX
    this.load.audio('placeFoodSFX', [
      `${baseURL}audio/sfx/placeFood.ogg`,
      `${baseURL}audio/sfx/placeFood.mp3`
    ]); // Credit: "action_02.wav" by dermotte: https://freesound.org/people/dermotte

    this.load.audio('servingCorrectSFX', [
      `${baseURL}audio/sfx/servingCorrect.ogg`,
      `${baseURL}audio/sfx/servingCorrect.mp3`
    ]); // Credit: "Video Game SFX Positive Action Long Tail" by rhodesmas: https://freesound.org/people/djlprojects

    this.load.audio('servingIncorrectSFX', [
      `${baseURL}audio/sfx/servingIncorrect.ogg`,
      `${baseURL}audio/sfx/servingIncorrect.mp3`
    ]); // Credit: "Incorrect 01" by rhodesmas: https://freesound.org/people/rhodesmas

    this.load.audio('servingEmptySFX', [
      `${baseURL}audio/sfx/servingEmpty.ogg`,
      `${baseURL}audio/sfx/servingEmpty.mp3`
    ]); // Credit: "Computer Error Noise [variants of KevinVG207's Freesound#331912].wav" by Timbre: https://freesound.org/people/Timbre

    this.load.audio('fiveStarsSFX', [
      `${baseURL}audio/sfx/fiveStars.ogg`,
      `${baseURL}audio/sfx/fiveStars.mp3`
    ]); // Credit: "Success 01" by rhodesmas: https://freesound.org/people/rhodesmas

    this.load.audio('nextWaveSFX', [
      `${baseURL}audio/sfx/nextWave.ogg`,
      `${baseURL}audio/sfx/nextWave.mp3`
    ]); // Credit: "old fashion radio jingle 2.wav" by rhodesmas: https://freesound.org/people/chimerical
  }

  create() {
    // Stop, reassign, and play the new music
    gameState.currentMusic.stop();
    gameState.currentMusic = this.sound.add('gameplayTheme');
    gameState.currentMusic.play({ loop: true });

    // Assign SFX
    gameState.sfx = {};
    gameState.sfx.placeFood = this.sound.add('placeFoodSFX');
    gameState.sfx.servingCorrect = this.sound.add('servingCorrectSFX');
    gameState.sfx.servingIncorrect = this.sound.add('servingIncorrectSFX');
    gameState.sfx.servingEmpty = this.sound.add('servingEmptySFX');
    gameState.sfx.fiveStars = this.sound.add('fiveStarsSFX');
    gameState.sfx.nextWave = this.sound.add('nextWaveSFX');

    // Create environment sprites
    gameState.floorServer = this.add.sprite(gameState.cam.midPoint.x, 0, 'Floor-Server').setScale(0.5).setOrigin(0.5, 0);
    gameState.floorCustomer = this.add.sprite(gameState.cam.midPoint.x, gameState.cam.worldView.bottom, 'Floor-Customer').setScale(0.5).setOrigin(0.5, 1);
    gameState.table = this.add.sprite(gameState.cam.midPoint.x, gameState.cam.midPoint.y, 'Barrier').setScale(0.5);

    // Create player and tray sprites
    gameState.tray = this.add.sprite(gameState.cam.midPoint.x, gameState.cam.midPoint.y, 'Tray').setScale(0.5);
    gameState.player = this.add.sprite(gameState.cam.midPoint.x, 200, 'Chef').setScale(0.5);

    // Display the score
    gameState.scoreTitleText = this.add.text(gameState.cam.midPoint.x, 30, 'Score', { fontSize: '15px', fill: '#666666' }).setOrigin(0.5);
    gameState.scoreText = this.add.text(gameState.cam.midPoint.x, gameState.scoreTitleText.y + gameState.scoreTitleText.height + 20, gameState.score, { fontSize: '30px', fill: '#000000' }).setOrigin(0.5);

    // Display the wave count
    gameState.waveTitleText = this.add.text(gameState.cam.worldView.right - 20, 30, 'Wave', { fontSize: '64px', fill: '#666666' }).setOrigin(1, 1).setScale(0.25);
    gameState.waveCountText = this.add.text(gameState.cam.worldView.right - 20, 30, gameState.currentWaveCount + '/' + gameState.totalWaveCount, { fontSize: '120px', fill: '#000000' }).setOrigin(1, 0).setScale(0.25);

    // Display number of customers left
    gameState.customerCountText = this.add.text(gameState.cam.worldView.right - 20, 80, `Customers left: ${gameState.customersLeftCount}`, { fontSize: '15px', fill: '#000000' }).setOrigin(1);
    
    // Generate wave group
    gameState.customers = this.add.group();
    this.generateWave();

    //step 18
    gameState.currentMeal = this.add.group();
    //step 19
    gameState.currentMeal.fullnessValue = 0;

    //step 24
    gameState.keys.Enter = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.ENTER);
    gameState.keys.A = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.A);
    gameState.keys.S = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.S);
    gameState.keys.D = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.D);

    gameState.starGroup = this.add.group();
    this.drawStars();
  }

  update() {
    //step 10
    if (gameState.readyForNextOrder === true) {
        gameState.readyForNextOrder = false;
        gameState.customerIsReady = false;


          for(let i = 0; i < gameState.customersServedCount; i++){
            this.tweens.add(
              {
                targets: gameState.currentCustomer,
                duration: 750,
                x: '-=300',
                angle: 0,
                onStart: () => {
                    gameState.customers.children.entries[i].meterContainer.visible = false;
                }
              });
              //step 28
            this.tweens.add({
                targets: gameState.customers.children.entries[i],
                duration: 750,
                x: '-=300',
                angle: 0
            }); 
          }

        gameState.currentCustomer = gameState.customers.children.entries[gameState.customersServedCount];
        this.add.tween(
          {
            targets: gameState.currentCustomer,
            duration: 1000, 
            delay: 100, 
            angle: 90,
            x: gameState.player.x,
            ease: 'Power2',
            onStart: () => {
              console.log(gameState.currentCustomer.timerMeterBody.width);
            },
            onComplete: function() {
              gameState.customerIsReady = true;
              //step 23
              gameState.currentCustomer.meterContainer.visible = true;   
            }
          }
        );
        //step 39
        for (let i = 0; i < gameState.customersLeftCount; i++) {
          let nextCustomer = gameState.customers.children.entries[gameState.customersServedCount + i + 1];
          this.tweens.add(
            {
              targets: nextCustomer,
              delay: 200,
              x: '-=200',
              duration: 1500
            }
          )
        }

      }
      //step 34
      if (Phaser.Input.Keyboard.JustDown(gameState.keys.A)) {
        this.placeFood('Burger', 5);
      } else if (Phaser.Input.Keyboard.JustDown(gameState.keys.S)) {
        this.placeFood('Fries', 3);
      } else if (Phaser.Input.Keyboard.JustDown(gameState.keys.D)) {
        this.placeFood('Shake', 1);
      } else if (Phaser.Input.Keyboard.JustDown(gameState.keys.Enter)) {
        if (gameState.readyForNextOrder === false && gameState.customerIsReady === true) {
          this.moveCustomerLine();
          this.updateCustomerCountText();
        }
      }
    }

  drawStars() {
    gameState.starGroup.children.entries.forEach((star) => { star.destroy()});
    for (let i = 0; i < gameState.starRating; i++) {
      let spacer = i * 50;
      gameState.starGroup.create(20 + spacer, 20, 'Star-full').setScale(.5).setOrigin(0);
    }
  }  

  /* WAVES */
  // Generate wave
  generateWave() {
    console.log('Looped: ')
    // Add the total number of customers per wave here:
    gameState.totalCustomerCount = Math.ceil((Math.random() * 10) * gameState.currentWaveCount);

    this.updateCustomerCountText();

    for (let i = 0; i < gameState.totalCustomerCount; i++) {
      // Create your container below and add your customers to it below:
      let customerContainer = this.add.container(gameState.cam.worldView.right + (200 * i), gameState.cam.worldView.bottom - 140);
      gameState.customers.add(customerContainer);


      // Customer sprite randomizer
      let customerImageKey = Math.ceil(Math.random() * 5);

      // Draw customers here!
      //step 6
      let customer = this.add.sprite(0, 0, `Customer-${customerImageKey}`).setScale(0.5);

      //step 7
      customerContainer.add(customer);
      
      // Fullness meter container
      customerContainer.fullnessMeter = this.add.group();

      // Define capacity
      customerContainer.fullnessCapacity = Math.ceil(Math.random() * 5 * gameState.totalWaveCount);

      // If capacity is an impossible number, reshuffle it until it isn't
      while (customerContainer.fullnessCapacity === 12 || customerContainer.fullnessCapacity === 14) {
        customerContainer.fullnessCapacity = Math.ceil(Math.random() * 5) * gameState.totalWaveCount;
      }

      // Edit the meterWidth
      //step 20
      //let meterWidth = 200;
      let meterWidth = customerContainer.fullnessCapacity * 10;
      customerContainer.meterContainer = this.add.container(0, customer.y + (meterWidth / 2));
      
      // Add the customerContainer.meterContainer to customerContainer
      //step 21
      customerContainer.add(customerContainer.meterContainer);

      // Add meter base
      customerContainer.meterBase = this.add.rectangle(-130, customer.y, meterWidth, 33, 0x707070).setOrigin(0);
      customerContainer.meterBase.setStrokeStyle(6, 0x707070);
      customerContainer.meterBase.angle = -90;
      customerContainer.meterContainer.add(customerContainer.meterBase);

      //step 45??

      // Add timer countdown meter body
      customerContainer.timerMeterBody = this.add.rectangle(customerContainer.meterBase.x + 22, customer.y + 1, meterWidth + 4, 12, 0x3ADB40).setOrigin(0);
      customerContainer.timerMeterBody.angle = -90;
      customerContainer.meterContainer.add(customerContainer.timerMeterBody);

      // Create container for individual fullness blocks
      customerContainer.fullnessMeterBlocks = [];

      // Create fullness meter blocks
      for (let j = 0; j < customerContainer.fullnessCapacity; j++) {
        customerContainer.fullnessMeterBlocks[j] = this.add.rectangle(customerContainer.meterBase.x, customer.y - (10 * j), 10, 20, 0xDBD53A).setOrigin(0);
        customerContainer.fullnessMeterBlocks[j].setStrokeStyle(2, 0xB9B42E);
        customerContainer.fullnessMeterBlocks[j].angle = -90;
        customerContainer.fullnessMeter.add(customerContainer.fullnessMeterBlocks[j]);
        customerContainer.meterContainer.add(customerContainer.fullnessMeterBlocks[j]);
      }

      // Hide meters
      customerContainer.meterContainer.visible = false;
    }
  }

  updateCustomerCountText() {
    gameState.customersLeftCount = gameState.totalCustomerCount - gameState.customersServedCount;
    gameState.customerCountText.setText(`Customers left: ${gameState.customersLeftCount}`);
  }

  placeFood(food, fullnessValue) {
    if (gameState.currentMeal.children.entries.length < 3 && gameState.customerIsReady === true) {
      let Xposition = gameState.cam.midPoint.x;

      switch(food) {
        case 'Burger':
          gameState.currentMeal.create(Xposition - 90, gameState.cam.midPoint.y, 'Burger').setScale(0.5);
          break;
        case 'Fries':
          gameState.currentMeal.create(Xposition, gameState.cam.midPoint.y, 'Fries').setScale(0.5);
          break; 
        case 'Shake':
          gameState.currentMeal.create(Xposition + 90, gameState.cam.midPoint.y, 'Shake').setScale(0.5);
          break;   
      }
      //step 28
      gameState.currentMeal.fullnessValue += fullnessValue;

      //step33
      gameState.sfx.placeFood.play();

      //step 29
      for (let i = 0; i < gameState.currentMeal.fullnessValue; i++) {
        if (i < gameState.currentCustomer.fullnessCapacity) { 
          if (gameState.currentMeal.fullnessValue < gameState.currentCustomer.fullnessCapacity) {
          gameState.currentCustomer.fullnessMeterBlocks[i].setFillStyle(0xFFFA81);
        } else if (gameState.currentMeal.fullnessValue === gameState.currentCustomer.fullnessCapacity) {
          gameState.currentCustomer.fullnessMeterBlocks[i].setFillStyle(0x3ADB40);
          gameState.currentCustomer.fullnessMeterBlocks[i].setStrokeStyle(2, 0x2EB94E);
        } else if (gameState.currentMeal.fullnessValue > gameState.currentCustomer.fullnessCapacity) {
          gameState.currentCustomer.fullnessMeterBlocks[i].setFillStyle(0xDB533A);
          gameState.currentCustomer.fullnessMeterBlocks[i].setStrokeStyle(2, 0xB92E2E);
          }
        }
      }
    }
  }

  updateStars(fullnessValue, fullnessCapacity) {
    if (fullnessValue === fullnessCapacity) {
      gameState.currentCustomer.list[0].setTint(0x3ADB40);
      gameState.sfx.servingCorrect.play();
      gameState.score += 100;
      gameState.scoreText.setText(`Score: ${gameState.score}`);
      if (gameState.starRating < 5) {
        gameState.starRating++;
        if (gameState.starRating === 5) {
          gameState.sfx.fiveStars.play();
       };
      }; 

    } else if (fullnessValue < fullnessCapacity) { 
      gameState.currentCustomer.list[0].setTint(0xDB533A);
      gameState.sfx.servingIncorrect.play();
      gameState.starRating -= 2;
      if ( gameState.starRating < 0) {
        gameState.starRating = 0;
      }
    } else if (fullnessValue > fullnessCapacity) {
      gameState.currentCustomer.list[0].setTint(0xDB9B3A);
      gameState.sfx.servingEmpty.play();
      gameState.starRating -= 1;
      if ( gameState.starRating < 0) {
        gameState.starRating = 0;
      }
    }
    this.drawStars();
  }

  moveCustomerLine() {
    this.updateStars(gameState.currentMeal.fullnessValue, gameState.currentCustomer.fullnessCapacity);
    gameState.currentMeal.clear(true);
    gameState.currentMeal.fullnessValue = 0;
    gameState.customersServedCount += 1;
    gameState.readyForNextOrder = true;
    

    
  }
}

Edit: I tried creating a new tween within the onStart with the timerMeterBody.width as target, and a duration of 4000ms, with an onComplete to call moveCustomerLine(), which actually worked in moving onto the next customer once the time ran out, however still don’t know how to shrink the bar, and reset the timer if moveCustomerLine was called manually…