• 写的第一个俄罗斯方块


    #object: 用tkinter绘制俄罗斯方块
    #writer: mike
    #time: 2020,09,04
    
    import tkinter as tk
    import random
    #import pygame
    from tkinter import messagebox
    import time
    
    
    window = tk.Tk()
    window.geometry("500x600")
    window.title("俄罗斯方块")
    
    #全局变量
    rows = 17
    coloms = 10
    cell_size = 30
    FPS = 500
    SHAPES = {
    
    'o': [(-1,-1),(0,-1),(-1,0),(0,0)],
    's':[(-1,0),(0,0),(0,-1),(1,-1)],
    't':[(-1,-1),(0,-1),(0,0,),(1,-1)],
    'i':[(0,-2),(0,-1),(0,0),(0,1)],
    'l':[(-1,-2),(0,0),(-1,-1),(-1,0)],
    'j':[(-1,0),(0,0),(0,-1),(0,-2)],
    'z':[(-1,-1),(0,-1),(0,0),(1,0)],
    }
    SHAPESCOLOR = {
    'o': "blue",
    's':"red",
    't':"yellow",
    "i":"green",
    'l':"purple",
    'j':"orange",
    'z':"Cyan",
    
    }
    CURRENT_BLOCK = None
    SCORE = 0
    
    #记录已经生成的小方块
    #初始化为空的二维数组
    block_list = []
    for i in range(rows):
        i_row = ["" for j in range(coloms)]
        block_list.append(i_row)
    
    #生成随机的俄罗斯方块
    def generate_new_block():
        kind = random.choice(list(SHAPES.keys()))
        new_block = {
            #注意这里应该用地板除,否则产生的方块坐标就乱了
            'cell_position' : [(coloms//2), 0],
            'cell_list' : SHAPES[kind],
            'kind' : kind,
        }
        return new_block
    
    
    #在指定的行,指定的列,绘制指定颜色的一个方格
    def draw_cell(canvas, x, y, color="#CCCCCC"):
        """
        canvas: 画布
        r: 行数
        c: 列数
        color:颜色
        
        """
        #左上角坐标
        x_left = x*cell_size
        y_left = y*cell_size
        #右上角坐标
        x_right = x_left + cell_size
        y_right = y_left + cell_size
        #绘制矩形
        canvas.create_rectangle(x_left, y_left, x_right, y_right, fill = color, outline = "white", width = 2)
    
    #绘制整个背景
    def draw_whole_background(canvas,block):
        for i in range(rows):
            for j in range(coloms):
                #取出当前的数组中的标记
                cell_type = block[i][j]
                #如果当前数组中有值,就按当前颜色来绘制,如果没有值,就按背景色来绘制
                if cell_type:
                    draw_cell(canvas,j, i, SHAPESCOLOR[cell_type])
                else:
                    draw_cell(canvas, j, i )
    
    
    #绘制特定的形状
    def draw_special_cell(canvas, r, c, cell_list, color = "#CCCCCC"):
        """
        canvas: 画布
        r:行
        c:列
        cell_list: 关于(0,1)的具体的形状
        color: 具体的颜色
        """
        for cell in cell_list:
            #得到相加之后的小方格的行列
            x, y = cell
            cell_x = r + x
            cell_y = c + y
            #依靠知觉,将colums与rows互换了位置
            if cell_x < coloms and cell_y < rows and cell_x>= 0 and cell_y >= 0:
                draw_cell(canvas, cell_x, cell_y, color)
    
    #绘制移动的带形状对的方块
    #思路为:首先将原有方块绘制成背景色,移动坐标,绘制新颜色
    #本函数position 的设置默认是不移动
    def move_shape(canvas, block, position = [0,0]):
        """
        canvas:画布
        block:包括了一个特定形状的所有内容
        position:移动的位置
    
        """
        shape_type = block['kind']
        c, r = block['cell_position']
        cell_list = block['cell_list']
        #将原有形状填充为背景色
        draw_special_cell(canvas, c, r, cell_list) #默认就是背景色
    
        #绘制新背景色
        d_x, d_y = position
        new_x =  c + d_x
        new_y = r + d_y
        #更改原有的字典
        block['cell_position'] = [new_x, new_y]
        draw_special_cell(canvas, new_x, new_y, cell_list, SHAPESCOLOR[shape_type])
    
    
    # 刷新屏幕
    def screen_loop():
        #window.update()
        global CURRENT_BLOCK
        #如果当前没有形状,则生成一个形状
        if CURRENT_BLOCK is None:
            new_block = generate_new_block()
            move_shape(canvas, new_block)
            CURRENT_BLOCK = new_block
            #如果数组中已经有标记了,也就是出生点已经被占用了,那么游戏结束
            if not check_move(CURRENT_BLOCK):
                messagebox.showinfo("GAME OVER ", "you final score is %d"%SCORE)
                window.destroy()
                return
         
        else:
          
            #如果小方格通过了边界检查,就向下移动
            if check_move(CURRENT_BLOCK, [0,1]):
            #如果已经有一个形状了,那么就向下移动这个小方块,直到这个小方块落地。
                move_shape(canvas, CURRENT_BLOCK, [0,1])
            #如果小方块超过了边界,就停止移动,并且在最初的位置生成新的小方块
            else:
                #当小方块无法移动时, 先将小方块存进数组,表示这几个小方格已经被占用了
                save_block(CURRENT_BLOCK)
                CURRENT_BLOCK = None
        
            
        clear_row()
    
        #生成一个显示分数的lable
        var = tk.StringVar()
        var.set("score is " + str(SCORE))
        fen_label = tk.Label(window, textvariable = var, bg = '#FFBBBB', width = 20, height = 2)
        fen_label.place(x=330, y=150)
    
        #这条语句是不是有递归的意思,如果是,那就能解释越来越慢的原因
        print(len(block_list[3]))
        window.after(FPS, screen_loop)
    
    #检查小方块是否会超出边界
    #这里position的默认值是0
    def check_move(block, direction = [0,0]):
        #得到圆点的坐标
        x, y = block['cell_position']
        #得到其他小格子的坐标
        qita = block['cell_list']
        for cell in qita:
            cell_x, cell_y = cell
            #通过循环计算出各个格子的新坐标
            new_x = cell_x + x + direction[0]
            new_y = cell_y + y + direction[1]
            if  new_x < 0 or new_x >= coloms or new_y >= rows:
                return False
            #如果小方格已经被标记了,那么也不能移动了
            #如果不见 new_y 大于0 , 那么小方格会在上方不下来, 因为在列表中,如果行为负数,列表默认从导数第一个算起
            #注意这里如果是 new_y > 0, 依然会有已经满行的,但是还在出生点生成小方格
            if block_list[new_y][new_x] and new_y>=0:
                return False
    
        return True
    
    #记录已经有的小方块的函数
    def save_block(block):
        block_type = block['kind']
        hang, lie = block['cell_position']
        c_list = block['cell_list']
    
        #将每一个形状的小方格纪录进二维数组
        for cell in c_list:
            xx, yy = cell
            new_xx = hang + xx
            new_yy = lie + yy
            block_list[new_yy][new_xx] = block_type
    
    #左右移动小方块
    def left_right_move(event):
        direction = [0,0]
        #event 表示键盘上的事件
        if event.keysym == "Left":
            direction = [-1,0]
        elif event.keysym == "Right":
            direction = [1,0]
    
        global CURRENT_BLOCK
        #如果当前block不为空,并且向下移动也没越界,没有标记,那么向下移动
        if CURRENT_BLOCK is not None and check_move(CURRENT_BLOCK, direction):
            move_shape(canvas, CURRENT_BLOCK, direction)
    
    #旋转小方块
    def rotate_move(event):
        global CURRENT_BLOCK
        if CURRENT_BLOCK is None:
            return
        #计算旋转的坐标
        yuan_list = CURRENT_BLOCK['cell_list']
        rotate_list = []
        for cell in yuan_list:
            cu_x, cu_y = cell
            #依据正余旋函数公式,旋转90度相当于,(x,y )变为(y,-x)
            rotate_xy = [cu_y, -cu_x]
            rotate_list.append(rotate_xy)
        #建立旋转后的小方块字典
        after_rotate = {
            'kind':CURRENT_BLOCK['kind'],
            'cell_list':rotate_list,
            'cell_position':CURRENT_BLOCK['cell_position'],
    
        }
        #如果可以变换,那么清空原有的小方块,并绘制旋转后的小方块
        if check_move(after_rotate):
            #获得当前的圆点坐标
            xx , yy = CURRENT_BLOCK['cell_position']
            #清空当前的小方块
            draw_special_cell(canvas, xx, yy , CURRENT_BLOCK['cell_list'])
            #绘制旋转后的小方块
            draw_special_cell(canvas, xx, yy, after_rotate['cell_list'], SHAPESCOLOR[CURRENT_BLOCK['kind']])
            CURRENT_BLOCK = after_rotate
    
    #向下移动小方块
    def speed_down(event):
        global CURRENT_BLOCK
        if CURRENT_BLOCK is None:
            return
         #得到当前的小方块的字典
        cell_li = CURRENT_BLOCK['cell_list']
        xxx, yyy = CURRENT_BLOCK['cell_position']
        #初始化最小的深度
        min_height = rows
        #得到每个形状的各个小方格的坐标
        for cell in cell_li:
            cel_x, cel_y = cell
            new_x = xxx + cel_x
            new_y = yyy + cel_y
            #判断当前的小方块是否已经被占用
            if block_list[new_y][new_x]:
                return 
            h = 0
            #从上到下遍历每一个小格子
            for ri in range(new_y+1, rows):
                #如果下面的小格子已经被占用了,就退出
                if block_list[ri][new_x]:
                    break
                else:
                    #如果下面的小格子没有冲突,那么就加一
                    h += 1
            #如果当前的可移动行数比当前的最小值小,就更新当前的最小值
            if h < min_height:
                min_height = h
    
        down = [0,min_height]
        #直接定位到最下面的位置
        if check_move(CURRENT_BLOCK, down):
            move_shape(canvas, CURRENT_BLOCK, down)
    
    #判断指定行是否已经满了
    def check_row_complete(row):
        for cell in row:
            if cell == '':
                return False
        else:
            return True
    
    #清除已经完成的行
    def clear_row():
        #下面的变量用于检查一行是否已经满了
        whether_complete = False
        for ri in range(len(block_list)):
            if check_row_complete(block_list[ri]):
                whether_complete = True
                #如果已经满的行,大于第一行,那么所有的当前行都等于上一行的内容
                if ri > 0:
                    #遍历从当前行往上的所有行,当然除了第一行除外
                    for curr_r in range(ri,0,-1):
                        block_list[curr_r] = block_list[curr_r-1]
                        block_list[0] = ['' for j in range(coloms)]
                #对于第一行,直接赋值为空
                else:
                    block_list[ri] = ['' for j in range(coloms)]
        #如果发现有行已经被清除了,那么重新绘制一遍所有的小方格
        if whether_complete:
            draw_whole_background(canvas, block_list)
            global SCORE
            SCORE += 100
    
    
    # 加入音乐
    # pygame.mixer.init()
    # pygame.mixer.music.load(r"C:usersmike1desktop123.mp3")
    # pygame.mixer.music.play(-1,0)
    
    
    #游戏界面的宽与高
    width = coloms*cell_size
    height = rows*cell_size
    
    #绘制游戏界面
    canvas = tk.Canvas(window, width = width, height = height)
    canvas.pack(side = 'left')
    
    #绘制基本的信息
    tk.Label(window, text = "writer: mike 
    time: 2020.09.04", bg = "#99FF33").place(x=350, y=50)
    
    draw_whole_background(canvas,block_list)
    
    
    #绑定键盘的左右键
    canvas.focus_set()
    canvas.bind("<KeyPress-Left>", left_right_move)
    canvas.bind("<KeyPress-Right>", left_right_move)
    canvas.bind("<KeyPress-Up>", rotate_move)
    canvas.bind("<KeyPress-Down>", speed_down)
    
    #通过循环实现刷新屏幕,不知上面的写法是不是递归
    # while 1:
    #     screen_loop()
      
    #     time.sleep(0.3)
       
    screen_loop()
    
    
    window.mainloop()

    以上的俄罗斯方块,运行到一定时间之后会变的很慢,难道是因为,用到了递归吗,我并没有用到递归啊, 并不是很清楚?

  • 相关阅读:
    首次调用u8api遇到的问题总结
    为datagridview添加自定义按钮
    我对数据库索引的初步了解
    ObjectARX 常见编译错误解决方式记录
    手动修改Visual Studio 版本互转工具
    [经验] FAS 20号指令的深入研究
    Object ARX 统一设置所有图层的RGB颜色
    ARX 选择集获得所有图形 遍历 实例 备忘
    ObjectARX2010 学习笔记002:读取已经存在的DWG文件中的内容
    利用编译时的全局声明对抗反编译
  • 原文地址:https://www.cnblogs.com/zijidefengge/p/13625704.html
Copyright © 2020-2023  润新知