Let’s try writing a complete puzzle game (Tetris) from scratch (Part 2: Detecting collisions)

Tram Ho

Collisions are very common situations and appear everywhere in the video game world. In the Mario Bros. game series, Mario heads his head in the tile box to break that box or gets a reward item. Or in the homegrown Flappy Bird game, when the bird hits the sewer, the game will end.

In order for such events to occur, game makers need to develop mechanisms to detect collisions between objects in the game, from the characters, to the flight path of bullets, terrain, …

It sounds simple, but this is a problem in game design, especially in complex 3D games with many objects. Perhaps when playing games, you sometimes encounter errors such as being stuck to the wall, going through the wall, getting stuck in the corner and falling out of the world, … the high probability of the responsibility belongs to the detection mechanism. The game’s collision did not work correctly 😄

In this article, I would like to share with you to learn and develop the collision detection feature for the simple 2D game Tetris which is still in progress in the previous article, and thereby have an overview of the playback mechanism. show video collisions in general.

Idea

In Tetris game, the cubes (Tetromino) will be changed position after each game tick or when the player presses the scroll key or rotate the block. Thus, we simply need to check that, with each Tetromino block being relocated, it is overwritten on any other element . Specifically, we must perform each check step between Tetromino blocks and the elements:

  • Bottom line,
  • The left border,
  • Right border,
  • And with the Tetromino blocks have “landed”.

And luckily, in the Tetris game, the currently falling Tetromino block is the only thing that moves . If it is another game with many moving elements in the game (players, enemies, NPCs, bullets, etc.), you will have to check the collision between all of them and against the standing object. Other yen. Imagine a complex game with an extremely wide environment and thousands of different elements. Not to mention, the game has to render continuously with a frame number of 60FPS (different from Tetris which is nearly 1 second to pass 1 tick). Obviously this is not a simple thing.

With this problem, there are many different methods to optimize performance, but it is outside the framework of this article, and making games is not my specialty (the life has rushed into making the Web. 😭 ). If you are passionate about writing games, do not be shy to pursue and learn more 😄

Proceed

There are a total of 4 actions that we need to check for collisions: blocks move down, blocks move left / right and blocks rotate.

Catch key press events

First of all, I will add a function that listens for key-press events, helping users control the game:

The functions tryMoveLeft() , tryRotating() , tryMoveRight() , tryMoveDown() will be implemented later in this article.

Move down

Tetromino blocks move down after each game tick (0.8 seconds), or when users accelerate the game by pressing the down arrow button. In this action, we need to check the collision between Tetromino block and 2 elements: lower boundary and landed blocks . To do this, I choose the following way:

  1. Every 0.8 seconds or when the user presses arrow down, executes the progress() function.
  2. In progress() , I proceed to do the following:
    1. Instead of updating the currentTetromino immediately, I create a temporary Tetromino object, called nextTetromino exactly the same as the current Tetromino block, except that the row is lower than 1 cell.
    2. Check to see nextTetromino this temporary nextTetromino block has been crossed from the lower boundary or overlap (that is, the overlap) with the landed blocks:
      1. If not, continue lowering the currentTetromino block by calling currentTetromino.fall() .
      2. If you already have discovered duplicate overrides, attach this block to the array blocks landed landedBoard and get a new random Tetromino other blocks.
  3. Conducting updates, drawing changes on the player interface via canvas.

Update the progress() function

The progress() function from my previous post needs to be updated as follows:

Note: the constructor() helps me call the correct method of class constructor directly creating currentTetromino . So whether currentTetromino is LShape or ZShape or anything, I will create another Tetromino similar with the least verbose code. 😃 .

Now we need to start writing the methods used in progress() .

Check the bottom border crossing with bottomOverlapped()

bottomOverlapped() accepts a Tetromino-class object and returns a boolean, indicating whether the Tetromino has passed the lower boundary of the game. Needless to say, this function is as simple as this:

Check overlap with landed cells with landedOverlapped()

landedOverlapped() also takes a Tetromino object and returns a boolean result. A true value means that there is a overlap between landedArray and Tetromino blocks.

To detect the overlap, here I choose to check every block of Tetromino to see if it overlaps with the landedBoard . Despite being 2 for loops, Tetromino blocks are always only a collection of 4 blocks, so performance is not a problem here. For more complex games, you need to consider choosing other techniques (such as hitbox), instead of checking every unit area (or volume) of an object like this: v.

Re-attach the Tetromino block to the landed blocks with mergeCurrentTetromino()

Once we know that the current Tetromino block has collided and can’t go any further, we need to get it stuck on the landedBoard array. Each type of Tetromino has its own color, so although it has become “a big block” with other Tetromino, I want the color of the falling block to remain in the landedBoard array.

We also just need to iterate through the currentTetromino , and update the value of each small cell to its corresponding position at landedBoard .

Speed ​​things up with tryMoveDown()

This function will run when the user presses the arrow down. Here I simply run the progress() function then proceed to draw changes to the canvas and it is done.

Result

See JSFiddle after the last section here: https://jsfiddle.net/tranxuanthang/3c1fj8os/6/

Img

Move left / right

Check Tetromino block beyond left / right margin

Similar to landedOverlapped() , checking whether Tetromino blocks are beyond the left / right boundary is very simple:

Proceed to move the block

Now we create the tryMoveLeft() and tryMoveRight() to proceed to move the Tetromino block to the left / right when the user presses the corresponding arrow key.

We must continue to create a temporary Tetromino block to simulate the next state of the Tetromino block. Whether moving left or right, you need to check for overlap with landedBoard (reuse landedOverlapped() we wrote in the previous section) and also check for overlap on left / right sides respectively. No need to check overlap with the opposite side or bottom border.

Result

It is much better. See JSFiddle to this step here: https://jsfiddle.net/tranxuanthang/4uLw7hn6/4/

Img

Rotate the Tetromino block

It’s the same as when we proceed to write functions that bring Tetromino blocks down or to the left / right. However, rotating Tetromino blocks is quite complicated and unexpected because it changes both the width and height of Tetromino blocks when they are in the table. In order for the rotation to work 100% correctly, you must fully check the collision / coincide with all other elements (except the left edge, due to the way I choose to place the Tetromino block position as the upper left corner).

Fortunately, the test functions we all do above, just reuse / recall:

Finally, we have perfected the player’s movement and control features for the game:

Img

The result here, you can click on the Result tab, click on the canvas frame once and try to control with 4 up / down / left / right arrow keys:

Conclusion

Today we have learned and done quite a bit, and the game is starting to take shape. However, the game is still far from perfect, and there are still many features we have to develop such as:

  • How to clear rows and earn points? How to calculate the point?
  • How to improve RNG algorithm into random in 7-bag style?
  • How to give users a hint about the next block to help players outline the strategy?
  • Pause & resume, sound effects and background music, …

I would like to save these shortcomings for the later part of the lesson. Thank you all for reading.

Share the news now

Source : Viblo