Fast Foodie: Step 35: Uncaught TypeError

Hello. I am working on the Phaser Fast Foodie project. Here is the link: https://www.codecademy.com/projects/practice/fastfoodie
I keep getting uncaught reference error when I try to run game at Step 35, the problem may have occurred before then, but I wasn’t throwing any errors and everything seemed to work until then. I know the problem is somewhere within the placeFood() function or where I’ve placed said function. The exact error is:

GameScene.js:245 Uncaught TypeError: Cannot read properties of undefined (reading ‘setStrokeStyle’)
at GameScene.placeFood (GameScene.js:245:60)
at GameScene.update (GameScene.js:153:12)
at initialize.step (phaser.min.js:1:189436)
at initialize.update (phaser.min.js:1:388536)
at initialize.step (phaser.min.js:1:733324)
at initialize.step (phaser.min.js:1:448455)
at e (phaser.min.js:1:445807)

I think it has something to do with the placement of my function, I started with it at the top, right below updateCustomerCountText(), or it could be this conditional, i === gameState.currentCustomer.fullnessCapacity.
Here is my code:

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' })
  }

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

  
  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();
    gameState.currentMeal = this.add.group();

    gameState.currentMeal.fullnessValue = 0;

    gameState.keys.Enter = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.ENTER);
    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() {
    if (gameState.readyForNextOrder) {
      gameState.readyForNextOrder = false;
      gameState.customerIsReady = false;

      gameState.currentCustomer = gameState.customers.children.entries[gameState.customersServedCount];
      let tween = this.tweens.add({
        targets: gameState.currentCustomer,
        duration: 1000,
        delay: 100,
        angle: 90,
        x: gameState.player.x,
        ease: 'Power2',
        onComplete: function() {
          gameState.customerIsReady = true;
          gameState.currentCustomer.meterContainer.visible = true;
        }
      });
    }
    if (Phaser.Input.Keyboard.KeyCodes.A) {
      this.placeFood('Burger', 5);
    } else if (Phaser.Input.Keyboard.KeyCodes.S) {
      this.placeFood('Fries', 3);
    } else if (Phaser.Input.Keyboard.KeyCodes.D) {
      this.placeFood('Shake', 1);
    }
  }

  /* WAVES */
  // Generate wave
  generateWave() {
    // 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:
      const 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!
      let customer = this.add.sprite(0, 0, `Customer-${customerImageKey}`).setScale(0.5);

      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
      let meterWidth = customerContainer.fullnessCapacity * 10;
      customerContainer.meterContainer = this.add.container(0, customer.y + (meterWidth / 2));
      
      // Add the customerContainer.meterContainer to customerContainer
      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;
    }
  }
  placeFood(food, fullnessValue) {
    if (gameState.currentMeal.children.entries.length < 3 && gameState.customerIsReady === true) {
      let Xposition = gameState.tray.x;

      switch (gameState.currentMeal.children.entries.length) {
        case 0:
          Xposition -= 90;
          break;
        case 2:
          Xposition += 100;
          break;
      }
      gameState.currentMeal.create(Xposition, gameState.tray.y, food).setScale(0.6);
      gameState.currentMeal.fullnessValue += fullnessValue;
      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);
        }
      };
      gameState.sfx.placeFood.play();
    }
  }
}

I would really appreciate any guidance that anyone has. I have posted before looking for help with this project, but no one ever responds. If anyone notices something wrong with my post that could be deterring people, please let me know so that I may improve. I have tried to provide all relevant info here so it got a little long.
Thanks!

New development… it works sometimes, but as soon as the customer gets to the counter 3 burgers are placed and no other keys work. Maybe something with my listener???

Hi,
I don’t think there’s anything wrong with how you’ve laid out your post, it’s probably just bad luck that the people who could help either haven’t seen it or aren’t familiar with the project/phaser.

I just compared your code to what I’d done, and unfortunately found out that I must have got distracted half-way through because I hadn’t finished it. Not sure why, might be that music, but anyway;

I think it’s this bit here that may be an issue;

    if (Phaser.Input.Keyboard.KeyCodes.A) {
      this.placeFood('Burger', 5);
    } else if (Phaser.Input.Keyboard.KeyCodes.S) {
      this.placeFood('Fries', 3);
    } else if (Phaser.Input.Keyboard.KeyCodes.D) {
      this.placeFood('Shake', 1);
    }

I think the condition should be along the lines of;
if (Phaser.Input.Keyboard.JustDown(gameState.keys.A) {

(incidentally, just above it, you’ve created the properties for keys S and D, but missed A.)

As I say, I’ve not finished it so I can’t test it - but now I’ve noticed it I’m going to try and find some time over the next day or two to have another go,

Hopefully that helps

Hey! Thank you so much! That music is pretty terrible, huh? So, I changed that part and I’m getting Uncaught TypeError. When you finish it, please reach out. I’m gonna keep trying things. Thanks again for your help.
This is my new error:
Uncaught TypeError: Cannot read properties of undefined (reading ‘_justDown’)
at t.exports [as JustDown] (phaser.min.js:1:614681)
at GameScene.update (GameScene.js:152:31)
at initialize.step (phaser.min.js:1:189436)
at initialize.update (phaser.min.js:1:388536)
at initialize.step (phaser.min.js:1:733324)
at initialize.step (phaser.min.js:1:448455)
at e (phaser.min.js:1:445807)
Here is my new code:

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);
    }

Did you catch the bit about missing creating the property for A key a bit further up ?
i.e

 gameState.keys.A = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.A);

The ‘Uncaught TypeError’ is usually when it’s trying to use a property before it’s been defined.

And, yes, if I see anything else I shall return
Have fun

UMMMM… YOU ARE THE PHASER GOD!!! Hopefully I can get through these last steps. Thank you!

1 Like

Hi! So, I’ve got it working some of the time, other times it throws this error:
Uncaught TypeError: Cannot read properties of undefined (reading ‘setFillStyle’)
at GameScene.placeFood (GameScene.js:248:60)
at GameScene.update (GameScene.js:154:12)
at initialize.step (phaser.min.js:1:189436)
at initialize.update (phaser.min.js:1:388536)
at initialize.step (phaser.min.js:1:733324)
at initialize.step (phaser.min.js:1:448455)
at e (phaser.min.js:1:445807)
Originally, it was catching it at line 245. This is when I changed my counter to gameState.currentMeal.fullnessValue and it worked, now it catches here. I tried changing that one too, and it didn’t fix it. I really think my problem is in this block.:

for (let i = 0; i < gameState.currentMeal.fullnessValue; i++) {
        if (i < 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 (i > gameState.currentCustomer.fullnessCapacity) {
          gameState.currentCustomer.fullnessMeterBlocks[i].setFillStyle(0xDB533A);
          gameState.currentCustomer.fullnessMeterBlocks[i].setStrokeStyle(2, 0xB92E2E);

Do you see anything? I’m not sure why changing the counter fixed the last error. It was just something I saw while combing the forums for a solution.

I completely changed that loop and it seems to be working better. Also, the problem seems to be when the customer is more than full. I am going to move on to the next steps to see if I get a resolution in another step. Still let me know if you see anything. Here’s the new loop:

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

Morning,
I don’t know if’s just me, but the instructions for this bit didn’t seem particularly clear.

Anyway, you’re intuition is right, when the customer is too full it’s trying to colour in the box outside of the customers block. If you change the first if statement to;
if (i < gameState.customerCustomer.fullnessCapacity)
that should sort that.
Also, I think the < and > in your other if statements might be the wrong way round.

It’s not just you, they are TERRIBLE!!! Thank you again!

Good afternoon. I’m working through 37 - 39 now. It’s very confusing. I’m adding tweens to make the line move. What I have right now makes everyone move, but they end up standing on top of one another. I’m on 38 and I have no idea how to target the previous customer. Here are the directions:
Use a tween to move customers down the line after they are fed. Therefore, if you’ve served at least one customer, create a tween for each previous customer to have:

  • Over a duration of 750 ms,
  • their x coordinate shift to the left 300px,
  • their angle set to 0.

These tweens should be added in the conditional that you added your previous tween.

Here is the code I’ve written for this section so far:

 if (gameState.readyForNextOrder) {
      gameState.readyForNextOrder = false;
      gameState.customerIsReady = false;

      // step 37
      for (let i = 0; i < gameState.customers; i++) {
        this.tweens.add({
          targets: gameState.customers[i],
          duration: 750,
          x: '-=300',
          angle: 0
        });
      };

      // Step 38
      this.tweens.add({
      
      });

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

I’ve put in a couple comments to show you where I’m stuck. When I first started I was going to create another loop like I had for #37, but I was thinking about it and I’m not sure if it’s necessary. If it is, I don’t know exactly what to loop through.
Thanks for always being so patient and kind with me. Let me know if you need to see more of my code.

Hello,
Terrible instructions strike again. It reads to me like they are both saying more or less the same thing!
Anywho, I have the loop going to
i < gameState.customersServedCount;
and then,
targets: gameState.customers.children.entries[i],

so, ‘i’ just runs through those in the array who’ve been served and pushes them left a bit each turn.

For 39:
I couldn’t get what they suggested to work well, so I went with
(just after gameState.currentCustomer = …)

      gameState.nextCustomer = gameState.customers.children.entries[gameState.customersServedCount + 1];
      
      if (gameState.nextCustomer) {
          this.tweens.add({
            targets: gameState.nextCustomer,
            x: 900,
            duration: 1500,
            delay: 200
          });
      };

You could also do with wrapping the bit after it (where you’re moving the customer to the counter) with an if statement;
if (gameState.customersLeftCount != 0) {
let tween = …
}
to check if there are any customers left before it tries to draw them.

Stay strong! We’re nearly there!

Hey, sorry I had to take a day or two away from this project. That definitely stopped them from stacking on top of each other. So, I am at 45. Everything works as far as not throwing any errors, but the logic seems to be messed up somewhere. What seems to be the fiveStars sound plays with each customer and they are all turning green. LOL. I was planning on working that out once I got through the rest of the steps. I am pretty confused about where to put this step.
Have you been able to finish it yet?

Hello!

I’ve finished it, more or less. (I couldn’t face trying to sort out the timer, and I occasionally get an random extra customer wander through without buying anything, but the rest works well enough…!). Just dragged on a little at the end, and every time I sat down, felt like most of the time was taken up working out where I’d got to.

For the stars, I have;

  updateStars(fullness, fullnessCapacity) {
    if (fullness === fullnessCapacity) {
      gameState.score += 100;
      gameState.currentCustomer.list[0].setTint(0x3ADB40);
      // play 'servingCorrect'
      gameState.scoreText.setText(`${gameState.score}`);
      if (gameState.starRating < 5) {
        gameState.starRating += 1;
        // play 'fiveStar' if  === 5
      } 
    } else if (fullness < fullnessCapacity) {
      gameState.currentCustomer.list[0].setTint(0xDB533A);
      // play 'servingIncorrect'
      gameState.starRating -= 2;
    } else {
      gameState.currentCustomer.list[0].setTint(0xDB9B3A);
      // play 'servingEmpty'
      gameState.starRating -= 1;
    };

    if (gameState.starRating < 1){
      gameState.currentMusic.stop();
      this.scene.stop('GameScene');
      this.scene.start('LoseScene');
    };

    this.drawStars();
  }

then call it on the first line of moveCustomerLine() with gameState.currentMeal.fullnessValue and gameState.currentCustomer.fullnessCapacity as the paramters.

Hope that helps.

It does. Thank you so much for pretty much holding my hand through this project. You’ve been extremely kind and patient. Happy coding…
Jess

1 Like