Python俄罗斯方块游戏

该博客围绕Python游戏开发展开,总结了生成随机数、range函数的for循环、在pygame窗体显示中文字体、绘图、插图、运算符“//”、精灵和精灵组、按键响应等知识点,还提及满行消行下落的bug待解决。

teteis.py:

class Tetris:
    """俄罗斯方块游戏"""
    def __init__(self):
        """初始化游戏设置"""
        pygame.init()
        self.clock = pygame.time.Clock()
        self.setting = Setting()
        self.screen = pygame.display.set_mode((self.setting.screen_width, self.setting.screen_height))
        self.screen.fill((255, 255, 255))
        self.bg = Bg(self)
        # 创建精灵组存储俄罗斯方块,其中self.blocks:正在运动的;self.static_blocks:停止运动的;self.next_blocks:下一个的俄罗斯方块
        self.blocks = pygame.sprite.Group()
        self.static_blocks = pygame.sprite.Group()
        self.next_blocks = pygame.sprite.Group()
        self.create_blocks(self.blocks)
        self.create_blocks(self.next_blocks)
        # 按照行存储 self.static_blocks_row[某一行][某一行的全部数据],初值为0
        self.static_blocks_row = [[0 for j in range(self.setting.columns)] for i in range(self.setting.rows)]
        # 满行的行数列表
        self.full_rows = []
        # 窗口标题
        pygame.display.set_caption("俄罗斯方块")

    def draw_last_blocks(self):
        """绘制下一个即将出现的俄罗斯方块"""
        for block in self.next_blocks:
            self.screen.blit(block.image, (block.rect.x + self.setting.game_area_width - 100, block.rect.y + 350))

    def create_blocks(self, blocks):
        """创建一个俄罗斯方块"""
        color_num = random.randint(1, len(self.setting.BLOCK_color))
        shape_num = random.randint(1, len(self.setting.BLOCK_SHAPE))
        # 遍历小方块的组合,根据里面存储的数据创建对应位置的小方块
        temp_list = self.setting.BLOCK_SHAPE[shape_num]
        for temp in temp_list:
            current_x = temp[0]* self.setting.grid_dx+180
            current_y = temp[1]*self.setting.grid_dx+0
            new_block = Block(self, self.setting.BLOCK_color[color_num], current_x, current_y,color_num, shape_num)
            blocks.add(new_block)

    def full_row(self):
        """判断是否满行"""
        self.full_rows.clear()
        for i in range(self.setting.rows):
            flag = 0
            for j in range(self.setting.columns):
                if self.static_blocks_row[i][j] == 0:
                    flag = 1
                    break
            if flag == 0:
                self.full_rows.append(i)

    def remove_row(self):
        """消除已经满行的"""
        self.full_row()
        for row in self.full_rows:
            for block in self.static_blocks:
                if block.rect.y//30 == row:
                    self.static_blocks.remove(block)
                    self.setting.score += 5
            self.down_rows(row)
            for j in range(self.setting.columns):
                self.static_blocks_row[row][j] = 0

    def down_rows(self, n):
        """消除已经满行的第n行之后,将第n行上边的方块向下移动一行"""
        m = n - 1
        while m >= 0:
            for i in range(0, self.setting.columns):
                if self.static_blocks_row[m][i] == 1:
                    self.static_blocks_row[m + 1][i] = 1
                    self.static_blocks_row[m][i] = 0
            for e in range(len(self.static_blocks.sprites())):
                if (self.static_blocks.sprites()[e].rect.y//30) == m:
                    self.static_blocks.sprites()[e].rect.y += 30
                    self.static_blocks_row[self.static_blocks.sprites()[e].rect.y//30][self.static_blocks.sprites()[e].rect.x//30] = 1
            m = m - 1

    def rotate(self):
        """旋转俄罗斯方块"""
        new_blocks = pygame.sprite.Group()
        shape_num,color_num,block_x,block_y = 0,0,0,0
        for block in self.blocks:
            color_num = block.color_num
            shape_num = self.setting.BLOCK_ROTATE[block.shape_num]
            block_x = block.rect.x
            block_y = block.rect.y
            break
        temp_list = self.setting.BLOCK_SHAPE[shape_num]
        for temp in temp_list:
            current_x = temp[0] * self.setting.grid_dx + block_x
            current_y = temp[1] * self.setting.grid_dx + block_y
            new_block = Block(self, self.setting.BLOCK_color[color_num], current_x,current_y, color_num, shape_num)
            new_blocks.add(new_block)
        self.blocks.empty()
        for block1 in new_blocks:
            self.blocks.add(block1)
        new_blocks.empty()

    def run_game(self):
        """开始游戏的循环"""
        # 一个游戏就做三件事:处理事件;更新游戏状态;绘制游戏到屏幕上
        while True:
            self.game_update()
            self.draw_last_blocks()
            self.static_blocks.draw(self.screen)
            self.bg.draw_bg()
            self.blocks.draw(self.screen)
            self.clock.tick(60)
            pygame.display.update()

    def game_update(self):
        self.setting.move_right = False
        self.setting.move_left = False
        self.setting.move_increase = False
        self.check_border()
        self.check_game_over()
        self.screen.fill((255, 255, 255))
        self.check_events()
        if self.setting.state_game and self.setting.game_not_stop and not self.setting.game_over:
            self.blocks.update()

    def check_events(self):
        """鼠标事件、键盘事件"""
        keys = pygame.key.get_pressed()
        if keys[pygame.K_DOWN]:
            self.setting.move_increase = True
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_SPACE:
                    self.setting.state_game = True
            if self.setting.state_game and not self.setting.game_over:
                # 开始游戏时
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_RIGHT and not self.setting.touch_right:
                        self.setting.move_right = True
                    if event.key == pygame.K_LEFT and not self.setting.touch_left:
                        self.setting.move_left = True
                    if event.key == pygame.K_UP and not self.setting.touch_bottom:
                        self.rotate()
                    if event.key == pygame.K_1:
                        if self.setting.game_not_stop:
                            self.setting.game_not_stop = False
                        else:
                            self.setting.game_not_stop = True

    def collision(self):
        """碰撞检测"""
        if self.static_blocks:
            for block in self.blocks:
                block_hit_list = pygame.sprite.spritecollide(block, self.static_blocks, False)
                if block_hit_list:
                    block.update()
                    # 如果发生了碰撞
                    return True
            # 未发生碰撞
            return False

    def check_game_over(self):
        for block in self.blocks:
            if block.rect.y == 0 and self.collision():
                self.setting.game_over = True
            break

    def check_border(self):
        """判断小方块是否触碰到窗口的边缘,如果小方块到达底部,创建新的方块"""
        max_x,min_x,max_y = 0,900,0
        for block in self.blocks:
            # 遍历当前正在移动的俄罗斯方块组合blocks,找出来边界值max_x,min_x,max_y
            if block.rect.x >= max_x:
                max_x = block.rect.x
            if block.rect.x <= min_x:
                min_x = block.rect.x
            if block.rect.y >= max_y:
                max_y = block.rect.y
        # 判断是否可以向下移动
        if max_y >= self.setting.screen_height - self.setting.grid_dx or self.collision():
            if not self.setting.game_over:
                self.setting.touch_bottom = True
                self.create_new_tetris()
        else:
            self.setting.touch_bottom = False
        # 判断是否可以向左或者向右移动
        if not self.setting.touch_bottom:
            if max_x >= self.setting.game_area_width - self.setting.grid_dx:
                self.setting.touch_right = True
            else:
                self.setting.touch_right =False
            if min_x <= 0:

                self.setting.touch_left = True
            else:
                self.setting.touch_left = False

    def create_new_tetris(self):
        for block in self.blocks:
            block.rect.y = (block.rect.y//30)*30
            self.static_blocks.add(block)
            self.static_blocks_row[block.rect.y//30][block.rect.x//30] = 1
            # 在添加后判断是否满行,如果有,消除
            self.remove_row()
        self.blocks.empty()
        for block1 in self.next_blocks:
            self.blocks.add(block1)
            self.next_blocks.remove(block1)
        self.create_blocks(self.next_blocks)


if __name__ == '__main__':
    game = Tetris()
    game.run_game()

blackground.py:

import pygame


class Bg:
    def __init__(self, game):
        self.setting = game.setting
        self.screen = game.screen
        self.white = (255, 255, 255)
        self.black = (0, 0, 0)
        self.red = (255, 0, 0)

    def draw_grids(self):
        # 绘制游戏区与提示区的分界线
        pygame.draw.line(self.screen, self.setting.grid_line_color, (self.setting.game_area_width, 0),
                         (self.setting.game_area_width, self.setting.screen_height), self.setting.grid_line_width)
        # 绘制方格线
        for i in range(self.setting.rows):
            # 绘制每行
            y = self.setting.grid_dx - self.setting.line_width
            y = y + i * self.setting.grid_dx
            pygame.draw.line(self.screen, self.setting.line_color, (0, y),
                             (self.setting.game_area_width, y),
                             self.setting.line_width)
        for j in range(self.setting.columns):
            # 绘制每列
            x = self.setting.grid_dx - self.setting.line_width
            x = x+j*self.setting.grid_dx
            pygame.draw.line(self.screen,self.setting.line_color,(x,0),
                             (x,self.setting.screen_height),
                             self.setting.line_width)

    def draw_panel(self):
        """绘制提示区"""
        font = pygame.font.Font("simkai.ttf", 30)
        text = font.render("得分:" + str(self.setting.score), True, self.red)
        text1 = font.render("按1可暂停", True, self.black)
        text2 = font.render("下一个", True, self.black)
        self.screen.blit(text, (self.setting.game_area_width + 50, 20))
        self.screen.blit(text1, (self.setting.game_area_width+50, self.setting.screen_height-50))
        self.screen.blit(text2, (self.setting.game_area_width+50, 200))
        pygame.draw.rect(self.screen, self.red, (self.setting.game_area_width+50, 250, 200, 200), 2)

        font = pygame.font.Font("simkai.ttf", 60)
        s = ""
        if not self.setting.state_game:
            if not self.setting.game_over:
                s= "空格开始游戏"
            if self.setting.game_over:
                s = "游戏结束"
        elif self.setting.state_game and not self.setting.game_not_stop:
            s = "游戏暂停"
        self.screen.blit(font.render(s, True, self.black), (self.setting.game_area_width * 0.2, self.setting.screen_height * 0.5))

    def draw_bg(self):
        self.draw_panel()
        self.draw_grids()

block.py:

import pygame.image
from pygame.sprite import Sprite


class Block(Sprite):
    """组成俄罗斯方块的一个小方块类"""
    def __init__(self, game, picture, x, y, color_num, shape_num):
        super().__init__()
        self.setting = game.setting
        self.image = pygame.image.load(picture)
        self.image = pygame.transform.scale(self.image, (self.setting.grid_dx, self.setting.grid_dx))
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y
        self.color_num = color_num
        self.shape_num = shape_num

    def update(self):
        if self.setting.move_right:
            self.rect.x += self.setting.grid_dx
        if self.setting.move_left:
            self.rect.x -= self.setting.grid_dx
        if not self.setting.touch_bottom:
            if self.setting.move_increase:
                speed = 5
            else:
                speed = self.setting.init_speed
            self.rect.y += speed

setting.py:

class Setting:
    """设置,游戏中的一些属性值"""
    def __init__(self):
        """初始化"""
        # 游戏区域的总行数
        self.rows = 20
        # 游戏区域的总列数
        self.columns = 20
        # 游戏开始、游戏结束、游戏中止
        self.state_game = False
        self.game_over = False
        self.game_not_stop = True
        # 游戏的得分
        self.score = 0
        # 游戏区域网格的属性设置
        self.grid_dx = 30
        # 关于屏幕窗口、提示区域、游戏区域的大小
        self.game_area_width = self.columns*self.grid_dx
        self.other_area_width = 300
        self.screen_width = self.game_area_width+self.other_area_width
        self.screen_height = self.rows * self.grid_dx
        # 关于线条的设置
        self.grid_line_color = (255,0,0)
        self.grid_line_width = 5
        self.line_color = (255, 0, 0)
        self.line_width = 1
        # 俄罗斯方块下落过程中的属性控制
        self.init_speed = 1
        self.move_increase = False
        self.move_right = False
        self.move_left = False
        self.move_up = False
        self.move_down = False
        self.touch_right = False
        self.touch_left = False
        self.touch_bottom = False
        # 小方块图片属性
        self.BLUE = "picture/blue.png"
        self.GREEN = "picture/green.png"
        self.L_BLUE = "picture/L_blue.png"
        self.ORANGE = "picture/orange.png"
        self.PURPLE = "picture/purple.png"
        self.RED = "picture/red.png"
        self.YELLOW = "picture/yellow.png"
        self.BLOCK_color = {
            1: self.GREEN,
            2: self.L_BLUE,
            3: self.ORANGE,
            4: self.PURPLE,
            5: self.RED,
            6: self.YELLOW,
            7: self.BLUE,
        }
        # 一个小方块组合
        self.shape1 = [(0, 0)]
        # 两个小方块组合
        self.shape2 = [(0, 0), (1, 0)]
        self.shape3 = [(0, 0), (0, 1)]
        # 三个小方块组合
        self.shape4 = [(0, 0), (1, 0), (-1, 0)]
        self.shape5 = [(0, 0), (1, 0), (1, -1)]
        self.shape6 = [(0, 0), (1, 0), (0, -1)]
        self.shape7 = [(0, 0), (0, -1), (0, 1)]
        self.shape8 = [(0,0),(1,0),(0,1)]
        self.shape9= [(0,0),(1,0),(1,1)]
        # 四个小方块组合
        self.shape10 = [(0, 0), (0, -1), (1, 0), (1, -1)]
        self.shape11 = [(0, 0), (1, 0), (2, 0), (0, -1)]
        self.shape12 = [(0, 0), (1, 0), (1, -1), (2, 0)]
        self.shape13 = [(0, 0), (1, 0), (2, 0), (2, -1)]
        self.shape14 = [(0, 0), (0, 1), (1, 0), (2, 0)]
        self.shape15 = [(0, 0), (1, 0), (2, 0), (1, 1)]
        self.shape16 = [(0, 0), (1, 0), (2, 0), (2, 1)]
        self.shape17 = [(0, 0), (1, 0), (1, -1), (1, -2)]
        self.shape18 = [(0, 0), (0, -1), (0, -2), (-1, -1)]
        self.shape19 = [(0, 0), (0, -1), (0, -2), (-1, -2)]
        self.shape20 = [(0, 0), (1, 0), (0, -1), (0, -2)]
        self.shape21 = [(0, 0), (0, -1), (0, -2), (1, -1)]
        self.shape22 = [(0, 0), (0, -1), (0, -2), (1, -2)]
        self.BLOCK_SHAPE = {
            1: self.shape1,
            2: self.shape2,
            3: self.shape3,
            4: self.shape4,
            5: self.shape5,
            6: self.shape6,
            7: self.shape7,
            8: self.shape8,
            9: self.shape9,
            10: self.shape10,
            11: self.shape11,
            12: self.shape12,
            13: self.shape13,
            14: self.shape14,
            15: self.shape15,
            16: self.shape16,
            17: self.shape17,
            18: self.shape18,
            19: self.shape19,
            20: self.shape20,
            21: self.shape21,
            22: self.shape22,
        }
        self.BLOCK_ROTATE= {
            1: 1,
            2: 3,
            3: 2,
            4: 7,
            5: 9,
            6: 5,
            7: 4,
            8: 6,
            9: 8,
            10: 10,
            11: 17,
            12: 18,
            13: 19,
            14: 20,
            15: 21,
            16: 22,
            17: 16,
            18: 15,
            19: 14,
            20: 13,
            21: 12,
            22: 11,
        }


if __name__ == "__main__":
    s = Setting()

总结知识点:

1、生成随机数

 ①生成指定范围内的随机整数:

import random

result=random.randint(a,b)   返回一个在指定范围内的随机整数,包括a和b

a和b必须是整数,两个参数必须存在

randint(start,stop)等价于randrange(start,stop+1)

 ②生成指定范围内的随机浮点数:

import random

result = random.uniform(a,b)   

 ③ 从序列(如列表、元组、字符串)中随机选择一个元素

import random

numbers = [1,2,3,4,5]

random_number = random.choice(number)

④ 从总体(如列表、元组、字符串)中随机选择多个不重复的元素

import random

numbers = [1,2,3,4,5]

random_numbers = random.sample(numbers,3)    # 从列表中随机选择3歌不重复的元素

⑤ 生成一个不连续范围的随机数

import random

# 生成一个大于40或者小于20的正整数

random_number = random.choice([random.randint(1,19),random.randint(41,100)]

⑥ 生成0到1之间的随机浮点数,包括0但不包括1

import random

random_number = random.random()

2、range函数的for循环

range(start,stop,step)

start作为开始值,开始值作为开始数,可省略(省略默认从0开始)

stop作为结束值, 结束数的下标,下标从0开始,不可省略

step作为步进值,输出两个值之间相差多少,可省略(省略默认为1)

# 输出1,2,3,4,5(包左不包右)
for i in  range(1, 6):
    print(i)

生成随机整数序列

import random

# 生成1到100之间的10歌随机整数

random_numbers= random.sample(range(1,101),10)

类似于list、tuple、str 等类型的数据可以使用for …… in…… 的循环遍历语法从其中依次拿到数据并进行使用,我们把这个过程称为遍历,也称迭代。python中可迭代的对象有list(列表)、tuple(元组)、dirt(字典)、str(字符串)set等。在该过程中,无法通过修改循环变量来改变对应的值

3、在pygame窗体中显示中文字体

pygame.font文档

pygame.font.SysFont()

从系统字体库中创建一个Font对象

SysFont(name, size, bold= False, italic=False) ->Font

从系统字体库中加载并返回一个新的字体对象

该字体将会匹配bold(加粗)和italic(斜体)参数的要求

如果找不到一个合适的系统字体,该函数会回退并加载默认的pygame字体

尝试搜索的name参数可以是一个用逗号隔开的列表

Class: pygame.font.Font

从一个字体文件创建一个Font对象

Font(filename, size) ->Font

Font(object,size) - >Font

pygame.font.Font.render()   ------在一个新的Surface对象上绘制文本

pygame.font.Font.size()     --------确定多大的空间用于表示文本

.......

在pygame窗口中显示中文,可以按照以下步骤进行操作:

  1. 导入pygame和pygame.locals模块,以及sys模块。

  2. 初始化pygame,设置窗口大小和标题。

  3. 加载中文字体文件,可以使用pygame.font模块中的Font函数。

  4. 创建一个文本对象,使用render函数将中文渲染到文本对象中。

  5. 将文本对象渲染到窗口中,使用blit函数。

  6. 刷新窗口,使用flip函数。

 

4、 绘图(绘制直线和矩形)

pygame中draw绘图详解

如何画一条直线

pygame.draw.line()

线条参数:操作对象,颜色(RGB),起始位置,结束位置,线条宽度pygame.draw.line(screen, (255, 0, 0), (0, 0), (300, 300), width=5)

如何画一个矩形

pygame.draw.rect(surface, color, rect, width)

surface:指主游戏窗口,无特殊情况,一般都会绘制在主屏幕上; color:该参数用于该图形着色; rect:绘制图形的位置和尺寸大小;
width:可选参数,指定边框的宽度 (默认为 0,表示填充该矩形区域),width存在就是矩形框,不存在就是一个实心的矩形。其中需要注意,当width<0,不会绘制任何图形

5、插图(自定义图片的大小)

pygame.transform.scale()函数来设置图片的大小。

该函数需要两个参数,第一个参数是要缩放的图片,第二个参数是缩放后的大小(像素)。

screen.blit()函数将图片贴到窗口上

pygame.display.flip()函数刷新窗口

6、运算符"//"

// 运算符:表示整数除法,其结果是两个数相除后向下取整(向小的数字偏向)后的结果。如果有小数位,则会将其舍去

例子:

>>> 9//2

4

>>> -9//2

-5

7、精灵和精灵组

  1. 精灵组的内置方法:
  • add(*sprites): 将一个或多个精灵添加到组中。
  • remove(*sprites): 将一个或多个精灵从组中移除。
  • sprites(): 返回组中所有精灵的列表。
  • update(*args): 更新组中所有精灵的状态。

碰撞检测:

精灵与精灵组之间的矩形碰撞检测

collide_list = pygame.sprite.spritecollide(sprite,group,False)
参数: 精灵,精灵组,第三个参数如果为True,则碰撞检测后,组中所有碰撞的精灵被删除

返回值:精灵组中被碰撞的精灵 

精灵组和精灵组之间的矩形检测

collide_list = pygame.sprite.groupcollide(group1,group2,True,False)

参数: 精灵组1,精灵组2, 发生碰撞时是否删除精灵组1的精灵,发生碰撞时是否删除精灵组1的精灵

返回值:返回字典类型,第一个组中的每一个精灵都会添加到字典中,第二个组中与之碰撞的精灵会添加到字典相应的条目中

精灵与精灵之间的矩形检测

pygame.sprite.collide_rect(first,second) 

参数:精灵1,精灵2

返回值:返回布尔值

修改精灵组中某一个精灵的属性值

self.group.sprites()是一个方法,用于返回精灵组中所有精灵的列表。

这个列表是精灵组内部存储精灵的列表的一个副本,因此对这个列表的修改不会影响精灵组本身。

但是,如果你修改了精灵组中任何一个精灵的属性,那么这个属性的改变将会反映在精灵组中。

for e in (len(self.big_blocks.sprites())-1):
    if (self.big_blocks.sprites()[e].rect.y //30) == m:
        self.big_blocks.sprites()[e].rect.y = 570

如果按照下面这种方式来写,则对应精灵的属性并不会发生改变,原因在于for循环中不能通过改变循环遍历的值来改变对象的值

for block in self.big_blocks.sprites():
    if block.rect.y//30 ==m:
        block.rect.y = 570

8、pygame按键持续按下持续响应

使用pygame.get_pressed()函数检测空格键是否被按下

while True:
    # 处理事件
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
    # 检测按键是否按下
    keys = pygame.key.get_pressed()
    if keys[pygame.K_SPACE]:
        # 响应事件
        counter += 1

9、满行后消行后上几行下落问题

当即将满行时,最后进入的俄罗斯方块会出现无法正常消除下落的情况,有bug,先留着,等考完四级就改

效果呈现

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值