Check out a free preview of the full Complete Front-End Project: Build a Game course:
The "Adding New States" Lesson is part of the full, Complete Front-End Project: Build a Game course featured in this preview video. Here's what you'd learn in this lesson:

Brian demonstrates how to create new states, and refactors the gameState.js file by adding new actions that the fox pet can execute once it is awake.

Get Unlimited Access Now

Transcript from the "Adding New States" Lesson

[00:00:00]
>> So now we have a running clock, we have user inactivity, and we have the ability to respond to a user saying feed or change the weather or something like that. So let's just start at our finite state machine moving, right, and get it in motion. So the first thing I wanna do is I wanna make a startGame function.

[00:00:21] So we're gonna go back over to gameState. And inside of it here doesn't really matter what order these functions come in I'm just gonna put a here in the middle. I'm gonna make a function called startGame, Okay. And just to kind of reassure myself that stuff is happening I'm gonna put a console.log.

[00:00:45] You can see I'm a big fan of console.log, I use it quite a bit. I'm gonna put ("Hatching"), And I'm gonna say this.current = "HATCHING", Right? So I've moved my finite state machine forward from INIT to HATCHING. So now, this.current this one here will now be equal to HATCHING.

[00:01:13] And then, I'm going to define a time that I'm going to wake up, so I'm gonna say this.wakeTime = this.clock, so the current time, +3, right? You can define what you want this to be, I'm gonna make it wait for 3 seconds, so it takes the fox 9 seconds to hatch, right?

[00:01:32] And then I'll also have to initialize that as well. So I'm gonna put wakeTime and I just put anything that's not currently working as -1. This is called a sentinel value that it doesn't actually mean that the clock's gonna do something at -1 it just means if it's at -1 it's not currently active.

[00:01:50] You could put this at undefined. You could put whatever you want. I tend to keep things in the same type, and I think that's just a habit of writing too much type script cuz type script would yell at you otherwise. All right, so that will start the game whenever I call that function.

[00:02:08] But we have to have a wake as well, so whenever the hatching time comes up we want it to wake, right? So what we're gonna do is make a function here I'm gonna put a console.log, And we'll put awoken. This.current will go from hatching to idling, right? To just standing by, so we're gonna put this.current = "IDLING''.

[00:02:37] And then we'll set this.wakeTime back to -1 cuz it's no longer waiting to wake up. If I call startGame this will get it going. I mean, I could even go as far as to do this. You don't have to follow this I'm just gonna show you. window.gameState = gameState.

[00:02:56] And now if I come back over here, I can actually say gameState.startGame and call it you can see here it says hatching and then after a few amount of ticks it'll say awoken. It actually won't do that because we haven't actually made that work yet but it would if there was anything watching for that time.

[00:03:18] So we're gonna have to go put that into tick, right? Because tick to has to track these times. Before we go do that let's go make the handleUserAction work first. I think that's a better thing to do first. So inside of handleUserAction, we already know this console or this works it's getting the correct icon so I'm just gonna delete the console.log.

[00:03:42] And I'm gonna say the first thing, like one of our rules is that if the fox is sleeping, feeding, celebrating or hatching it can't do anything which means that you do not respond to user input during those times. So I'm gonna say if it's ([ "SLEEP", "FEEDING", "CELEBRATING", Or "HATCHING" ], And you say, includes (this.current) which is another way of saying if this.current is inside of this array that'll be true if it's asleep, feeding, celebrating, or hatching, then // do nothing, and you just return, Okay?

[00:04:34] So that's our first order of business, do nothing in these particular states. When do we start the game? If the user presses the middle button w/hen do we actually call startGame? So we're gonna say, And we can use the same trick actually, you can do it either one of two ways, my notes have it this way.

[00:04:54] You could do it this way where you say this array includes blah, or you can say if (this.current === "INIT". Or you can restart the game if the fox is dead, so this.current === "DEAD". If either one of those is true then you're gonna say this.startGame, And then you return because at that point you're done, okay?

[00:05:28] And then next, Last thing we're gonna do is we're gonna put switch, we're gonna use a switch statement based on which icon they clicked. So in case "weather" you're gonna call a function called this.changeWeather, Which we'll make in a second and then break. Case, poop, Then we're gonna say this.cleanupPoop.

[00:06:14] Break. In case "fish", Call this.feed and then break. Okay, so this is just kind of saying if icon is equal, and icon refers to this icon right, the one that's passed into the function. If it's weather, then call the changeWeather function. If it's poop, call the cleanupPoop function.

[00:06:45] If it's fish then call the feed function. And we're gonna go just make placeholders for that. So changeWeather, I'm just gonna make a console.log. ChangeWeather. For cleanup poop, console.log ("cleanUpPoop"). And last one feed, Is console.log ("feed"). And I need to comma there. There we go. Okay, so the one thing that we did accomplish in this particular case, If you click the middle button now you should get, this is undefined.

[00:07:49] That's true and we do still have a problem with this. What is that? Well, if we come back over here to our function. When I call this.startGame what is this? What is this right here? Well, you would hope it would be gameState that's what your intention of that is.

[00:08:14] But it turns out that's actually not the case right now. Why? Where is it actually being called? If you go over to buttons.js it's being called here handleUserAction which is being called from here. And this is going to be, it's probably a window if I had to take my guess.

[00:08:32] We can take a look. So if I say console.log this right here and I refresh, and I click, it's just undefined so it's not even that. And that's because it's invoking that from the browser it's not invoking it from the context of gameState. So how do we handle this?

[00:08:55] We can use something called bind. So what we're gonna do is we're gonna say export default handleUserAction or not default rather export const handleUserAction = handleUserAction.bind (gameState). So what is this? Now we have a named export here called handleUserAction and we've pulled out handleUserAction and we've bound, this is a something built into JavaScript, we've bound gameState to be this no matter what, no matter where it's called.

[00:09:34] If you've watched my previous versions of the React training, we have to handle this as well in React, it's pretty common. So this is just binding the context of handleUserAction to always be this, to always be gameState. Now we have to go back over to, INIT.js and here next to game we're gonna also pull out handleUserAction like this.

[00:10:03] And we're just going to pass in handleUserAction instead of gain.handleUserAction, right? And now this is going to be the bound version of handleUserAction and that's what's gonna be passed into INIT buttons. Okay, so now, Can't access lexical declaration, handleUserAction. It's actually going to be = export const handleUserAction game.handleUserAction, gameState, gameState.handleUserAction, Right?

[00:10:44] Because it's actually on gameState right there. So gameState.handleUserAction. So game state.handleUserAction.bind (gameState) and you're exporting that which you're then passing over to buttons.js that's importing or it's actually rather it's INIT. This here is now a bound handleUserAction which is then being passed into an INIT buttons, which is here in buttons, which is this function here.

[00:11:14] And that's only being called when the user clicks the middle button. Now, if I click this, you can see it goes to hatching which so it's calling that INIT game.