From app to Games: 02 - Creating the internal game engine (Creating a game with React)

Tuesday, June 05, 2018 0 Comments

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:

Code

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:

Code - Words state

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:

Code - Final state

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.

Code - Map words to JX
Note: (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:

Code - Render word

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.

Note: 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:

Code - Render word change

Change one of the words to be discovered = true and check your browser :) You should have something similar to this:

Rendering words

(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:

Code - Adding handler

And right above our state definition:

Code - Adding handler - First part

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:

Code - Adding handler - Second part

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:

Code - Check word

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.

Code - Handling game state

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 :)

We develop Plastic SCM, a version control that excels in branching and merging, can deal with huge projects and big binary assets natively, and it comes with GUIs and tools to make everything simpler.

If you want to give it a try, download it from here.

We are also the developers of SemanticMerge, and the gmaster Git client.

0 comentarios: