1. 程式人生 > >Python3+pygame實現的俄羅斯方塊 程式碼完整 有演示效果

Python3+pygame實現的俄羅斯方塊 程式碼完整 有演示效果

一、簡單說明

80、90後的小夥伴都玩過“俄羅斯方塊”,那種“叱吒風雲”場景 偶爾閃現在腦海 真的是太爽了;如果沒有來得及玩過的同學,這次可以真正的自己做一個了

本例項用的是Python3(當然了Python3.5 3.6 3.7....都行 )+ pygame實現的

執行之前需要安裝pygame模組,安裝命令如下

pip install pygame -i https://mirrors.aliyun.com/pypi/simple/

二、執行效果

三、完整程式碼

下面的程式碼,用到了字型檔案yh.ttf,下載地址 https://www.itprojects.cn/detail.html?example_id=8bf48a0ac4cee29043d4122fe6e563cb

檔案main.py程式碼如下:

"""
作者:it專案例項網
更多專案例項,請訪問:www.itprojects.cn
"""

import random
import sys
import time

import pygame

from blocks import block_s, block_i, block_j, block_l, block_o, block_t, block_z

SCREEN_WIDTH, SCREEN_HEIGHT = 450, 750
BG_COLOR = (40, 40, 60)  # 背景色
BLOCK_COL_NUM = 10  # 每行的方格數
SIZE = 30  # 每個小方格大小
BLOCK_ROW_NUM = 25  # 每列的方個數
BORDER_WIDTH = 4  # 遊戲區邊框寬度
RED = (200, 30, 30)  # 紅色,GAME OVER 的字型顏色


def judge_game_over(stop_all_block_list):
    """
    判斷遊戲是否結束
    """
    if "O" in stop_all_block_list[0]:
        return True


def change_speed(score):
    speed_level = [("1", 0.5, 0, 20), ("2", 0.4, 21, 50), ("3", 0.3, 51, 100), ("4", 0.2, 101, 200), ("5", 0.1, 201, None)]
    for speed_info, speed, score_start, score_stop in speed_level:
        if score_stop and score_start <= score <= score_stop:
            return speed_info, speed
        elif score_stop is None and score >= score_start:
            return speed_info, speed


def judge_lines(stop_all_block_list):
    """
    判斷是否有同一行的方格,如果有則消除
    """
    # 記錄剛剛消除的行數
    move_row_list = list()
    # 消除滿格的行
    for row, line in enumerate(stop_all_block_list):
        if "." not in line:
            # 如果這一行沒有. 那麼就意味著全部是O,則消除這一行
            stop_all_block_list[row] = ['.' for _ in range(len(line))]
            move_row_list.append(row)

    # 如果沒有滿格的行,則結束此函式
    if not move_row_list:
        return 0

    # 移動剩餘的行到下一行
    for row in move_row_list:
        stop_all_block_list.pop(row)
        stop_all_block_list.insert(0, ['.' for _ in range(len(line))])

    return len(move_row_list) * 10


def add_to_stop_all_block_list(stop_all_block_list, current_block, current_block_start_row, current_block_start_col):
    """
    將當前已經停止移動的block新增到列表中
    """
    for row, line in enumerate(current_block):
        for col, block in enumerate(line):
            if block != '.':
                stop_all_block_list[current_block_start_row + row][current_block_start_col + col] = "O"


def change_current_block_style(current_block):
    """
    改變圖形的樣式
    """
    # 計算出,當前圖形樣式屬於哪個圖形
    current_block_style_list = None
    for block_style_list in [block_s, block_i, block_j, block_l, block_o, block_t, block_z]:
        if current_block in block_style_list:
            current_block_style_list = block_style_list

    # 得到當前正在用的圖形的索引(下標)
    index = current_block_style_list.index(current_block)
    # 它的下一個圖形的索引
    index += 1
    # 防止越界
    index = index % len(current_block_style_list)
    # 返回下一個圖形
    return current_block_style_list[index]


def judge_move_right(current_block, current_block_start_col):
    """
    判斷是否可以向右移動
    """
    # 先判斷列的方式是從右到左
    for col in range(len(current_block[0]) - 1, -1, -1):
        # 得到1列的所有元素
        col_list = [line[col] for line in current_block]
        # 判斷是否碰到右邊界
        if 'O' in col_list and current_block_start_col + col >= BLOCK_COL_NUM:
            return False
    return True


def judge_move_left(current_block, current_block_start_col):
    """
    判斷是否可以向左移動
    """
    # 先判斷列的方式是從左到右
    for col in range(len(current_block[0])):
        # 得到1列的所有元素
        col_list = [line[col] for line in current_block]
        # 判斷是否碰到右邊界
        if 'O' in col_list and current_block_start_col + col < 0:
            return False
    return True


def judge_move_down(current_block, current_block_start_row, current_block_start_col, stop_all_block_list):
    """
    判斷是否碰撞到其它圖形或者底邊界
    """
    # 得到其它圖形所有的座標
    stop_all_block_position = list()
    for row, line in enumerate(stop_all_block_list):
        for col, block in enumerate(line):
            if block != ".":
                stop_all_block_position.append((row, col))
    # print(stop_all_block_position)

    # 判斷碰撞
    for row, line in enumerate(current_block):
        if 'O' in line and current_block_start_row + row >= BLOCK_ROW_NUM:
            # 如果當前行有0,且從起始行開始算+當前顯示的行,超過了總行數,那麼就認為碰到了底部
            return False
        for col, block in enumerate(line):
            if block != "." and (current_block_start_row + row, current_block_start_col + col) in stop_all_block_position:
                return False

    return True


def get_block():
    """
    建立一個圖形
    """
    block_style_list = random.choice([block_s, block_i, block_j, block_l, block_o, block_t, block_z])
    return random.choice(block_style_list)


def main():
    pygame.init()
    screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
    pygame.display.set_caption('俄羅斯方塊')

    current_block = get_block()  # 當前圖形
    current_block_start_row = -2  # 當前圖片從哪一行開始顯示圖形
    current_block_start_col = 4  # 當前圖形從哪一列開始顯示
    next_block = get_block()  # 下一個圖形
    last_time = time.time()
    speed = 0.5  # 降落的速度
    speed_info = '1'  # 顯示的速度等級

    # 定義一個列表,用來儲存所有的已經停止移動的形狀
    stop_all_block_list = [['.' for i in range(BLOCK_COL_NUM)] for j in range(BLOCK_ROW_NUM)]

    # 字型
    font = pygame.font.Font('yh.ttf', 24)  # 黑體24
    game_over_font = pygame.font.Font("yh.ttf", 72)
    game_over_font_width, game_over_font_height = game_over_font.size('GAME OVER')
    game_again_font_width, game_again_font_height = font.size('滑鼠點選任意位置,再來一局')

    # 得分
    score = 0

    # 標記遊戲是否結束
    game_over = False

    # 建立計時器(防止while迴圈過快,佔用太多CPU的問題)
    clock = pygame.time.Clock()
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_LEFT:
                    if judge_move_left(current_block, current_block_start_col - 1):
                        current_block_start_col -= 1
                elif event.key == pygame.K_RIGHT:
                    if judge_move_right(current_block, current_block_start_col + 1):
                        current_block_start_col += 1
                elif event.key == pygame.K_UP:
                    current_block_next_style = change_current_block_style(current_block)
                    if judge_move_left(current_block_next_style, current_block_start_col) and \
                            judge_move_right(current_block_next_style, current_block_start_col) and \
                            judge_move_down(current_block, current_block_start_row, current_block_start_col, stop_all_block_list):
                        # 判斷新的樣式沒有越界
                        current_block = current_block_next_style
                elif event.key == pygame.K_DOWN:
                    # 判斷是否可以向下移動,如果碰到底部或者其它的圖形就不能移動了
                    if judge_move_down(current_block, current_block_start_row + 1, current_block_start_col, stop_all_block_list):
                        current_block_start_row += 1
            elif event.type == pygame.MOUSEBUTTONDOWN and event.button:
                if game_over:
                    # 重置遊戲用到的變數
                    current_block = get_block()  # 當前圖形
                    current_block_start_row = -2  # 當前圖片從哪一行開始顯示圖形
                    current_block_start_col = 4  # 當前圖形從哪一列開始顯示
                    next_block = get_block()  # 下一個圖形
                    stop_all_block_list = [['.' for i in range(BLOCK_COL_NUM)] for j in range(BLOCK_ROW_NUM)]
                    score = 0
                    game_over = False

        # 判斷是否修改當前圖形顯示的起始行
        if not game_over and time.time() - last_time > speed:
            last_time = time.time()
            # 判斷是否可以向下移動,如果碰到底部或者其它的圖形就不能移動了
            if judge_move_down(current_block, current_block_start_row + 1, current_block_start_col, stop_all_block_list):
                current_block_start_row += 1
            else:
                # 將這個圖形儲存到統一的列表中,這樣便於判斷是否成為一行
                add_to_stop_all_block_list(stop_all_block_list, current_block, current_block_start_row, current_block_start_col)
                # 判斷是否有同一行的,如果有就消除,且加上分數
                score += judge_lines(stop_all_block_list)
                # 判斷遊戲是否結束(如果第一行中間有O那麼就表示遊戲結束)
                game_over = judge_game_over(stop_all_block_list)
                # 調整速度
                speed_info, speed = change_speed(score)
                # 建立新的圖形
                current_block = next_block
                next_block = get_block()
                # 重置資料
                current_block_start_col = 4
                current_block_start_row = -2

        # 畫背景(填充背景色)
        screen.fill(BG_COLOR)

        # 畫遊戲區域分隔線
        pygame.draw.line(screen, (100, 40, 200), (SIZE * BLOCK_COL_NUM, 0), (SIZE * BLOCK_COL_NUM, SCREEN_HEIGHT), BORDER_WIDTH)

        # 顯示當前圖形
        for row, line in enumerate(current_block):
            for col, block in enumerate(line):
                if block != '.':
                    pygame.draw.rect(screen, (20, 128, 200), ((current_block_start_col + col) * SIZE, (current_block_start_row + row) * SIZE, SIZE, SIZE), 0)

        # 顯示所有停止移動的圖形
        for row, line in enumerate(stop_all_block_list):
            for col, block in enumerate(line):
                if block != '.':
                    pygame.draw.rect(screen, (20, 128, 200), (col * SIZE, row * SIZE, SIZE, SIZE), 0)

        # 畫網格線 豎線
        for x in range(BLOCK_COL_NUM):
            pygame.draw.line(screen, (0, 0, 0), (x * SIZE, 0), (x * SIZE, SCREEN_HEIGHT), 1)
        # 畫網格線 橫線
        for y in range(BLOCK_ROW_NUM):
            pygame.draw.line(screen, (0, 0, 0), (0, y * SIZE), (BLOCK_COL_NUM * SIZE, y * SIZE), 1)

        # 顯示右側(得分、速度、下一行圖形)
        # 得分
        score_show_msg = font.render('得分: ', True, (255, 255, 255))
        screen.blit(score_show_msg, (BLOCK_COL_NUM * SIZE + 10, 10))
        score_show_msg = font.render(str(score), True, (255, 255, 255))
        screen.blit(score_show_msg, (BLOCK_COL_NUM * SIZE + 10, 50))
        # 速度
        speed_show_msg = font.render('速度: ', True, (255, 255, 255))
        screen.blit(speed_show_msg, (BLOCK_COL_NUM * SIZE + 10, 100))
        speed_show_msg = font.render(speed_info, True, (255, 255, 255))
        screen.blit(speed_show_msg, (BLOCK_COL_NUM * SIZE + 10, 150))
        # 下一個圖形(文字提示)
        next_style_msg = font.render('下一個: ', True, (255, 255, 255))
        screen.blit(next_style_msg, (BLOCK_COL_NUM * SIZE + 10, 200))
        # 下一個圖形(圖形)
        for row, line in enumerate(next_block):
            for col, block in enumerate(line):
                if block != '.':
                    pygame.draw.rect(screen, (20, 128, 200), (320 + SIZE * col, (BLOCK_COL_NUM + row) * SIZE, SIZE, SIZE), 0)
                    # 顯示這個方格的4個邊的顏色
                    # 左
                    pygame.draw.line(screen, (0, 0, 0), (320 + SIZE * col, (BLOCK_COL_NUM + row) * SIZE), (320 + SIZE * col, (BLOCK_COL_NUM + row + 1) * SIZE), 1)
                    # 上
                    pygame.draw.line(screen, (0, 0, 0), (320 + SIZE * col, (BLOCK_COL_NUM + row) * SIZE), (320 + SIZE * (col + 1), (BLOCK_COL_NUM + row) * SIZE), 1)
                    # 下
                    pygame.draw.line(screen, (0, 0, 0), (320 + SIZE * col, (BLOCK_COL_NUM + row + 1) * SIZE), (320 + SIZE * (col + 1), (BLOCK_COL_NUM + row + 1) * SIZE), 1)
                    # 右
                    pygame.draw.line(screen, (0, 0, 0), (320 + SIZE * (col + 1), (BLOCK_COL_NUM + row) * SIZE), (320 + SIZE * (col + 1), (BLOCK_COL_NUM + row + 1) * SIZE), 1)

        # 顯示遊戲結束畫面
        if game_over:
            game_over_tips = game_over_font.render('GAME OVER', True, RED)
            screen.blit(game_over_tips, ((SCREEN_WIDTH - game_over_font_width) // 2, (SCREEN_HEIGHT - game_over_font_height) // 2))
            # 顯示"滑鼠點選任意位置,再來一局"
            game_again = font.render('滑鼠點選任意位置,再來一局', True, RED)
            screen.blit(game_again, ((SCREEN_WIDTH - game_again_font_width) // 2, (SCREEN_HEIGHT - game_again_font_height) // 2 + 80))

        # 重新整理顯示(此時窗口才會真正的顯示)
        pygame.display.update()
        # FPS(每秒鐘顯示畫面的次數)
        clock.tick(60)  # 通過一定的延時,實現1秒鐘能夠迴圈60次


if __name__ == '__main__':
    main()

檔案blocks.py程式碼如下:

# S形方塊
block_s = [['.OO',
            'OO.',
            '...'],
           ['O..',
            'OO.',
            '.O.']]
# Z形方塊
block_z = [['OO.',
            '.OO',
            '...'],
           ['.O.',
            'OO.',
            'O..']]
# I型方塊
block_i = [['.O..',
            '.O..',
            '.O..',
            '.O..'],
           ['....',
            '....',
            'OOOO',
            '....']]
# O型方塊
block_o = [['OO',
            'OO']]
# J型方塊
block_j = [['O..',
            'OOO',
            '...'],
           ['.OO',
            '.O.',
            '.O.'],
           ['...',
            'OOO',
            '..O'],
           ['.O.',
            '.O.',
            'OO.']]
# L型方塊
block_l = [['..O',
            'OOO',
            '...'],
           ['.O.',
            '.O.',
            '.OO'],
           ['...',
            'OOO',
            'O..'],
           ['OO.',
            '.O.',
            '.O.']]
# T型方塊
block_t = [['.O.',
            'OOO',
            '...'],
           ['.O.',
            '.OO',
            '.O.'],
           ['...',
            'OOO',
            '.O.'],
           ['.O.',
            'OO.',
            '.O.']]

&n