You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: 俄罗斯方块游戏.md
+50-25Lines changed: 50 additions & 25 deletions
Original file line number
Diff line number
Diff line change
@@ -514,45 +514,53 @@ if __name__ == '__main__':
514
514
sys.exit(app.exec_())
515
515
```
516
516
The game is simplified a bit so that it is easier to understand. The game starts immediately after it is launched. We can pause the game by pressing the p key. The Space key will drop the tetris piece instantly to the bottom. The game goes at constant speed, no acceleration is implemented. The score is the number of lines that we have removed.
517
-
517
+
```
518
518
self.tboard = Board(self)
519
519
self.setCentralWidget(self.tboard)
520
+
```
520
521
An instance of the Board class is created and set to be the central widget of the application.
We create a statusbar where we will display messages. We will display three possible messages: the number of lines already removed, the paused message, or the game over message. The msg2Statusbar is a custom signal that is implemented in the Board class. The showMessage() is a built-in method that displays a message on a statusbar.
525
-
527
+
```
526
528
self.tboard.start()
529
+
```
527
530
This line initiates the game.
528
-
531
+
```
529
532
class Board(QFrame):
530
533
531
534
msg2Statusbar = pyqtSignal(str)
532
535
...
536
+
```
533
537
A custom signal is created. The msg2Statusbar is a signal that is emitted when we want to write a message or the score to the statusbar.
534
-
538
+
```
535
539
BoardWidth = 10
536
540
BoardHeight = 22
537
541
Speed = 300
542
+
```
538
543
These are Board's class variables. The BoardWidth and the BoardHeight define the size of the board in blocks. The Speed defines the speed of the game. Each 300 ms a new game cycle will start.
539
-
544
+
```
540
545
...
541
546
self.curX = 0
542
547
self.curY = 0
543
548
self.numLinesRemoved = 0
544
549
self.board = []
545
550
...
551
+
```
546
552
In the initBoard() method we initialize some important variables. The self.board variable is a list of numbers from 0 to 7. It represents the position of various shapes and remains of the shapes on the board.
547
-
553
+
```
548
554
def shapeAt(self, x, y):
549
555
return self.board[(y * Board.BoardWidth) + x]
556
+
```
550
557
The shapeAt() method determines the type of a shape at a given block.
The board can be dynamically resized. As a consequence, the size of a block may change. The squareWidth() calculates the width of the single square in pixels and returns it. The Board.BoardWidth is the size of the board in blocks.
555
-
563
+
```
556
564
for i in range(Board.BoardHeight):
557
565
for j in range(Board.BoardWidth):
558
566
shape = self.shapeAt(j, Board.BoardHeight - i - 1)
@@ -561,8 +569,9 @@ for i in range(Board.BoardHeight):
561
569
self.drawSquare(painter,
562
570
rect.left() + j * self.squareWidth(),
563
571
boardTop + i * self.squareHeight(), shape)
572
+
```
564
573
The painting of the game is divided into two steps. In the first step, we draw all the shapes, or remains of the shapes that have been dropped to the bottom of the board. All the squares are remembered in the self.board list variable. The variable is accessed using the shapeAt() method.
565
-
574
+
```
566
575
if self.curPiece.shape() != Tetrominoe.NoShape:
567
576
568
577
for i in range(4):
@@ -572,24 +581,29 @@ if self.curPiece.shape() != Tetrominoe.NoShape:
572
581
self.drawSquare(painter, rect.left() + x * self.squareWidth(),
573
582
boardTop + (Board.BoardHeight - y - 1) * self.squareHeight(),
574
583
self.curPiece.shape())
584
+
```
575
585
The next step is the drawing of the actual piece that is falling down.
In the keyPressEvent() method we check for pressed keys. If we press the right arrow key, we try to move the piece to the right. We say try because the piece might not be able to move.
In the tryMove() method we try to move our shapes. If the shape is at the edge of the board or is adjacent to some other piece, we return False. Otherwise we place the current falling piece to a new position.
In the timer event, we either create a new piece after the previous one was dropped to the bottom or we move a falling piece one line down.
626
-
642
+
```
627
643
def clearBoard(self):
628
644
629
645
for i in range(Board.BoardHeight * Board.BoardWidth):
630
646
self.board.append(Tetrominoe.NoShape)
647
+
```
631
648
The clearBoard() method clears the board by setting Tetrominoe.NoShape at each block of the board.
632
-
649
+
```
633
650
def removeFullLines(self):
634
651
635
652
numFullLines = 0
@@ -656,8 +673,9 @@ def removeFullLines(self):
656
673
657
674
numFullLines = numFullLines + len(rowsToRemove)
658
675
...
676
+
```
659
677
If the piece hits the bottom, we call the removeFullLines() method. We find out all full lines and remove them. We do it by moving all lines above the current full line to be removed one line down. Notice that we reverse the order of the lines to be removed. Otherwise, it would not work correctly. In our case we use a naive gravity. This means that the pieces may be floating above empty gaps.
660
-
678
+
```
661
679
def newPiece(self):
662
680
663
681
self.curPiece = Shape()
@@ -671,8 +689,9 @@ def newPiece(self):
671
689
self.timer.stop()
672
690
self.isStarted = False
673
691
self.msg2Statusbar.emit("Game over")
692
+
```
674
693
The newPiece() method creates randomly a new tetris piece. If the piece cannot go into its initial position, the game is over.
675
-
694
+
```
676
695
class Tetrominoe(object):
677
696
678
697
NoShape = 0
@@ -683,10 +702,11 @@ class Tetrominoe(object):
683
702
SquareShape = 5
684
703
LShape = 6
685
704
MirroredLShape = 7
705
+
```
686
706
The Tetrominoe class holds names of all possible shapes. We have also a NoShape for an empty space.
687
707
688
708
The Shape class saves information about a tetris piece.
689
-
709
+
```
690
710
class Shape(object):
691
711
692
712
coordsTable = (
@@ -695,15 +715,19 @@ class Shape(object):
695
715
...
696
716
)
697
717
...
718
+
```
698
719
The coordsTable tuple holds all possible coordinate values of our tetris pieces. This is a template from which all pieces take their coordinate values.
699
-
720
+
```
700
721
self.coords = [[0,0] for i in range(4)]
722
+
```
701
723
Upon creation we create an empty coordinates list. The list will save the coordinates of the tetris piece.
702
724
703
-
Coordinates
704
-
Figure: Coordinates
705
-
The above image will help understand the coordinate values a bit more. For example, the tuples (0, -1), (0, 0), (-1, 0), (-1, -1) represent a Z-shape. The diagram illustrates the shape.
725
+
坐标系示意图:
706
726
727
+

728
+
729
+
The above image will help understand the coordinate values a bit more. For example, the tuples (0, -1), (0, 0), (-1, 0), (-1, -1) represent a Z-shape. The diagram illustrates the shape.
730
+
```
707
731
def rotateLeft(self):
708
732
709
733
if self.pieceShape == Tetrominoe.SquareShape:
@@ -718,6 +742,7 @@ def rotateLeft(self):
718
742
result.setY(i, -self.x(i))
719
743
720
744
return result
745
+
```
721
746
The rotateLeft() method rotates a piece to the left. The square does not have to be rotated. That is why we simply return the reference to the current object. A new piece is created and its coordinates are set to the ones of the rotated piece.
0 commit comments