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.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窗口中显示中文,可以按照以下步骤进行操作:
导入pygame和pygame.locals模块,以及sys模块。
初始化pygame,设置窗口大小和标题。
加载中文字体文件,可以使用pygame.font模块中的Font函数。
创建一个文本对象,使用render函数将中文渲染到文本对象中。
将文本对象渲染到窗口中,使用blit函数。
刷新窗口,使用flip函数。

4、 绘图(绘制直线和矩形)
如何画一条直线
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、精灵和精灵组
- 精灵组的内置方法:
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,先留着,等考完四级就改
效果呈现

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

被折叠的 条评论
为什么被折叠?



