Create game Tetris (puzzle) with Angular (Part 1)

Tram Ho

Surely childhood wishes everyone knows the game “Tetris” or often called the game ‘puzzle’. But how it is created, those who do not work in programming will probably not know, but for those with knowledge of programming, why not try to code and play on the game we code? Today, I will take you to the journey of implementing the classic game Tetris. I will cover concepts like graphics, game loop, and collision detection. Finally, we will have a fully operational game with points and levels. The full version of the game code is available on GitHub . Please go up and research more offline.

Surely we all know how to play this game, so let’s come to implement it too

What knowledge do you need?

Actually, it’s not a big deal, everything is very basic

  • A bit of typescript knowledge
  • Know a little about Angular (just a little bit, learning 2 3 hours is enough =)))
  • A little knowledge of Canvas (just basic drawing, you can see here )

Install the environment

With angular, of course, we will first install Angular CLI

With CLI installed, we can create a new project with new ng:

Playing board consists of 10 columns and 20 rows. These values ​​will probably have to be used frequently to loop in implementation logic, so I’ll put them in constants values ​​at constants.ts

First, we need to introduce a <canvas> element, which we can implement in the component template. We will also use a reference variable on the element to be able to reference its component class. Here is the complete template, I will implement some logic to make cool things happen:

This is board.component.ts and these are initial logic, this component is the main component to implement logic, see:

In app.component.ts add the game-board declared above:

Styling

This is an old-fashioned 80s game, Press Start 2P is a bitmap font based on the font design from the arcade game Namco of the 1980s. We can add it in two steps:

We styled our game container and prepared the logic.

The Board

The table in Tetris consists of cells, whether or not that cell is displayed. My first thought is to represent a cell with boolean value, but have you thought about how the cell color will display? It has a lot of value, right? We can represent an empty cell with 0 and the colors will be numbered.

The next concept is to represent the rows and columns of the game. From the beginning, we can already imagine that it can use two-dimensional (2D) arrays, right, this concept introduces programming, everyone has to do exercises related to this concept, and This is when it comes into play.

Let go, let’s start coding logic! First let’s create a function that returns an empty table with all cells set to 0.

When starting the game, of course I have to use the above function, we start with the play button, so name it play.

Using console.table, we see a representation of the array (the main console of the game).

The X and Y coordinates represent table cells. Now that we have the board, let’s look at the shapes (pieces).

Tetrominos

A piece in a Tetris is a block that can rotate by one axis in one state. They are commonly called tetrominos and come in seven styles and each has a different color. These blocks are no stranger to us, perhaps taking the shape of the letters I, J, L, O, S, T and Z.

I represent tetromino J as a matrix where the number two represents the colored cells I add row of zeros to have the center rotate around:

With the remaining values, please figure out for yourself, remember to define why each block will have a rotation center for convenient rotation as well as use it to represent the position of that block.

I want the Piece class to know its position on the board, its color and shape. So to be able to draw itself on the board, it needs a reference to the canvas context .

To draw the tetromino on the board, we loop through all the cells of the piece. If the value in the cell is greater than 0, then we color the piece.

At this point, you may notice that the edges of the piece stick together into an integer because it is very small, so we need to “blow it up”, scaling it up to block size like at constan. BLOCK_SIZE :

On the main board let’s try to create and draw it when we press the play button:

The blue tetromino J block appears!

For other words, please create your own and then draw it to see why

Once you have the pieces, use the keyboard events to control them as well

Keyboard input

Now, let’s see how we move pieces across the board over this function

This function takes an IPiece form and assigns the coordinates of the IPiece (that argument) to the current Piece. At first it looks a bit weird, right, let’s see what its purpose is.

First of all we have to map the keys intended to be used with its code, so that when a key is pressed we know whether that key controls our block or not.

As above I have a move function with an IPiece style, I haven’t said what the purpose is, right. My purpose is very simple: I will create a copy of it, then I assign the new coordinates that the block comes to that copy, and pass the move function, like that the coordinates of the block. that has been changed. Note: In JavaScript, we can use the shallow copying feature to copy primitive data types like numbers and strings. However, with an object, the mechanism is different, surely anyone who works for a while will understand. So ES6 provides two shallow copy mechanisms: Object.assign () and the spread operator .

We always use the spread operator :

We can use it like this to get a new state without changing the original one. This is important because we don’t always want to move to a new position.

Come here, everyone is extremely convinced then =))

To listen to keyboard events, we can use the HostListener decorator in our table component.

Now we are listening to events on the keyboard and if we press the left, right or down arrow, then we can see the moving part.

Okay okay, however, the ghost shards that go through the wall are not what we want. Let’s continue to improve it

Collision detection

This collision detection not only solves the problem that not only in the above case we just had, but it also in many special cases as well. Through the game we have also thought of a few cases already. Specifically:

  • Top =)) (too high to hit the floor)
  • Light moving sides and hitting the wall
  • Touch another block (piece) on the board
  • When rotating vertically, horizontally the image touches a wall or other block

In the previous section we have identified the position where when we control the block with the keyboard, this is similar to when the block falls (because it falls on itself like we press the ‘down’ button). So we will simply check the new location (if qualified) first, if it is valid, then move to it. To check for collisions, I looped through all the gaps in the table that the tetromino would be able to reach. If it isn’t zero, then it probably has another block or a wall. The Arraut method that works best for this is every() . With it, we can check if all of the elements in the array pass the logic we need or not. We need to calculate every cell in the block (piece) to see if it is a valid position or not, let’s take a look at this code:

By using this approach before moving the block to a new location, we make sure that the block doesn’t go anywhere it can’t get to:

This function I put in the service, also let you create offline. Or you can do part time. Let’s check it out

Okay, it looks like it’s okay, no more wall-to-wall phenomenon. Now we will continue to code so that when we also let the block fall under control, it can stop when there is an obstacle.

Handle rotation

… This part I will continue in the next part:

  • Handle rotation
  • The way to ramdom one block
  • Game loop
  • Timer
  • Eat points
  • … Generally speaking, will perfect the game. Thank you for watching

Author

Article translated from: https://medium.com/angular-in-depth/game-development-tetris-in-angular-64ef96ce56f7 by Michael Karén . Please try the game after finishing here and leave feedback

Share the news now

Source : Viblo