• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            隨筆 - 41, 文章 - 8, 評論 - 8, 引用 - 0
            數據加載中……

            [Python][PyQt4]PyQt4 中的俄羅斯方塊

            PyQt4 中的俄羅斯方塊

            創建一個電腦游戲是很具挑戰性的。遲早程序員想創建一個電腦游戲。事實上,很多人對編程感興趣,是因為它們玩了游戲,然后想創建自己的游戲。創建一個游戲會提升你的編程技能。

            俄羅斯方塊

            俄羅斯方塊從創建開始就成為很流行的游戲之一。游戲最開始是由俄羅斯程序員 Alexey Pajitnov 設計和編寫的。從那時開始幾乎在所有的計算機平臺運行。甚至我的手機就有一個修改版的俄羅斯方塊。

            這個游戲是落下方塊。我們可以有七種不同的形狀,稱為 tetromino 。S形狀,Z形狀,T形狀,L形狀,行,鏡像L形狀,方塊形狀。而這些形狀又由四個方塊構成。這些形狀會掉到板上。這個游戲的目標是移動和旋轉這些形狀,所以可以把他們填滿。如果我們填滿了一行,這行就被銷毀然后我們得分。游戲在方塊填到頂部就結束了。

            _images/tetrominoes.png

            PyQt4 是可以用于創建應用的。也有很多其它庫可以用于游戲。盡管如此,PyQt4 仍可以創建游戲。

            下面的例子是一個改版的俄羅斯方塊,在 PyQt4 安裝的目錄下。

            開發

            我們不準備使用圖片,而是使用 PyQt4 的 API 。在每個計算機游戲背后,都有一個數學模型。俄羅斯方塊也是。

            一些想法。

            • 使用 QtCore.QBasicTimer 創建游戲的一輪
            • 這些方塊是往下掉的
            • 這些形狀一格格移動
            • 游戲盤數學上是簡單的列表
            #!/usr/bin/python
            # tetris.py
            import sys
            import random
            from PyQt4 import QtCore, QtGui
            class Tetris(QtGui.QMainWindow):
                def __init__(self):
                    #QtGui.QMainWindow.__init__(self)
                    super(Tetris, self).__init__()
                    self.setGeometry(300, 300, 180, 380)
                    self.setWindowTitle('Tetris')
                    self.tetrisboard = Board(self)
                    self.setCentralWidget(self.tetrisboard)
                    self.statusbar = self.statusBar()
                    self.connect(self.tetrisboard, QtCore.SIGNAL("messageToStatusbar(QString)"),
                        self.statusbar, QtCore.SLOT("showMessage(QString)"))
                    self.tetrisboard.start()
                    self.center()
                def center(self):
                    screen = QtGui.QDesktopWidget().screenGeometry()
                    size =  self.geometry()
                    self.move((screen.width()-size.width())/2,
                        (screen.height()-size.height())/2)
            class Board(QtGui.QFrame):
                BoardWidth = 10
                BoardHeight = 22
                Speed = 300
                def __init__(self, parent):
                    #QtGui.QFrame.__init__(self, parent)
                    super(Board, self).__init__(parent)
                    self.timer = QtCore.QBasicTimer()
                    self.isWaitingAfterLine = False
                    self.curPiece = Shape()
                    self.nextPiece = Shape()
                    self.curX = 0
                    self.curY = 0
                    self.numLinesRemoved = 0
                    self.board = []
                    self.setFocusPolicy(QtCore.Qt.StrongFocus)
                    self.isStarted = False
                    self.isPaused = False
                    self.clearBoard()
                    self.nextPiece.setRandomShape()
                def shapeAt(self, x, y):
                    return self.board[(y * Board.BoardWidth) + x]
                def setShapeAt(self, x, y, shape):
                    self.board[(y * Board.BoardWidth) + x] = shape
                def squareWidth(self):
                    return self.contentsRect().width() / Board.BoardWidth
                def squareHeight(self):
                    return self.contentsRect().height() / Board.BoardHeight
                def start(self):
                    if self.isPaused:
                        return
                    self.isStarted = True
                    self.isWaitingAfterLine = False
                    self.numLinesRemoved = 0
                    self.clearBoard()
                    self.emit(QtCore.SIGNAL("messageToStatusbar(QString)"),
                        str(self.numLinesRemoved))
                    self.newPiece()
                    self.timer.start(Board.Speed, self)
                def pause(self):
                    if not self.isStarted:
                        return
                    self.isPaused = not self.isPaused
                    if self.isPaused:
                        self.timer.stop()
                        self.emit(QtCore.SIGNAL("messageToStatusbar(QString)"), "paused")
                    else:
                        self.timer.start(Board.Speed, self)
                        self.emit(QtCore.SIGNAL("messageToStatusbar(QString)"),
                            str(self.numLinesRemoved))
                    self.update()
                def paintEvent(self, event):
                    painter = QtGui.QPainter(self)
                    rect = self.contentsRect()
                    boardTop = rect.bottom() - Board.BoardHeight * self.squareHeight()
                    for i in range(Board.BoardHeight):
                        for j in range(Board.BoardWidth):
                            shape = self.shapeAt(j, Board.BoardHeight - i - 1)
                            if shape != Tetrominoes.NoShape:
                                self.drawSquare(painter,
                                    rect.left() + j * self.squareWidth(),
                                    boardTop + i * self.squareHeight(), shape)
                    if self.curPiece.shape() != Tetrominoes.NoShape:
                        for i in range(4):
                            x = self.curX + self.curPiece.x(i)
                            y = self.curY - self.curPiece.y(i)
                            self.drawSquare(painter, rect.left() + x * self.squareWidth(),
                                boardTop + (Board.BoardHeight - y - 1) * self.squareHeight(),
                                self.curPiece.shape())
                def keyPressEvent(self, event):
                    if not self.isStarted or self.curPiece.shape() == Tetrominoes.NoShape:
                        QtGui.QWidget.keyPressEvent(self, event)
                        return
                    key = event.key()
                    if key == QtCore.Qt.Key_P:
                        self.pause()
                        return
                    if self.isPaused:
                        return
                    elif key == QtCore.Qt.Key_Left:
                        self.tryMove(self.curPiece, self.curX - 1, self.curY)
                    elif key == QtCore.Qt.Key_Right:
                        self.tryMove(self.curPiece, self.curX + 1, self.curY)
                    elif key == QtCore.Qt.Key_Down:
                        self.tryMove(self.curPiece.rotatedRight(), self.curX, self.curY)
                    elif key == QtCore.Qt.Key_Up:
                        self.tryMove(self.curPiece.rotatedLeft(), self.curX, self.curY)
                    elif key == QtCore.Qt.Key_Space:
                        self.dropDown()
                    elif key == QtCore.Qt.Key_D:
                        self.oneLineDown()
                    else:
                        QtGui.QWidget.keyPressEvent(self, event)
                def timerEvent(self, event):
                    if event.timerId() == self.timer.timerId():
                        if self.isWaitingAfterLine:
                            self.isWaitingAfterLine = False
                            self.newPiece()
                        else:
                            self.oneLineDown()
                    else:
                        QtGui.QFrame.timerEvent(self, event)
                def clearBoard(self):
                    for i in range(Board.BoardHeight * Board.BoardWidth):
                        self.board.append(Tetrominoes.NoShape)
                def dropDown(self):
                    newY = self.curY
                    while newY > 0:
                        if not self.tryMove(self.curPiece, self.curX, newY - 1):
                            break
                        newY -= 1
                    self.pieceDropped()
                def oneLineDown(self):
                    if not self.tryMove(self.curPiece, self.curX, self.curY - 1):
                        self.pieceDropped()
                def pieceDropped(self):
                    for i in range(4):
                        x = self.curX + self.curPiece.x(i)
                        y = self.curY - self.curPiece.y(i)
                        self.setShapeAt(x, y, self.curPiece.shape())
                    self.removeFullLines()
                    if not self.isWaitingAfterLine:
                        self.newPiece()
                def removeFullLines(self):
                    numFullLines = 0
                    rowsToRemove = []
                    for i in range(Board.BoardHeight):
                        n = 0
                        for j in range(Board.BoardWidth):
                            if not self.shapeAt(j, i) == Tetrominoes.NoShape:
                                n = n + 1
                        if n == 10:
                            rowsToRemove.append(i)
                    rowsToRemove.reverse()
                    for m in rowsToRemove:
                        for k in range(m, Board.BoardHeight):
                            for l in range(Board.BoardWidth):
                                self.setShapeAt(l, k, self.shapeAt(l, k + 1))
                    numFullLines = numFullLines + len(rowsToRemove)
                    if numFullLines > 0:
                        self.numLinesRemoved = self.numLinesRemoved + numFullLines
                        self.emit(QtCore.SIGNAL("messageToStatusbar(QString)"),
                            str(self.numLinesRemoved))
                        self.isWaitingAfterLine = True
                        self.curPiece.setShape(Tetrominoes.NoShape)
                        self.update()
                def newPiece(self):
                    self.curPiece = self.nextPiece
                    self.nextPiece.setRandomShape()
                    self.curX = Board.BoardWidth / 2 + 1
                    self.curY = Board.BoardHeight - 1 + self.curPiece.minY()
                    if not self.tryMove(self.curPiece, self.curX, self.curY):
                        self.curPiece.setShape(Tetrominoes.NoShape)
                        self.timer.stop()
                        self.isStarted = False
                        self.emit(QtCore.SIGNAL("messageToStatusbar(QString)"), "Game over")
                def tryMove(self, newPiece, newX, newY):
                    for i in range(4):
                        x = newX + newPiece.x(i)
                        y = newY - newPiece.y(i)
                        if x < 0 or x >= Board.BoardWidth or y < 0 or y >= Board.BoardHeight:
                            return False
                        if self.shapeAt(x, y) != Tetrominoes.NoShape:
                            return False
                    self.curPiece = newPiece
                    self.curX = newX
                    self.curY = newY
                    self.update()
                    return True
                def drawSquare(self, painter, x, y, shape):
                    colorTable = [0x000000, 0xCC6666, 0x66CC66, 0x6666CC,
                                  0xCCCC66, 0xCC66CC, 0x66CCCC, 0xDAAA00]
                    color = QtGui.QColor(colorTable[shape])
                    painter.fillRect(x + 1, y + 1, self.squareWidth() - 2,
                        self.squareHeight() - 2, color)
                    painter.setPen(color.light())
                    painter.drawLine(x, y + self.squareHeight() - 1, x, y)
                    painter.drawLine(x, y, x + self.squareWidth() - 1, y)
                    painter.setPen(color.dark())
                    painter.drawLine(x + 1, y + self.squareHeight() - 1,
                        x + self.squareWidth() - 1, y + self.squareHeight() - 1)
                    painter.drawLine(x + self.squareWidth() - 1,
                        y + self.squareHeight() - 1, x + self.squareWidth() - 1, y + 1)
            class Tetrominoes(object):
                NoShape = 0
                ZShape = 1
                SShape = 2
                LineShape = 3
                TShape = 4
                SquareShape = 5
                LShape = 6
                MirroredLShape = 7
            class Shape(object):
                coordsTable = (
                    ((0, 0),     (0, 0),     (0, 0),     (0, 0)),
                    ((0, -1),    (0, 0),     (-1, 0),    (-1, 1)),
                    ((0, -1),    (0, 0),     (1, 0),     (1, 1)),
                    ((0, -1),    (0, 0),     (0, 1),     (0, 2)),
                    ((-1, 0),    (0, 0),     (1, 0),     (0, 1)),
                    ((0, 0),     (1, 0),     (0, 1),     (1, 1)),
                    ((-1, -1),   (0, -1),    (0, 0),     (0, 1)),
                    ((1, -1),    (0, -1),    (0, 0),     (0, 1))
                )
                def __init__(self):
                    self.coords = [[0,0] for i in range(4)]
                    self.pieceShape = Tetrominoes.NoShape
                    self.setShape(Tetrominoes.NoShape)
                def shape(self):
                    return self.pieceShape
                def setShape(self, shape):
                    table = Shape.coordsTable[shape]
                    for i in range(4):
                        for j in range(2):
                            self.coords[i][j] = table[i][j]
                    self.pieceShape = shape
                def setRandomShape(self):
                    self.setShape(random.randint(1, 7))
                def x(self, index):
                    return self.coords[index][0]
                def y(self, index):
                    return self.coords[index][1]
                def setX(self, index, x):
                    self.coords[index][0] = x
                def setY(self, index, y):
                    self.coords[index][1] = y
                def minX(self):
                    m = self.coords[0][0]
                    for i in range(4):
                        m = min(m, self.coords[i][0])
                    return m
                def maxX(self):
                    m = self.coords[0][0]
                    for i in range(4):
                        m = max(m, self.coords[i][0])
                    return m
                def minY(self):
                    m = self.coords[0][1]
                    for i in range(4):
                        m = min(m, self.coords[i][1])
                    return m
                def maxY(self):
                    m = self.coords[0][1]
                    for i in range(4):
                        m = max(m, self.coords[i][1])
                    return m
                def rotatedLeft(self):
                    if self.pieceShape == Tetrominoes.SquareShape:
                        return self
                    result = Shape()
                    result.pieceShape = self.pieceShape
                    for i in range(4):
                        result.setX(i, self.y(i))
                        result.setY(i, -self.x(i))
                    return result
                def rotatedRight(self):
                    if self.pieceShape == Tetrominoes.SquareShape:
                        return self
                    result = Shape()
                    result.pieceShape = self.pieceShape
                    for i in range(4):
                        result.setX(i, -self.y(i))
                        result.setY(i, self.x(i))
                    return result
            app = QtGui.QApplication(sys.argv)
            tetris = Tetris()
            tetris.show()
            sys.exit(app.exec_())
            

            我稍微簡化了此游戲,所以理解起來比較容易。游戲在載入后就立即開始了。我們可以按 'p' 來暫停游戲。而空格可以讓形狀立即掉到底部。游戲以恒速進行,并沒有實現加速。分數是我們移掉的行數。

            self.statusbar = self.statusBar()
            self.connect(self.tetrisboard, QtCore.SIGNAL("messageToStatusbar(QString)"),
                self.statusbar, QtCore.SLOT("showMessage(QString)"))
            

            我們創建了狀態欄,用于顯示消息。我們將顯示三種可能的消息。移除的行數,暫停消息,結束消息。

            ...
            self.curX = 0
            self.curY = 0
            self.numLinesRemoved = 0
            self.board = []
            ...
            

            在游戲開始前,我們先初始化一些重要的變量。變量 self.board 是一組 0 到 7 的列表。它代表這些形狀的位置,以及剩余的形狀。

            for j in range(Board.BoardWidth):
                shape = self.shapeAt(j, Board.BoardHeight - i - 1)
                if shape != Tetrominoes.NoShape:
                    self.drawSquare(painter,
                        rect.left() + j * self.squareWidth(),
                        boardTop + i * self.squareHeight(), shape)
            

            游戲的繪制被分為兩部分。第一步,我們繪制所有的形狀,或剩余的形狀,這些是已經掉落的。所有的都記在 self.board 中。我們使用 shapeAt() 方法訪問它。

            if self.curPiece.shape() != Tetrominoes.NoShape:
                for i in range(4):
                    x = self.curX + self.curPiece.x(i)
                    y = self.curY - self.curPiece.y(i)
                    self.drawSquare(painter, rect.left() + x * self.squareWidth(),
                        boardTop + (Board.BoardHeight - y - 1) * self.squareHeight(),
                        self.curPiece.shape())
            

            下一步我們繪制落下的部分。

            elif key == QtCore.Qt.Key_Left:
                self.tryMove(self.curPiece, self.curX - 1, self.curY)
            elif key == QtCore.Qt.Key_Right:
                self.tryMove(self.curPiece, self.curX + 1, self.curY)

            keyPressEvent 中,我們檢查按鍵。如果我們按向右的鍵,那么那塊就會往右移。我們說 try,是因為可能不能移。

            def tryMove(self, newPiece, newX, newY):
                for i in range(4):
                    x = newX + newPiece.x(i)
                    y = newY - newPiece.y(i)
                    if x < 0 or x >= Board.BoardWidth or y < 0 or y >= Board.BoardHeight:
                        return False
                    if self.shapeAt(x, y) != Tetrominoes.NoShape:
                        return False
                self.curPiece = newPiece
                self.curX = newX
                self.curY = newY
                self.update()
                return True
            

            在這個方法中,我們嘗試移動形狀。如果到了畫板邊緣或是碰到其它,我們就返回 False 。否則,我們就放置到新的位置。

            def timerEvent(self, event):
                if event.timerId() == self.timer.timerId():
                    if self.isWaitingAfterLine:
                        self.isWaitingAfterLine = False
                        self.newPiece()
                    else:
                        self.oneLineDown()
                else:
                    QtGui.QFrame.timerEvent(self, event)
            

            在計時器事件中,我們或者是創建一個新的方塊,或是讓方塊下落。

            def removeFullLines(self):
                numFullLines = 0
                rowsToRemove = []
                for i in range(Board.BoardHeight):
                    n = 0
                    for j in range(Board.BoardWidth):
                        if not self.shageAt(j, i) == Tetrominoes.NoShape:
                            n = n + 1
                    if n == 10:
                        rowsToRemove.append(i)
                rowsToRemove.reverse()
                for m in rowsToRemove:
                    for k in range(m, Board.BoardHeight):
                        for l in range(Board.BoardWidth):
                            self.setShapeAt(l, k, self.shapeAt(l, k + 1))
            ...
            

            如果那塊碰到底部,我們就調用 removeFullLines() 方法。首先,我們找到所有的行數。然后我們移除他們。我們要把滿了的行數移除。注意,我們把要移除的列表進行反轉。否則,它可能不能正確移除。我們沒有使用重力效應。這就意味著某些方塊會懸浮。

            def newPiece(self):
                self.curPiece = self.nextPiece
                self.nextPiece.setRandomShape()
                self.curX = Board.BoardWidth / 2 + 1
                self.curY = Board.BoardHeight - 1 + self.curPiece.minY()
                if not self.tryMove(self.curPiece, self.curX, self.curY):
                    self.curPiece.setShape(Tetrominoes.NoShape)
                    self.timer.stop()
                    self.isStarted = False
                    self.emit(QtCore.SIGNAL("messageToStatusbar(QString)"), "Game Over")
            

            newPiece() 方法隨機創建一個方塊。如果這個方塊不能動了,游戲就結束了。

            Shape 類保存了每個方塊的形狀。

            self.coords = [[0,0] for i in range(4)]
            

            在創建時,我們創建了一個空的坐標列表。這個列表保存每個方塊的坐標。比如,這些元組 (0,-1),(0,0),(-1,0),(-1,-1) 代表旋轉的 S 形狀。下面的圖表指示了這個形狀。

            _images/coordinates.png

            當我們繪制方塊時,我們就把它畫在 self.curXself.curY 上。然后我們把四個方塊畫起來。


            這是一個用 PyQt4 寫的俄羅斯方塊。

            posted on 2012-02-12 10:18 mirguest 閱讀(1758) 評論(0)  編輯 收藏 引用 所屬分類: Python

            国产成人久久精品一区二区三区 | 精品久久久久久无码专区| 热re99久久精品国99热| 久久99精品国产麻豆宅宅| 国产叼嘿久久精品久久| 日韩va亚洲va欧美va久久| 午夜人妻久久久久久久久| 伊人色综合久久天天| 久久经典免费视频| 18岁日韩内射颜射午夜久久成人| 久久狠狠一本精品综合网| 热re99久久6国产精品免费| 久久福利片| 成人综合伊人五月婷久久| 日本精品久久久久影院日本| 成人国内精品久久久久一区| 久久婷婷国产剧情内射白浆| 久久亚洲高清观看| 久久亚洲中文字幕精品有坂深雪| 精品欧美一区二区三区久久久| 久久久噜噜噜久久熟女AA片| 久久久久亚洲AV无码专区网站| 久久国产乱子精品免费女| 亚洲AV无码1区2区久久| 色狠狠久久综合网| 狠狠久久综合伊人不卡| 成人久久综合网| 久久综合综合久久综合| 狠狠色丁香婷婷久久综合| 久久人人爽人爽人人爽av | 精品久久亚洲中文无码| 久久久亚洲精品蜜桃臀| 91久久九九无码成人网站| 国产精品久久亚洲不卡动漫| 欧美精品久久久久久久自慰| 久久综合九色综合网站| 中文成人久久久久影院免费观看| 久久久精品久久久久特色影视| 99久久99久久精品国产片| 久久伊人精品青青草原高清| 久久精品国产精品亚洲精品|