pyQt4实现俄罗斯方块游戏

本文实例为大家分享了pyQt4实现俄罗斯方块游戏的具体代码,供大家参考,具体内容如下

#!/usr/bin/python
# -*- coding: utf-8 -*- 

import sys, random
from PyQt4 import QtCore, QtGui 

class Tetris(QtGui.QMainWindow):
  #Tetris的构造函数,由于是QMainWindow的子类,所以要先调用父类的构造函数
  def __init__(self):
    super(Tetris, self).__init__()
    #QtGui.QMainWindow.__init__(self)
    self.initUI() 

  def initUI(self):   

    self.tboard = Board(self)  #创建一个Board类的实例
    self.setCentralWidget(self.tboard) #将游戏窗口放到屏幕的中间 

    self.statusbar = self.statusBar()  #创建状态栏
    self.tboard.msg2Statusbar[str].connect(self.statusbar.showMessage) #3种可能的信息:1.score 2.game over 3.pause 

    self.tboard.start() #开始初始化程序 

    self.resize(180, 380)  #游戏窗口的大小
    #self.resize(480, 380)  #游戏窗口的大小
    self.center()
    self.setWindowTitle('Tetris')  #窗口的名字
    self.show() #这句一定不能忘了,显示窗口 

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

  msg2Statusbar = QtCore.pyqtSignal(str) 

  #游戏窗口的宽和高(单位为块)
  BoardWidth = 10   #宽度为10块
  BoardHeight = 22  #高度为22块
  Speed = 300 #游戏的速度 

  def __init__(self, parent):
    super(Board, self).__init__(parent) 

    self.initBoard() 

  def initBoard(self):
    '''''
    初始化一些关键的变量
    '''
    self.timer = QtCore.QBasicTimer()  #创建一个定时器
    self.isWaitingAfterLine = False 

    self.curX = 0
    self.curY = 0
    self.numLinesRemoved = 0
    #a list of numbers from 0-7.
    #It represents the position of various shapes and remains of the shapes on the board.
    self.board = []  

    self.setFocusPolicy(QtCore.Qt.StrongFocus)
    self.isStarted = False
    self.isPaused = False
    self.clearBoard() 

  #determine the type of a shape at a given block.
  #返回(x,y)坐标处对应的点的类型
  def shapeAt(self, x, y):
    return self.board[(y * Board.BoardWidth) + x] 

  #设置(x,y)坐标处对应的点的类型
  def setShapeAt(self, x, y, shape):
    self.board[(y * Board.BoardWidth) + x] = shape 

  #calculate the width of the single square in pixels and return it
  #The Board.BoardWidth is the size of the board in blocks
  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.msg2Statusbar.emit(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.msg2Statusbar.emit("paused") 

    else:
      self.timer.start(Board.Speed, self)
      self.msg2Statusbar.emit(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 != Tetrominoe.NoShape:
          self.drawSquare(painter,
            rect.left() + j * self.squareWidth(),
            boardTop + i * self.squareHeight(), shape) 

    if self.curPiece.shape() != Tetrominoe.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):
    #如果游戏没有开始(暂停)或者curPiece为空(游戏结束),响应父窗口的按键事件,返回
    if not self.isStarted or self.curPiece.shape() == Tetrominoe.NoShape:
      super(Board, self).keyPressEvent(event)
      return 

    key = event.key()  #捕获按键 

    if key == QtCore.Qt.Key_P: #如果按键为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.rotateRight(), self.curX, self.curY) 

    elif key == QtCore.Qt.Key_Up:    #按下上箭头,向左旋转
      self.tryMove(self.curPiece.rotateLeft(), self.curX, self.curY) 

    elif key == QtCore.Qt.Key_Space:  #按下空格键,直接掉到底部
      self.dropDown() 

    elif key == QtCore.Qt.Key_D:
      self.oneLineDown() 

    else:
      super(Board, self).keyPressEvent(event) 

  def timerEvent(self, 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.
    '''
    if event.timerId() == self.timer.timerId(): 

      if self.isWaitingAfterLine:
        self.isWaitingAfterLine = False
        self.newPiece()
      else:
        self.oneLineDown() 

    else:
      super(Board, self).timerEvent(event) 

  def clearBoard(self):
    #  清除board,全部设置为NoShape
    for i in range(Board.BoardHeight * Board.BoardWidth):
      self.board.append(Tetrominoe.NoShape) 

  def dropDown(self): 

    newY = self.curY 

    while newY > 0:
      #使curPiece一直沿着y减小的方向移动,直到不能移动或者到达底部为止
      if not self.tryMove(self.curPiece, self.curX, newY - 1):
        break
      newY -= 1  #自减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):
    '''''
    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.
    '''
    numFullLines = 0
    rowsToRemove = [] 

    for i in range(Board.BoardHeight): 

      n = 0  #n记录每行shape的个数
      for j in range(Board.BoardWidth):
        if not self.shapeAt(j, i) == Tetrominoe.NoShape:
          n = n + 1
      #如果n等于10,将行号加入要删除的队列
      if n == 10:
        rowsToRemove.append(i) 

    rowsToRemove.reverse() #行号队列反置 

    for m in rowsToRemove:
      #从m行以上的shape均下移一行
      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.msg2Statusbar.emit(str(self.numLinesRemoved)) 

      self.isWaitingAfterLine = True
      self.curPiece.setShape(Tetrominoe.NoShape)
      self.update() 

  def newPiece(self):
    '''''
    The newPiece() method creates randomly a new tetris piece.
    If the piece cannot go into its initial position, the game is over.
    随机地创建一个方块。如果方块不能在它起始的位置,游戏结束。
    '''
    self.curPiece = Shape()   #当前块
    self.curPiece.setRandomShape() #随机设置
    self.curX = Board.BoardWidth / 2 + 1    #current X位置在中心
    self.curY = Board.BoardHeight - 1 + self.curPiece.minY() 

    #将self.curPiece移动到当前的坐标处,如果不能移动,游戏结束。
    #curPiece置为空,timer停止,显示消息'game over'
    if not self.tryMove(self.curPiece, self.curX, self.curY): 

      self.curPiece.setShape(Tetrominoe.NoShape)
      self.timer.stop()
      self.isStarted = False
      self.msg2Statusbar.emit("Game over") 

  def tryMove(self, newPiece, newX, newY):
    '''''
    如果the shape is at the edge of the board 或者 is adjacent to some other piece, 返回False
    否则的话,变动位置并返回True
    '''
    for i in range(4): 

      x = newX + newPiece.x(i)
      y = newY - newPiece.y(i)
      '''''
      如果x<0说明已经到了左边缘;如果x>=Board.BoardWidth,说明已经到了右边缘
      如果y<0说明已经到了底部;如果x>=Board.BoardHeight,说明已经到了最顶部
      以上情况均不能移动,返回False
      '''
      if x < 0 or x >= Board.BoardWidth or y < 0 or y >= Board.BoardHeight:
        return False
      #如果当前的位置不为空,返回False
      if self.shapeAt(x, y) != Tetrominoe.NoShape:
        return False 

    self.curPiece = newPiece
    self.curX = newX  #现在的坐标变为新坐标
    self.curY = newY
    self.update()  #frame更新 

    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 Tetrominoe(object):
  '''''
  定义游戏中出现的形状,共有8种,分别用0-7表示。
  其中0表示没有形状,1-7表示可能出现的形状:Z,S,Line,T,Square,L,MirroredL。 

  相当于C++中的枚举类型,用有意义的字符串名代替数字
  '''
  NoShape = 0
  ZShape = 1
  SShape = 2
  LineShape = 3
  TShape = 4
  SquareShape = 5
  LShape = 6
  MirroredLShape = 7 

class Shape(object):
  '''''
  Shape类保存每种方块的信息
  '''
  #coordsTable tuple holds all possible coordinate values of our Tetris pieces. 0-7
  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)]   #[[0, 0], [0, 0], [0, 0], [0, 0]]
    self.pieceShape = Tetrominoe.NoShape 

    self.setShape(Tetrominoe.NoShape) 

  #返回当前shape类型
  def shape(self):
    return self.pieceShape 

  def setShape(self, shape): 

    table = Shape.coordsTable[shape]  #table是对应的tuple元组
    #将对应的table赋给self.coords
    for i in range(4):
      for j in range(2):
        self.coords[i][j] = table[i][j] 

    self.pieceShape = shape 

  #随机获取一个块形状(从1,2,3,4,5,6,7中随机选1个)
  def setRandomShape(self):
    self.setShape(random.randint(1, 7)) 

  #返回index的x坐标,index是从0-3,分别表示方块对应的4个点
  def x(self, index):
    return self.coords[index][0] 

  #返回index的y坐标
  def y(self, index):
    return self.coords[index][1] 

  #设置当前index的x坐标
  def setX(self, index, x):
    self.coords[index][0] = x 

  #设置当前index的y坐标
  def setY(self, index, y):
    self.coords[index][1] = y 

  #返回当前块的最小x坐标
  def minX(self):
    m = self.coords[0][0]
    for i in range(4):
      m = min(m, self.coords[i][0])
    return m 

  #返回当前块的最大x坐标
  def maxX(self):
    m = self.coords[0][0]
    for i in range(4):
      m = max(m, self.coords[i][0])
    return m 

  #返回当前块的最小y坐标
  def minY(self):
    m = self.coords[0][1]
    for i in range(4):
      m = min(m, self.coords[i][1])
    return m 

  #返回当前块的最大y坐标
  def maxY(self):
    m = self.coords[0][1]
    for i in range(4):
      m = max(m, self.coords[i][1])
    return m 

  def rotateLeft(self):  #rotate a piece to the left
    #如果块是方块的话,直接返回当前块,不做任何处理
    if self.pieceShape == Tetrominoe.SquareShape:
      return self 

    result = Shape()
    result.pieceShape = self.pieceShape
    for i in range(4):
      #将i点的x坐标换为y坐标
      result.setX(i, self.y(i))
      #将i点的y坐标换为-x坐标
      result.setY(i, -self.x(i))
    #返回新的左旋后的方块
    return result 

  def rotateRight(self):
    #如果块是方块的话,直接返回当前块,不做任何处理
    if self.pieceShape == Tetrominoe.SquareShape:
      return self 

    result = Shape()
    result.pieceShape = self.pieceShape
    for i in range(4):
      #将i点的x坐标换为-y坐标
      result.setX(i, -self.y(i))
      #将i点的y坐标换为x坐标
      result.setY(i, self.x(i))
    #返回新的右旋后的方块
    return result 

'''''
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. 

'''
def main():
  #创建一个界面app
  app = QtGui.QApplication([])
  #创建一个俄罗斯方块类
  tetris = Tetris()
  #进入主循环
  app.exec_() 

if __name__ == '__main__':
  main() 

源代码来自:详细链接

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • python实现俄罗斯方块游戏

    在公司实习.公司推崇Python和Django框架,所以也得跟着学点. 简单瞅了下Tkinter,和Canvas配合在一起,还算是简洁的界面开发API.threading.Thread创建新的线程,其多线程机制也算是方便. 只是canvas.create_rectangle居然不是绘制矩形,而是新建了矩形控件这点让人大跌眼镜.先开始,在线程里每次都重绘多个矩形(随数组变化),其实是每次都新建了N个矩形,结果内存暴增.原来,对矩形进行变更时,只需用canvas.itemconfig即可. 下面就是

  • pyqt5实现俄罗斯方块游戏

    本章我们要制作一个俄罗斯方块游戏. Tetris 译注:称呼:方块是由四个小方格组成的 俄罗斯方块游戏是世界上最流行的游戏之一.是由一名叫Alexey Pajitnov的俄罗斯程序员在1985年制作的,从那时起,这个游戏就风靡了各个游戏平台. 俄罗斯方块归类为下落块迷宫游戏.游戏有7个基本形状:S.Z.T.L.反向L.直线.方块,每个形状都由4个方块组成,方块最终都会落到屏幕底部.所以玩家通过控制形状的左右位置和旋转,让每个形状都以合适的位置落下,如果有一行全部被方块填充,这行就会消失,并且得分

  • pygame实现俄罗斯方块游戏(基础篇3)

    上一章请点击查看:pygame实现俄罗斯方块游戏(基础篇2) 现在继续 一.给每个方块设置不同的颜色 根据代码这里可以判断正在下落的方块在那些Block子类里加一个属性最合适,而已经落下的方块颜色管理最合适的地方应该是修改在Panel类里的rect_arr Block子类里的修改比较简单,以TBlock类为例,在__init__函数加一行 self.color=(255,0,0) 在Panel的paint函数里将代码 # 绘制正在落下的方块 if self.move_block: for rec

  • pygame实现俄罗斯方块游戏

    本文实例为大家分享了pygame实现俄罗斯方块的具体代码,供大家参考,具体内容如下 import random, time, pygame, sys from pygame.locals import * FPS = 25 WINDOWWIDTH = 640#整个游戏屏幕的宽 WINDOWHEIGHT = 480#整个游戏屏幕的高 BOXSIZE = 20#每个小格子的宽和高 BOARDWIDTH = 10#游戏窗口本身有10个方块的宽度 BOARDHEIGHT = 20#游戏窗口本身有20个方

  • 用Python编写一个简单的俄罗斯方块游戏的教程

    俄罗斯方块游戏,使用Python实现,总共有350+行代码,实现了俄罗斯方块游戏的基本功能,同时会记录所花费时间,消去的总行数,所得的总分,还包括一个排行榜,可以查看最高记录. 排行榜中包含一系列的统计功能,如单位时间消去的行数,单位时间得分等. 附源码: from Tkinter import * from tkMessageBox import * import random import time #俄罗斯方块界面的高度 HEIGHT = 18 #俄罗斯方块界面的宽度 WIDTH = 10

  • pygame实现俄罗斯方块游戏(基础篇1)

    本文实例为大家分享了pygame实现俄罗斯方块游戏的具体代码,基础的第一篇,供大家参考,具体内容如下 一.初始界面 之前的游戏都比较简单,所以代码都是面向过程的写法,这次游戏后面可能会写比较复杂(比如人机对战.联机对战.使用道具对战等),这次面向对象一点来写这个项目. 游戏的窗口设计一个专门的Panel类便于负责单个游戏窗口的管理控制. 游戏主窗口按每个方块30像素,那么宽3010=300,高是3020=600 # -*- coding=utf-8 -*- import random impor

  • python和pygame实现简单俄罗斯方块游戏

    本文为大家分享了python实现俄罗斯方块游戏的具体代码,供大家参考,具体内容如下 Github:Tetris 代码: # -*- coding:utf-8 -*- import pygame, sys, random, copy from pygame.locals import * pygame.init() CubeWidth = 40 CubeHeight = 40 Column = 10 Row = 20 ScreenWidth = CubeWidth * (Column + 5) S

  • pygame实现俄罗斯方块游戏(基础篇2)

    接上章<pygame实现俄罗斯方块游戏(基础篇1)>继续写俄罗斯方块游戏 五.计算方块之间的碰撞 在Panel类里增加函数 def check_overlap(self, diffx, diffy): for x,y in self.moving_block.get_rect_arr(): for rx,ry in self.rect_arr: if x+diffx==rx and y+diffy==ry: return True return False 修改move_block函数的判断,

  • python实现俄罗斯方块

    网上搜到一个Pygame写的俄罗斯方块(tetris),大部分看懂的前提下增加了注释,Fedora19下运行OK的 主程序: #coding:utf8 #! /usr/bin/env python # 注释说明:shape表示一个俄罗斯方块形状 cell表示一个小方块 import sys from random import choice import pygame from pygame.locals import * from block import O, I, S, Z, L, J,

  • Python使用pygame模块编写俄罗斯方块游戏的代码实例

    文章先介绍了关于俄罗斯方块游戏的几个术语. 边框--由10*20个空格组成,方块就落在这里面. 盒子--组成方块的其中小方块,是组成方块的基本单元. 方块--从边框顶掉下的东西,游戏者可以翻转和改变位置.每个方块由4个盒子组成. 形状--不同类型的方块.这里形状的名字被叫做T, S, Z ,J, L, I , O.如下图所示: 模版--用一个列表存放形状被翻转后的所有可能样式.全部存放在变量里,变量名字如S_SHAPE_TEMPLATE or J_SHAPE_TEMPLATE 着陆--当一个方块

  • Python小游戏之300行代码实现俄罗斯方块

    前言 本文代码基于 python3.6 和 pygame1.9.4. 俄罗斯方块是儿时最经典的游戏之一,刚开始接触 pygame 的时候就想写一个俄罗斯方块.但是想到旋转,停靠,消除等操作,感觉好像很难啊,等真正写完了发现,一共也就 300 行代码,并没有什么难的. 先来看一个游戏截图,有点丑,好吧,我没啥美术细胞,但是主体功能都实现了,可以玩起来. 现在来看一下实现的过程. 外形 俄罗斯方块整个界面分为两部分,一部分是左边的游戏区域,另一部分是右边的显示区域,显示得分.速度.下一个方块样式等.

  • pygame实现俄罗斯方块游戏(AI篇1)

    上次更新到pygame实现俄罗斯方块游戏(基础篇3) 现在继续 一.定义玩家类 定义玩家类是为了便于进行手动和机器模式或各种不同机器人模式的混合使用,增加代码扩展性. 可以先定义一个玩家基类 class Player(object): auto_mode=False # 是否是自动模式,自动模式应当不响应键盘操作 def __init__(self): pass def run(self): # 进行操作 pass 手动类和机器类继承自Player类 class HumanPlayer(Play

随机推荐