From app to Games: 02 - Creating the internal game engine (Creating a game with React)
In the first part of this series, we started a new project using the create-react-app tool, started practicing some topics related to ReactJS itself and we created a basic timer.In this post, we are going to start creating the engine for Word Unscramble.
Handling the keyboard events
One of the most common mistakes in ReactJS is to handle the virtual DOM directly. For example, trying to manipulate its structure like how it used to be done in jQuery or even in vanilla Javascript.
For the Word Unscramble game, you need to treat the keyboard globally. That means you need to be able to receive events for the current window object. To do this, in the game component, you need to use two life cycle methods: componentDidMount
and componentWillUnmount
. The componentDidMount
will be called when the component was mounted in the real DOM and componentWillUnmount
right before the component is being removed from the real DOM. It's perfect for adding any external event handler in componentDidMount
and remove in componentWillUnmount
.
In App.js
, add the following methods where you add/remove a listener to window:
Save the component file and test the project. You can press any key on keyboard and see the results in Console (remember to show your dev tools - F12 in Windows or Command+Opt+I on Mac).
Now that you know how to handle the keyboard, you can start designing the internal game state and how you can mutate it.
Game internal state
Basically, an anagram game consists of a list of possible words and letters that you can use to discover these words. These possible words can already be discovered or not. So the first part of our internal state could have this schema:
Other information that needs to be stored is all the available letters. You can use the same idea that you used in words; store each letter and if you already used it. Besides this, you need to store the word that is been assembled. Your final state will look like this:
Showing the current game state
Since you already defined your internal state, it's time to show it on the screen.
To do this, you need to use your render method located in App.js
. You start by showing the words available in the game.
The first test you can do is just show every word. Since JSX (the way you built the User Interface) is Javascript with super-powers or on steroids, you can use the words array with a map. So a map function basically maps each value in array to another. In this case, you are going to map a word to a word representation.
(w,i) => <p>...</p>
is a way of creating a function in Javascript. This function receives two parameters and returns an JSX element <p>
. If you use the keyword this
inside this function, it references the parent context (this kind of function does not have its own context).
Doing this, you are going to show every word on the screen. But you can improve this by showing only blank spaces if the word was not discovered yet. For this you can create another method to handle it. Remember our arrow-function that was used in map? Let's transform it in a method.
Right before your render method, you can type it. It should look like this:
Basically, you render the word to see if it was discovered. If not, you generate a sequence of underscores in the same length of the word and show them on the screen.
padStart
will fill out the string with "_" until it gets the desired length. split('')
will break the string in chars, creating an array. join
will glue an array using the desired string as glue.
Our instruction inside render should be changed to:
Change one of the words to be discovered = true and check your browser :) You should have something similar to this:
(PS: LAMB is a discovered word and there are two more that were not discovered yet).
Mutating the game state
The game logic will occur everytime the users presses a key on keyboard either for writing or erasing a letter. We can create a new method just for handling this. In this case, we can call handleKey
. So inside our keyListener
, we will add this:
And right above our state definition:
Basically, this method is checking to see if you pressed BACKSPACE or other key. If it was pressed by any other key, you check to see if it's present in available letters and it's not used yet. If this happens (letterIndex >=0
), you clone the available array, change used
of the letter to true, assemble the word, and set available and word back to internal state. You can save and test it in your browser - you already can assemble words ;)
What about Backspace?
If you press backspace, you need to do the opposite: grab the last letter from the word and find the used one in the available array to make it usable again. You can do this way:
You can test the game :) Now it's possible to assemble and erase words ;)
Checking if we get the right word
Checking to see if the player got the right word uses the same logic from letters. You search for the word in the list and switch discovered to true or false. Later, you just need to update the internal state properly (remember you need to reset word and available letters too). The final code would look like this:
Notice that we added another condition in handleKey
that calls checkWord
if the player presses enter. This makes the game slightly harder (optionally, you can call it after every letter has been pressed - for a easier mode).
Controlling the game state
Right now, as soon as you enter the game - you already start playing. And usually this is not what you want :) It's good idea to have a lobby where you chose to start playing or even control if the game has ended/timed-out. You can handle this by adding another key to your internal state: playState
. Basically, we can have three values for it: stopped, playing, timeout.
Now in our render method, you can check for the value of playState
. Notice that I already added the start/restart game too.
You can finally test the game again. It's pretty playable :)
In the next post, we are going to finish our game adding CSS styles and controlling the phases :) The source code is available here. I'll see you in the next post :)
0 comentarios: