用python的pygame模块制作俄罗斯方块小游戏

前言

		想学习一下pygame,无意间看了鬼码博主的一个关于pygame编写俄罗斯方块的视频,于是就模仿着写一写,这篇博客不算是原创,但是在原代码的基础上,做出了很大的改动,算是二创。
		如果您试了这段代码,觉得不错,请不吝赏个赞,如果有BUG或需要改进的地方,请留个言或私信交流。
		刚开始接触pygame这个模块,基本上现在还处于睁眼瞎状态,很多方法的调用,大多都是模仿。
		如果转载或引用这段代码,请注明一下出处!

游戏截图

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

完整代码如下:

import random
import pygame


blocks = [
    [[1, 5, 9, 13], [4, 5, 6, 7]],
    [[4, 5, 9, 10], [2, 5, 6, 9]],
    [[6, 7, 9, 10], [1, 5, 6, 10]],
    [[1, 2, 5, 6]],
    [[1, 2, 5, 9], [0, 4, 5, 6], [1, 5, 9, 8], [4, 5, 6, 10]],
    [[1, 2, 6, 10], [5, 6, 7, 9], [2, 6, 10, 11], [3, 5, 6, 7]],
    [[1, 4, 5, 6], [1, 4, 5, 9], [4, 5, 6, 9], [1, 5, 6, 9]]
]
colors = [
    (0, 0, 0), (3, 56, 174), (114, 203, 59), (255, 166, 0), (255, 151, 28), (255, 50, 19)
]


class Block:
    def __init__(self, x, y):
        # 方块在游戏界面的坐标,颜色,形状
        self.x_in_grids = x
        self.y_in_grids = y
        self.color = random.randint(1, len(colors) - 1)
        self.type = random.randint(0, len(blocks) - 1)
        self.rotation = 0

    # 想要双屏显示,游戏界面显示第一个,另一个小屏显示即将下落的第二个
    def block(self):
        return blocks[self.type][self.rotation]

    def rotate(self):
        self.rotation = (self.rotation + 1) % len(blocks[self.type])


class Tetris:
    def __init__(self):
        self.position_x = 250
        self.position_y = -110
        self.grid_size = 30
        self.block = None
        self.game_state = 'running'
        self.grids = []
        self.interation = False
        self.level = 1
        self.score = 0
        self.pause = False
        for row_grids in range(24):
            new_line = []
            for column_grids in range(10):
                new_line.append(0)
            self.grids.append(new_line)

    def go_down(self, blocker):
        blocker.y_in_grids += 1
        if self.go_crash(blocker):
            blocker.y_in_grids -= 1
            self.freeze(blocker)
        self.break_lines()

    def freeze(self, blocker):
        for row in range(4):
            for column in range(4):
                if row * 4 + column in blocker.block():
                    self.grids[blocker.y_in_grids + row][blocker.x_in_grids + column] = blocker.color
        if sum(self.grids[4]) != 0:
            self.game_state = 'game_over'

    def go_crash(self, blocker):
        self.interation = False
        for row in range(4):
            for column in range(4):
                if row * 4 + column in blocker.block():
                    if blocker.y_in_grids + row > 23 or self.grids[blocker.y_in_grids + row][blocker.x_in_grids + column] != 0:
                        self.interation = True
        return self.interation

    def go_side(self, blocker, dx):
        if self.game_state == 'running':
            blocker.x_in_grids += dx
            lst = []
            for row in range(4):
                for column in range(4):
                    if row * 4 + column in blocker.block():
                        lst.append(column % 4)
            # print(sorted(lst))
            # 使方块移动时不会超过左右边界
            if blocker.x_in_grids < 0:
                blocker.x_in_grids = -sorted(lst)[0]
            if blocker.x_in_grids + sorted(lst)[-1] > 9:
                blocker.x_in_grids = 9 - sorted(lst)[-1]
            # 如果移动后的位置有方块,那么不可以移动
            lst1 =[]
            for row in range(4):
                for column in range(4):
                    if row * 4 + column in blocker.block():
                        lst1.append(self.grids[blocker.y_in_grids + row][blocker.x_in_grids + column])
            # print(lst1)
            if sum(lst1) != 0:
                blocker.x_in_grids -= dx

    def rotate(self, blocker):
        # 在旋转之前解决因为旋转而出现方块超出游戏的左右界面的BUG
        old_x = blocker.x_in_grids
        lst0 = []
        lst1 = []
        for row in range(4):
            for column in range(4):
                if row * 4 + column in blocker.block():
                    lst0.append(column)
                if row * 4 + column in blocks[blocker.type][
                        (blocker.rotation + 1) % len(blocks[blocker.type])]:
                    lst1.append(column)
        lst0.sort()
        lst1.sort()
        set0 = set(lst0)
        set1 = set(lst1)
        # print(lst0)
        # print(set0)
        # print(lst1)
        # print(set1)
        if blocker.type == 0:
            if blocker.x_in_grids < 0:
                blocker.x_in_grids = 0
            if blocker.x_in_grids > 6:
                blocker.x_in_grids = 6
        if blocker.type == 1:
            if blocker.x_in_grids < 0:
                blocker.x_in_grids = old_x + len(set1) - len(set0)
        if blocker.type == 2:
            if blocker.x_in_grids > 6:
                blocker.x_in_grids = old_x - len(set1) + len(set0)
        if blocker.type == 4:
            if blocker.x_in_grids < 0:
                blocker.x_in_grids += 1
            if blocker.x_in_grids > 7:
                blocker.x_in_grids -= 1
        if blocker.type == 5:
            if blocker.x_in_grids < -1 and len(set0) < len(set1) and lst0[0] != lst1[0]:
                blocker.x_in_grids += 1
            if blocker.x_in_grids > 6 and len(set0) < len(set1) and lst0[-1] != lst1[-1]:
                blocker.x_in_grids -= 1
        if blocker.type == 6:
            if blocker.x_in_grids < 0 and len(set0) < len(set1) and lst0[0] != lst1[0]:
                blocker.x_in_grids += 1
            if blocker.x_in_grids > 7 and len(set0) < len(set1) and lst0[-1] != lst1[-1]:
                blocker.x_in_grids -= 1

        # 变形后的位置没有方块才可以变形。
        if blocker.x_in_grids < 0 and lst0[0] == lst1[0]:
            blocker.x_in_grids = old_x
        if blocker.x_in_grids > 6 and lst0[-1] == lst1[-1]:
            blocker.x_in_grids = old_x
        lst2 = []
        for row in range(4):
            for column in range(4):
                if row * 4 + column in blocks[blocker.type][
                        (blocker.rotation + 1) % len(blocks[blocker.type])]:
                    lst2.append(self.grids[blocker.y_in_grids + row][blocker.x_in_grids + column])
        if sum(lst2) == 0:
            blocker.rotate()

    def break_lines(self):
        lines = 0
        for row in range(24):
            if 0 not in self.grids[row]:
                lines += 1
                for new_row in range(row, 4, -1):
                    for column in range(10):
                        self.grids[new_row][column] = self.grids[new_row -1][column]
        self.score += lines ** 2
        if self.score > 20:
            self.level = 2
        if self.score > 60:
            self.level = 3
        if self.score > 100:
            self.level = 4
        if self.score > 150:
            self.level = 5
        if self.score > 250:
            self.level = 6
        if self.score > 400:
            self.level = 7
        if self.score > 600:
            self.level = 8
        if self.score > 800:
            self.level = 9
        if self.score > 1000:
            self.level = 10

    def go_space(self, blocker):
        while not self.go_crash(blocker):
            blocker.y_in_grids += 1
        blocker.y_in_grids -= 1
        self.freeze(blocker)




SCREEN = (600, 650)
BLACK = (0, 0, 0)
WHILE = (255, 255, 255)
GRAY = (194, 194, 194)
game = Tetris()
pygame.init()
screen = pygame.display.set_mode(SCREEN)
pygame.display.set_caption('俄罗斯方块')
pygame.key.stop_text_input()
fps = 60
clock = pygame.time.Clock()
game_over = False
counter = 0
pressing_down = False
# font_name = os.path.join('font.ttf')
# print(pygame.font.get_fonts ())  # 查询系统支持的字体
while not game_over:
    clock.tick(fps)
    counter += 1
    if counter == 10000:
        counter = 0
    if game.block is None:
        if game.game_state == 'running':
            game.block = [Block(3, 0), Block(3, 0)]

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            game_over = True
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_UP:
                if not game.pause:
                    game.rotate(game.block[0])
            if event.key == pygame.K_LEFT:
                if not game.pause:
                    game.go_side(game.block[0], -1)
            if event.key == pygame.K_RIGHT:
                if not game.pause:
                    game.go_side(game.block[0], 1)
            if event.key == pygame.K_DOWN:
                if not game.pause:
                    pressing_down = True
            if event.key == pygame.K_SPACE:
                if not game.pause:
                    if game.game_state == 'running':
                        game.go_space(game.block[0])
            if event.key == pygame.K_ESCAPE:
                if game.game_state == 'game_over':
                    game.__init__()
            if event.key == pygame.K_1:
                game.pause = True
            if event.key == pygame.K_2:
                game.pause = False
        if event.type == pygame.KEYUP:
            if event.key == pygame.K_DOWN:
                pressing_down = False
    screen.fill(WHILE)

    if game.block is None:
        if game.game_state == 'running':
            game.block = [Block(3, 0), Block(3, 0)]

    # 画游戏屏
    for row in range(4, 24):
        for column in range(10):
            pygame.draw.rect(screen, GRAY,
                             [game.position_x + column * game.grid_size, game.position_y + row * game.grid_size,
                              game.grid_size, game.grid_size], 1)
    # 画预告屏
    for row in range(4):
        for column in range(4):
            pygame.draw.rect(screen, GRAY,
                             [50 + column * game.grid_size, 50 + row * game.grid_size,
                              game.grid_size, game.grid_size], 1)
    # 画碰撞后的方块
    for row in range(4, 24):
        for column in range(10):
            if game.grids[row][column] != 0:
                pygame.draw.rect(screen, colors[game.grids[row][column]],
                                 [game.position_x + column * game.grid_size + 1,
                                     game.position_y + row * game.grid_size + 1,
                                  game.grid_size - 2, game.grid_size - 2
                                 ])
    # 这段代码实现了预告屏的功能
    if game.game_state == 'running':
        if not game.interation:
            # 画下落中的方块
            for row in range(4):
                for column in range(4):
                    if row * 4 + column in game.block[0].block():
                        if game.block[0].y_in_grids + row >= 4:
                            pygame.draw.rect(screen, colors[game.block[0].color],
                                             [game.position_x + (game.block[0].x_in_grids + column) * game.grid_size + 1,
                                              game.position_y + (game.block[0].y_in_grids + row) * game.grid_size + 1,
                                              game.grid_size - 2, game.grid_size - 2])
            # 画预告的方块
            for row in range(4):
                for column in range(4):
                    if row * 4 + column in game.block[-1].block():
                        pygame.draw.rect(screen, colors[game.block[-1].color],
                                         [50 + column * game.grid_size + 1,
                                          50 + row * game.grid_size + 1,
                                          game.grid_size - 2, game.grid_size - 2])
            if counter % (fps // game.level) == 0 or pressing_down:
                if not game.pause:
                    game.go_down(game.block[0])
        else:
            game.block.pop(0)
            game.block.append(Block(3, 0))
            game.interation = False
    font_score = pygame.font.SysFont('Times', 20, True, True)
    next_block = font_score.render('Next-Block', True, BLACK)
    score_text = font_score.render(f'Score: {game.score}', True, BLACK)
    level_text = font_score.render(f'Level: {game.level}', True, BLACK)
    screen.blit(next_block, [60, 20])
    screen.blit(score_text, [50, 180])
    screen.blit(level_text, [50, 220])

    # font_info = pygame.font.Font(font_name, 15)
    font_info = pygame.font.SysFont('simsun', 15)
    lst = ['操作说明:', 'UP:旋转', 'DOWN:加速', 'LEFT:右移', 'RIGHT:左移', 'SPACE:下落', 'ESC:重新开始', '1:暂停', '2:继续']
    for i, info in enumerate(lst):
        info_text = font_info.render(f'{info}', True, BLACK)
        screen.blit(info_text, [50, 300 + 30 * i])

    if game.game_state == 'game_over':
        font_over = pygame.font.SysFont('Times', 50, True, True)
        game_over_text = font_over.render('Game-Over', True, BLACK)
        screen.blit(game_over_text, [280, 150])
        font_start = pygame.font.SysFont('Times', 25, True, True)
        restart_text = font_start.render('Press ESC To Restart Game', True, BLACK)
        screen.blit(restart_text, [255, 300])

    pygame.display.flip()

pygame.quit()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值