【Python小游戏】面向对象编程,使用tkinter制作一个俄罗斯方块小游戏

目录

一. 游戏介绍

二. 代码重点

2.1 格子矩阵

2.2 当前方块

2.3 方块位置

2.4 绑定键盘事件

2.5 分数计算

三. 代码


一. 游戏介绍

《俄罗斯方块》(Tetris,俄文:Тетрис)是一款由俄罗斯人阿列克谢·帕基特诺夫于1984年6月发明的休闲游戏。

该游戏曾经被多家公司代理过。经过多轮诉讼后,该游戏的代理权最终被任天堂获得。任天堂对于俄罗斯方块来说意义重大,因为将它与GB搭配在一起后,获得了巨大的成功。

《俄罗斯方块》的基本规则是移动、旋转和摆放游戏自动输出的各种方块,使之排列成完整的一行或多行并且消除得分。


二. 代码重点

2.1 格子矩阵

        程序中的一个核心变量是记录棋盘位置的numpy.ndarray对象grid,这里是用来记录每个格子的值,用以在前端画图的时候展示,四面的墙的值为-1,中间空间里面,空白的地方的值为0,有方块的位置的值为方块所对应的数值(大于0),程序通过判断grid里面的数值来对tkinter前端进行不同颜色的填充。

2.2 当前方块

        另一个非常重要的变量是this_block,表示的是当前选中的方块(BLOCK_1~BLOCK_7),每次的选择都是随机性的,BLOCK_1~BLOCK_7都是简单的类,里面主要有三个变量:BLOCK_COLOR、BLOCK_NUM、BLOCK_LOCATION,其中,BLOCK_COLOR是方块的颜色,BLOCK_NUM是方块的号码。这里最重要的是BLOCK_LOCATION,列表类型,表示的是方块的初始位置,但是由于方块会有变形这个动作,因此这里列出来了所有方块变形后对应的初始位置。同时维护一个变量change_num,通过维护change_num来改变方块的初始位置。

self.BLOCK_1 = BLOCK_1()
self.BLOCK_2 = BLOCK_2()
self.BLOCK_3 = BLOCK_3()
self.BLOCK_4 = BLOCK_4()
self.BLOCK_5 = BLOCK_5()
self.BLOCK_6 = BLOCK_6()
self.BLOCK_7 = BLOCK_7()
self.block_list = [self.BLOCK_1, self.BLOCK_2, self.BLOCK_3, self.BLOCK_4, self.BLOCK_5, self.BLOCK_6, self.BLOCK_7]

self.this_block = self.block_list[random.randint(0,len(self.block_list)-1)]

2.3 方块位置

        方块移动的重点是维护一个变量this_block_location,表示的是当前方块的位置,所有的移动操作(方块的生成、自动下落、加速下落、左右移动、方块变形)都是对这个this_block_location进行修改。

        考虑到这里的方块会有变形,因此这里的this_block_location的维护方式是这样的:首先是有一个原始的位置this_block.BLOCK_LOCATION,以及对列和对行的偏移值变量block_col_offset和block_row_offset。在自动下落、加速下落中是对block_row_offset值的修改,在左右移动中是对block_col_offset的修改,在方块的生成、方块变形中,是对方块原始位置this_block.BLOCK_LOCATION的值的修改。

        This_block_location的值的构成是由this_block.BLOCK_LOCATION和block_col_offset、block_row_offset构成,构成如下:self.this_block_location = [[i[0] + self.block_col_offset, i[1] + self.block_row_offset] for i in self.this_block.BLOCK_LOCATION[self.choose_one]]

方块的初始位置图如下:

2.4 绑定键盘事件

        另一个重点是绑定键盘事件,这里的绑定键盘事件主要有三种:

  1. 左右移动。这里主要是看方块的移动是否在空间内(即不能撞墙);
  2. 向下移动。这里是看方块最终停留的位置;
  3. 方块变形。将当前的方块进行指定方向变形。

        三种都是涉及到方块当前位置的更改,其中主要要考虑的是一个碰撞检验,即移动/变形后的方块位置是否没有其他的方块相撞、或者是在内部空间内。

2.5 分数计算

        另外还有一个重要的部分是分数的计算,在俄罗斯方块中,每次消除的得分和同时消除的行数相关

连续消除1行——100分

连续消除2行——200分

连续消除3行——400分

连续消除4行——800分

        因此,这里需要判断连续消除的行数是多少。


三. 代码

from tkinter import *
from tkinter import messagebox
import time
import random
import numpy as np

class BLOCK_1():
    '''
    “田”字形方块
    '''
    def __init__(self):
        self.BLOCK_LOCATION = [[[0, 0], [0, 1], [1, 0], [1, 1]]]
        self.BLOCK_COLOR = 'blue'
        self.BLOCK_NUM = 1

class BLOCK_2():
    '''
    “一”字型方块
    '''
    def __init__(self):
        self.BLOCK_LOCATION = [[[0, 0], [0, 1], [0, 2], [0, 3]], [[-1, 0], [0, 0], [1, 0], [2, 0]]]
        self.BLOCK_COLOR = 'yellow'
        self.BLOCK_NUM = 2

class BLOCK_3():
    '''
    反“Z”字型方块
    '''
    def __init__(self):
        self.BLOCK_LOCATION = [[[0, 0], [0, 1], [1, 0], [-1, 1]], [[0, 1], [0, 2], [-1, 0], [-1, 1]]]
        self.BLOCK_COLOR = 'pink'
        self.BLOCK_NUM = 3

class BLOCK_4():
    '''
    “L”字型方块
    '''
    def __init__(self):
        self.BLOCK_LOCATION = [[[0, 0], [0, 1], [0, 2], [1, 2]], [[-1, 0], [-1, 1], [0, 0], [1, 0]],
                               [[1, 0], [1, 1], [1, 2], [0, 0]], [[1, 0], [1, 1], [0, 1], [-1, 1]]]
        self.BLOCK_COLOR = 'green'
        self.BLOCK_NUM = 4

class BLOCK_5():
    '''
    “Z”字型方块
    '''
    def __init__(self):
        self.BLOCK_LOCATION = [[[0, 0], [0, 1], [1, 1], [-1, 0]], [[0, 0], [0, 1], [-1, 1], [-1, 2]]]
        self.BLOCK_COLOR = 'purple'
        self.BLOCK_NUM = 5

class BLOCK_6():
    '''
    反“L”字型方块
    '''
    def __init__(self):
        self.BLOCK_LOCATION = [[[0, 0], [0, 1], [0, 2], [-1, 2]], [[-1, 0], [-1, 1], [0, 1], [1, 1]],
                               [[-1, 0], [-1, 1], [-1, 2], [0, 0]], [[-1, 0], [0, 0], [1, 0], [1, 1]]]
        self.BLOCK_COLOR = 'orange'
        self.BLOCK_NUM = 6

class BLOCK_7():
    '''
    “T”字型方块
    '''
    def __init__(self):
        self.BLOCK_LOCATION = [[[0, 0], [0, 1], [0, 2], [-1, 1]], [[0, 0], [0, 1], [1, 1], [-1, 1]],
                               [[0, 0], [0, 1], [1, 1], [0, 2]], [[-1, 1], [0, 1], [1, 1], [0, 2]]]
        self.BLOCK_COLOR = 'white'
        self.BLOCK_NUM = 7

class Tetris():
    def __init__(self):
        self.width = 360        # 界面宽度
        self.height = 700       # 界面高度
        self.grid_size = 30     # 单格子边长
        self.row_num = 20       # 行数
        self.col_num = 12       # 列数

        self.speed = 500        # 格子下降速度
        self.total_score = 0    # 总分数

        self.inner_row_num = self.row_num - 2       # 内部格子空间行数
        self.inner_col_num = self.col_num - 2       # 内部格子空间列数

        self.grid = np.full((self.row_num, self.col_num), 0)    # 记录格子空间的矩阵
        self.grid_init()

        self.BLOCK_1 = BLOCK_1()
        self.BLOCK_2 = BLOCK_2()
        self.BLOCK_3 = BLOCK_3()
        self.BLOCK_4 = BLOCK_4()
        self.BLOCK_5 = BLOCK_5()
        self.BLOCK_6 = BLOCK_6()
        self.BLOCK_7 = BLOCK_7()
        self.block_list = [self.BLOCK_1, self.BLOCK_2, self.BLOCK_3, self.BLOCK_4, self.BLOCK_5, self.BLOCK_6, self.BLOCK_7]
        self.color_dic = {1: self.BLOCK_1.BLOCK_COLOR, 2: self.BLOCK_2.BLOCK_COLOR, 3: self.BLOCK_3.BLOCK_COLOR,
                          4: self.BLOCK_4.BLOCK_COLOR, 5: self.BLOCK_5.BLOCK_COLOR, 6: self.BLOCK_6.BLOCK_COLOR,
                          7: self.BLOCK_7.BLOCK_COLOR, 0: 'black', -1: 'grey'}      # 颜色对照表

        self.tk = Tk()
        self.tk.title('俄罗斯方块')
        self.tk.geometry(f'{self.width}x{self.height}')

        self.canvas = Canvas(self.tk, width = self.width, height = self.height, background='#FFFFFF')
        self.canvas.pack()
        self.canvas.focus_set()  # 聚焦
        self.canvas.bind("<KeyPress-Left>", self.move)
        self.canvas.bind("<KeyPress-Right>", self.move)
        self.canvas.bind("<KeyPress-Down>", self.move)
        self.canvas.bind("<KeyPress-Up>", self.block_change)

        self.draw_grid()

        self.start_game()
        self.tk.mainloop()

    def grid_init(self):
        # 墙体赋值
        for i in range(0, self.row_num):
            self.grid[i][0] = -1
            self.grid[i][self.col_num - 1] = -1
        for i in range(0, self.col_num):
            self.grid[0][i] = -1
            self.grid[self.row_num - 1][i] = -1

    def draw_grid(self):
        # 画格子
        for i in range(0, self.row_num):
            for j in range(0, self.col_num):
                self.canvas.create_rectangle(j * self.grid_size + 2, i * self.grid_size + 2,
                                            (j + 1) * self.grid_size - 2, (i + 1) * self.grid_size - 2,
                                             fill=self.color_dic[self.grid[i][j]])
        self.label_text = StringVar()
        self.label_text.set('当前分数:' + str(self.total_score))
        self.label_score = Label(self.tk, textvariable=self.label_text, font=('宋体', 20), foreground='#000000', background='#FFFFFF').place_configure(x=70, y=620)

    def block_change(self, event):
        # 方块变形
        tmp_change_num = self.change_num + 1
        tmp_choose_one = tmp_change_num % len(self.this_block.BLOCK_LOCATION)
        tmp_block_location = [[i[0] + self.block_col_offset, i[1] + self.block_row_offset] for i in
                                    self.this_block.BLOCK_LOCATION[tmp_choose_one]]
        for i in tmp_block_location:
            if self.grid[i[1], i[0]] != 0 and i not in self.this_block_location:
                return

        self.change_num += 1
        self.choose_one = self.change_num % len(self.this_block.BLOCK_LOCATION)
        for i in self.this_block_location:
            self.grid[i[1], i[0]] = 0
        self.this_block_location = [[i[0] + self.block_col_offset, i[1] + self.block_row_offset] for i in
                                    self.this_block.BLOCK_LOCATION[self.choose_one]]
        self.canvas.delete('all')

        for i in self.this_block_location:
            self.grid[i[1], i[0]] = self.this_block.BLOCK_NUM

        self.draw_grid()

    def get_random_block(self):
        # 获取随机的格子,开始下落
        self.this_block = self.block_list[random.randint(0,len(self.block_list)-1)]
        self.block_col_offset = 5
        self.block_row_offset = 1
        self.choose_one = 0
        self.this_block_location = [[i[0] + self.block_col_offset, i[1] + self.block_row_offset] for i in self.this_block.BLOCK_LOCATION[self.choose_one]]
        self.start_judge = True
        self.change_num = 0
        if self.if_game_end():
            messagebox.showinfo('提示', '游戏结束')
            self.tk.destroy()

    def move(self, event):
        # 键盘事件,移动
        if event.keysym == 'Left':
            next_block_location = [[i[0] - 1, i[1]] for i in self.this_block_location]
            for i in next_block_location:
                if i not in self.this_block_location and self.grid[i[1], i[0]] != 0:
                    return
            for i in self.this_block_location:
                self.grid[i[1], i[0]] = 0
            self.block_col_offset -= 1
            self.this_block_location = [[i[0] + self.block_col_offset, i[1] + self.block_row_offset] for i in self.this_block.BLOCK_LOCATION[self.choose_one]]
        elif event.keysym == 'Right':
            next_block_location = [[i[0] + 1, i[1]] for i in self.this_block_location]
            for i in next_block_location:
                if i not in self.this_block_location and self.grid[i[1], i[0]] != 0:
                    return
            for i in self.this_block_location:
                self.grid[i[1], i[0]] = 0
            self.block_col_offset += 1
            self.this_block_location = [[i[0] + self.block_col_offset, i[1] + self.block_row_offset] for i in self.this_block.BLOCK_LOCATION[self.choose_one]]
        elif event.keysym == 'Down':
            for i in self.this_block_location:
                self.grid[i[1], i[0]] = 0
            while self.if_block_end() == False:
                self.block_row_offset += 1
                self.this_block_location = [[i[0] + self.block_col_offset, i[1] + self.block_row_offset] for i in self.this_block.BLOCK_LOCATION[self.choose_one]]
        self.canvas.delete('all')

        for i in self.this_block_location:
            self.grid[i[1], i[0]] = self.this_block.BLOCK_NUM

        self.draw_grid()

    def start_game(self):
        # 开始游戏
        self.get_random_block()
        self.block_move()

    def block_eliminate(self):
        # 方块清除
        tmplist = self.grid.tolist()
        continuous_row = 0
        score = 0
        for i in range(1, self.inner_row_num + 1):
            tmpls = tmplist[i][1: self.inner_col_num + 1]
            if min(tmpls) > 0:
                continuous_row += 1
                for j in range(i, 1, -1):
                    self.grid[j] = self.grid[j - 1]
            else:
                if continuous_row == 1:
                    score += 100
                elif continuous_row == 2:
                    score += 200
                elif continuous_row == 3:
                    score += 400
                elif continuous_row >= 4:
                    score += 800
                continuous_row = 0
        if continuous_row == 1:
            score += 100
        elif continuous_row == 2:
            score += 200
        elif continuous_row == 3:
            score += 400
        elif continuous_row >= 4:
            score += 800
        continuous_row = 0
        return score

    def if_game_end(self):
        # 判断游戏是否结束
        for i in self.this_block_location:
            if self.grid[i[1], i[0]] > 0:
                return True
        return False

    def if_block_end(self):
        # 判断方块是否下落完毕
        next_block_location = [[i[0], i[1] + 1] for i in self.this_block_location]
        for i in next_block_location:
            next_block_x = i[1]
            next_block_y = i[0]
            if i not in self.this_block_location and self.grid[next_block_x, next_block_y] != 0:
                return True
        return False

    def block_move(self):
        # 方块的自动下落
        if self.if_block_end() == True:
            self.total_score += self.block_eliminate()
            self.get_random_block()

        for i in self.this_block_location:
            self.grid[i[1], i[0]] = 0

        if self.start_judge != True:
            self.block_row_offset += 1
            self.this_block_location = [[i[0] + self.block_col_offset, i[1] + self.block_row_offset] for i in self.this_block.BLOCK_LOCATION[self.choose_one]]

        self.canvas.delete('all')

        for i in self.this_block_location:
            self.grid[i[1], i[0]] = self.this_block.BLOCK_NUM

        self.draw_grid()
        self.start_judge = False

        if self.if_block_end() == False:
            self.canvas.after(self.speed, self.block_move)
        else:
            self.get_random_block()
            self.canvas.after(self.speed, self.block_move)

if __name__ == '__main__':
    tetris = Tetris()

  • 8
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

moyuweiqing

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值