• 14天学习训练营导师课程|Pygame学习笔记-俄罗斯方块项目代码解析2


    14天学习训练营导师课程:
    李宁《Python Pygame游戏开发入门与实战》
    李宁《计算机视觉OpenCV Python项目实战》1
    李宁《计算机视觉OpenCV Python项目实战》2
    李宁《计算机视觉OpenCV Python项目实战》3


    做题/项目思路

    在这里插入图片描述

    进过变换这里的源码使用了7种

    self.shapeDict = {1:[(0,0),(0,-1),(0,-2),(0,1)], # shape I
    2:[(0,0),(0,-1),(1,-1),(1,0)], # shape O
    3:[(0,0),(-1,0),(0,-1),(1,0)], # shape T T型
    4:[(0,0),(0,-1),(1,0),(2,0)], # shape J 右长倒L盖子
    5:[(0,0),(0,-1),(-1,0),(-2,0)], # shape L
    6:[(0,0),(0,-1),(-1,-1),(1,0)], # shape Z
    7:[(0,0),(-1,0),(0,-1),(1,-1)]} # shape S
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    `
      
    2.俄罗斯方块的变换(关键算法,同学们自已考虑):移动、旋转

    用户使用← → ↓箭头键来移动方块,移动方式有3种:左移一格、右移一格、下移一格, 对应的俄罗斯方块坐标变换公式如下:
    左移一格:cx1=cx0-1, cy1=cy0
    右移一格:cx1=cx0+1, cy1=cy0
    下移一格:cx1=cx0, cy1=cy0+1

    用户使用a、d键来旋转方块,旋转方式有两种:左转(a键)和右转(d键)
    左转90度(逆时针):每个正方形的坐标变换公式为:y 1 = x0 , x1 = -y0 .
    右转90度(顺时针):每个正方形的坐标变换公式为:y 1 = -x0 , x1 = y0 .
    方块变换程序流程: 用变换公式计算变换后的方块中心坐标、各正方正形相对坐标,

    3. 界面空间设定
    游戏空间可以看成由sp_WIDTH × sp_HIGHT个正方形小格子构成,每格子都有一个相对于左上角的坐标。可以用一个sp_WIDTH × sp_HIGHT的二维数组表示游戏空间。如下:
    int gamespace[sp_WIDTH][sp_HIGHT];
    某格子对应的数组元素的值为1,表示此格子已被方块填充,为0表示未被填充。
    在游戏空间中,被方块填充了的格子为深灰色,未被填充的格子为白色(底色),灰色格子触及空间顶部时,游戏结束。即gamespace0中有元素的值为1时,游戏结束。

    4.。 判断俄罗斯方块是否能进行指定的变换(移动、旋转)
    俄罗斯方块中心点在游戏空间中的坐标由方块结构体变量中的cx, cy指定,各小正方形在空间中的坐标等于小正方形相对坐标加上方块中心点坐标,即:
      [cx + box[i].x , cy + box[i].y ]
    能否进行下一步指定变换的判断方法是:执行指定的变换坐标计算程序(函数),返回(得到)变换后俄罗斯方块的信息体,判断变换后方块中各小正方形格(设坐标为[a, b])是否有超出边界,小正方形所在的位置是否已被填充。即:
      0<= a <= sp_WIDTH   b <= sp_HIGHT
      且 gamespace[a][b] = = 0 成立


    代码解析

    
    
    • 1
    # 先用randRange获取1~7中的随机整数,随机到某一整数,那么访问self.shapeDict,获取这种形状方块的核心块及其他方块的相对位置。
    # 访问颜色字典,获取此方块的颜色。建立循环,当方块可移动时(while self. canMove():),且暂停键未被摁下(if isPause:),
    # 核心块纵坐标加一,根据核心块及其他方块对于核心块的相对位置,画出四个方块。用self.getLocation()函数获取方块的位置。
    
    • 1
    • 2
    • 3
    def drawNew(self):
    global next
    global getNew
    global core
    next = randrange(1,8)
    #形状
    self.getNew = self.shapeDict[next]
    getNew = self.getNew
    core = [4,-2]
    time = 0.2
    while self.canMove():
    if isPause:
    core[1] += 1
    self.drawSquare()
    if self.isFaster:
    sleep(time-0.15)
    else:
    sleep(time+0.22)
    self.isFaster = False
    else:
    self.drawSquare()
    sleep(time)
    self.getLocation()
    # 绘制当前方块
    def drawSquare(self):
    self.area.delete("new")
    for i in range(4):
    self.area.create_rectangle((core[0]+self.getNew[i][0])*self.size,
    (core[1]+self.getNew[i][1])*self.size,
    (core[0]+self.getNew[i][0]+1)*self.size,
    (core[1]+self.getNew[i][1]+1)*self.size,
    fill=self.color[next-1],tags="new")
    self.area.update()
    # 给底部每行中方块都加上标签:bottom + str(j), j代表该块所在行数,每次遍历map,建立对于range(self. height)的for循环,删去每一行,
    # 若map什么地方的元素为1,画出这一位置的方块,不断更新。这样可以画出底部方块。
    def drawBottom(self):
    for j in range(self.height):
    self.area.delete('bottom'+str(j))
    for i in range(self.width):
    if map[(i,j)] == 1:
    self.area.create_rectangle(self.size*i,self.size*j,self.size*(i+1),
    self.size*(j+1),fill='grey',tags='bottom'+str(j))
    self.area.update()
    # 判断填满遍历map每一行的各个元素,若所有元素为1,则标签中score值+10,将
    # 此行所有元素改为0,行数map(i,j)=map(i-1,j)(即所有之上的行下移)
    # ,那么后续画底部方块时,可实现消行。
    def isFill(self):
    for j in range(self.height):
    t = 0
    for i in range(self.width):
    if map[(i,j)] == 1:
    t = t + 1
    if t == self.width:
    self.getScore()
    self.deleteLine(j)
    # 加分
    def getScore(self):
    scoreValue = eval(self.scoreLabel2['text'])
    scoreValue += 10
    self.scoreLabel2.config(text=str(scoreValue))
    # 消行
    def deleteLine(self,j):
    for t in range(j,2,-1):
    for i in range(self.width): map[(i,t)] = map[(i,t-1)]
    for i in range(self.width): map[(i,0)] = 0
    self.drawBottom()
    # 遍历每一行,若从顶部到底部map每一行都有某一个元素或更多元素为1,
    # 那么说明方块以顶到最上端,游戏结束。此处不可以简单判定最上一行是否有元素为1就判定结束,
    # 若这样会产生顶部有新的方块产生,然后导致顶部有元素为1,误判为游戏结束。
    def isOver(self):
    t = 0
    for j in range(self.height):
    for i in range(self.width):
    if self.map[(i,j)] == 1:
    t += 1
    break
    if t >= self.height:
    return False
    else:
    return True
    # 先判断方块是否可以旋转(针对其靠近边界时)。先将其现在所在位置对应map中的元素改为0,判断其旋
    # 转后位置对应map中的元素是否有一,若有,说明其旋转后的位置已经被占,是不能旋转的,返回值为False
    # 。否则为可旋转,返回值True。若已判定可以旋转,那么访问self.rotateDict,得出旋转以后所有小块的位置
    # 变换,将变换以后的位置对应map的元素设为1,旋转便已完成。
    def canRotate(self):
    for i in range(4): map[((core[0]+getNew[i][0]),
    (core[1]+getNew[i][1]))] = 0
    for i in range(4):
    if map[((core[0]+self.rotateDict[getNew[i]][0]),
    (core[1]+self.rotateDict[getNew[i]][1]))] == 1:
    return False
    return True
    #旋转
    def rotate(self,event):
    if next != 2:
    if self.canRotate():
    for i in range(4):
    getNew[i] = self.rotateDict[getNew[i]]
    self.drawSquare()
    if not self.canMove():
    for i in range(4): map[((core[0]+getNew[i][0]),(core[1]+getNew[i][1]))] = 1
    # 先判断是否左移/右移,同样,将方块现在所处位置的map中元素设为0,看其移动后的位置上map的元素是否有1,
    # 若有,说明这一位置已被占据或已到边界,不可移动,返回False。若可移动,返回True。按下左键,若可
    # 以移动,核心块的横坐标减1,由于我们只讨论其他小块对于核心块的相对位置,所以其他小块的位置自动随
    # 核心块的位置移动而移动。将移动过后的位置对应map中的元素设为1。
    def canLeft(self):
    coreNow = core
    for i in range(4): map[((coreNow[0]+getNew[i][0]),(coreNow[1]+getNew[i][1]))] = 0
    for i in range(4):
    if map[((coreNow[0]+getNew[i][0]-1),(coreNow[1]+getNew[i][1]))] == 1:
    return False
    return True
    #左移
    def moveLeft(self,event):
    if self.canLeft():
    core[0] -= 1
    self.drawSquare()
    self.drawBottom()
    if not self.canMove():
    for i in range(4): map[((core[0]+getNew[i][0]),(core[1]+getNew[i][1]))] = 1
    # 判断右移
    def canRight(self):
    for i in range(4): map[((core[0]+getNew[i][0]),(core[1]+getNew[i][1]))] = 0
    for i in range(4):
    if map[((core[0]+getNew[i][0]+1),(core[1]+getNew[i][1]))] == 1:
    return False
    return True
    # 右移
    def moveRight(self,event):
    if self.canRight():
    core[0] += 1
    self.drawSquare()
    self.drawBottom()
    if not self.canMove():
    for i in range(4): map[((core[0]+getNew[i][0]),(core[1]+getNew[i][1]))] = 1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135

    `

    总结

    4篇打卡完成,PY体系后续有空继续深入学习,通过案例解析和学习,对PY的基本语法有了更深刻的理解,包括字典等独特的数据类型。
    暂停打卡类活动,今后1年,专注前端动效类的设计与实际工程化开发,目标交互应用。

  • 相关阅读:
    MYSQL数据库之备份与恢复
    NSSCTF第11页(3)
    [创业-38]:公司、企业、组织的本质与层次
    前端后端的爱恨情仇
    C基础学习之C 函数指针
    1001 A+B Format(字符串处理)
    Linux 14:HTTP协议与web服务器
    基础算法:大整数减法
    灌区量测水监测系统解决方案 灌区量测水系统解决方案 农业水价综合改革解决方案
    SpringBoot 调用外部接口的三种方式
  • 原文地址:https://blog.csdn.net/qq_16430177/article/details/128125554