Python制作俄罗斯方块游戏软件示例(第1节)


小游戏开发,制作俄罗斯方块

Python是一种万能型编程语言,可以应用于游戏开发领域。Python可以在各种操作系统上运行,包括Windows、Mac OS和Linux等,使得游戏的移植变得更加容易。本节教程将简单介绍如何通过Python编程制作一个俄罗斯方块游戏软件,最后用pyinstaller模块把游戏软件打包成exe可执行文件,直接发给别人,可以在任意一台电脑上运行游戏,完全不依赖Python环境。

1、俄罗斯方块游戏代码

在Python编程中,制作俄罗斯方块游戏只需要运行包含游戏代码的文件(以“.py”为后缀名),就可以弹出游戏画面,游戏完整的代码如下:

import tkinter as tk
from tkinter import messagebox
import random
Row = 20
Col = 12
DELAY = 150
size = 30
height = Row * size
width = Col * size
MODEL = {
    "Z": [(-1, -1), (0, -1), (0, 0), (1, 0)],
    "O": [(-1, -1), (0, -1), (-1, 0), (0, 0)],
    "S": [(-1, 0), (0, 0), (0, -1), (1, -1)],
    "T": [(-1, 0), (0, 0), (0, -1), (1, 0)],
    "I": [(0, 1), (0, 0), (0, -1), (0, -2)],
    "L": [(-1, 0), (0, 0), (-1, -1), (-1, -2)],
    "J": [(-1, 0), (0, 0), (0, -1), (0, -2)]
}
MODELCOLOR = {
    "O":"violet",
    "S":"navy",
    "T":"gold",
    "I":"red",
    "L":"blue",
    "J":"green",
    "Z":"brown",
}
def print_board(app, box):
    for ri in range(Row):
        for ci in range(Col):
            cell_type = box[ri][ci]
            if cell_type:
                print_cell_background(app, ci, ri, MODELCOLOR[cell_type])
            else:
                print_cell_background(app, ci, ri)
def print_cell_background(app, col, row, color="#e6e6e6"):
    x0 = col * size
    y0 = row * size
    x1 = col * size + size
    y1 = row * size + size
    app.create_rectangle(x0, y0, x1, y1, fill=color, outline="white", width=2)
def print_blank_board(app):
    for ri in range(Row):
        for cj in range(Col):
            print_cell_background(app, cj, ri)
def print_cells(app, col, row, cell_list, color="#e6e6e6"):
    for cell in cell_list:
        cell_col, cell_row = cell
        ci = cell_col + col
        ri = cell_row + row
        if 0 <= col < Col and 0 <= row < Row:
            print_cell_background(app, ci, ri, color)
windows = tk.Tk()
app = tk.Canvas(windows, width=width, height=height)
app.pack()
box = []
for i in range(Row):
    i_row = ['' for j in range(Col)]
    box.append(i_row)
print_board(app, box)
def print_part_move(app, part, direction=[0,0]):
    shape_type = part['kind']
    c, r = part['cr']
    cell_list = part['cell_list']
    print_cells(app, c, r, cell_list)
    dc, dr = direction
    new_c, new_r = c + dc, r + dr
    part['cr'] = [new_c, new_r]
    print_cells(app, new_c, new_r, cell_list, MODELCOLOR[shape_type])
one_part = {
    'kind': 'O',
    'cell_list': MODEL['O'],
    'cr': [3, 3],
}
def product_new_part():
    kind = random.choice(list(MODEL.keys()))
    cr = [Col // 2, 0]
    new_part = {
        "kind": kind,
        "cell_list": MODEL[kind],
        'cr': cr
    }
    return new_part
def check_move(part, direction=[0,0]):
    cc, cr = part['cr']
    cell_list = part['cell_list']
    for cell in cell_list:
        cell_c, cell_r = cell
        c = cell_c + cc + direction[0]
        r = cell_r + cr + direction[1]
        if c < 0 or c >= Col or r >= Row:
            return False
        if r >= 0 and box[r][c]:
            return False
    return True
def save_to_box(part):
    shape_type = part['kind']
    cc, cr = part['cr']
    cell_list = part['cell_list']
    for cell in cell_list:
        cell_c, cell_r = cell
        c = cell_c + cc
        r = cell_r + cr
        box[r][c] = shape_type
def horizontal_move_part(event):
    direction = [0, 0]
    if event.keysym == 'Left':
        direction = [-1, 0]
    elif event.keysym == 'Right':
        direction = [1, 0]
    else:
        return
    global current_part
    if current_part is not None and check_move(current_part, direction):
        print_part_move(app, current_part, direction)
def rotate_part(event):
    global current_part
    if current_part is None:
        return
    cell_list = current_part['cell_list']
    rotate_list = []
    for cell in cell_list:
        cell_c, cell_r = cell
        rotate_cell = [cell_r, -cell_c]
        rotate_list.append(rotate_cell)
    part_after_rotate = {
        'kind': current_part['kind'],
        'cell_list': rotate_list,
        'cr': current_part['cr']
    }
    if check_move(part_after_rotate):
        cc, cr = current_part['cr']
        print_cells(app, cc, cr, current_part['cell_list'])
        print_cells(app, cc, cr, rotate_list, MODELCOLOR[current_part['kind']])
        current_part = part_after_rotate
def land(event):
    global current_part
    if current_part is None:
        return
    cell_list = current_part['cell_list']
    cc, cr = current_part['cr']
    min_height = Row
    for cell in cell_list:
        cell_c, cell_r = cell
        c, r = cell_c + cc, cell_r + cr
        if box[r][c]:
            return
        h = 0
        for ri in range(r + 1, Row):
            if box[ri][c]:
                break
            else:
                h += 1
        if h < min_height:
            min_height = h
    down = [0, min_height]
    if check_move(current_part, down):
        print_part_move(app, current_part, down)
def check_row_complete(row):
    for cell in row:
        if cell == '':
            return False
    return True
score = 0
windows.title("得分: %s" % score)
def check_and_clear():
    has_complete_row = False
    for ri in range(len(box)):
        if check_row_complete(box[ri]):
            has_complete_row = True
            if ri > 0:
                for cur_ri in range(ri, 0, -1):
                    box[cur_ri] = box[cur_ri - 1][:]
                box[0] = ['' for j in range(Col)]
            else:
                box[ri] = ['' for j in range(Col)]
            global score
            score += 10
    if has_complete_row:
        print_board(app, box)
        windows.title("得分: %s" % score)
def play_loop():
    windows.update()
    global current_part
    if current_part is None:
        new_part = product_new_part()
        print_part_move(app, new_part)
        current_part = new_part
        if not check_move(current_part, [0, 0]):
            messagebox.showinfo("游戏结束!", "你的得分是 %s" % score)
            windows.destroy()
            return
    else:
        if check_move(current_part, [0, 1]):
            print_part_move(app, current_part, [0, 1])
        else:
            save_to_box(current_part)
            current_part = None
    check_and_clear()
    windows.after(DELAY, play_loop)
current_part = None
app.focus_set()
app.bind("<KeyPress-Left>", horizontal_move_part)
app.bind("<KeyPress-Right>", horizontal_move_part)
app.bind("<KeyPress-Up>", rotate_part)
app.bind("<KeyPress-Down>", land)
if __name__ == '__main__':
    play_loop()
    windows.mainloop()

2、运行俄罗斯方块游戏

打开第2章教程中介绍的Pyhint编辑器,在代码框中输入以上游戏代码,点击“运行”按钮就会弹出游戏页面,如下图所示:

现在我们通过操作电脑键盘就可以玩游戏了,键盘的“左”、“右”键可以移动俄罗斯方块,“上”键则实现变换俄罗斯方块形状。

3、把游戏代码打包成exe文件

(1)前面我们已经介绍了,在Pyhint编辑器的代码框中输入俄罗斯方块游戏代码后,点击“运行”按钮就会弹出俄罗斯方块的游戏页面。现在,我们点击Pyhint编辑器的“另存为”按钮,把俄罗斯方块的游戏代码另存为“tetris.py”文件,Pyhint编辑器会自动把“tetris.py”文件保存在“Pyhint\Learn-Python\test”文件夹内。

(2)现在,暂时退出360等杀毒软件,因为有时候打包完成后,一些杀毒软件会误判打包好的exe文件为病毒,从而导致exe文件无法运行。这是因为杀毒软件在扫描过程中对于一些普通程序的行为没有进行充分的区分而产生误判。想要把Python程序打包成exe文件,首先需要安装PyInstaller模块。退出杀毒软件后,打开Pyhint编辑器,点击“打开终端”按钮,在弹出的黑色cmd终端窗口中,输入“pip install pyinstaller”后按Enter回车键安装PyInstaller模块,如果运行结果出现“Successfully installed”,说明已经安装成功,如下图所示:

(3)再次在cmd终端窗口中输入“Pyinstaller -F -w tetris.py”后按Enter回车键开始打包。耐心等待一会儿,直到提示“completed successfully”打包成功。如下图所示:

(4)打包成功后,在“Pyhint\Learn-Python\test”文件夹内生成了build和dist两个文件夹,以及一个tetris.spec文件。打开dist文件夹,就可以看到已打包完成的exe软件“tetris.exe”,如下图所示:

(5)这个时候一定要暂时退出360等杀毒软件,因为任何一个简单exe程序软件没有添加到杀毒软件的白名单中,都会被产生误判。最后鼠标双击tetris.exe运行软件,就可以弹出游戏页面,如下图所示:

这个exe软件可以移动到任意一个文件夹内,也可以直接发给别人,在任意一台电脑上都可以运行游戏,完全不依赖Python环境。