前言
想学习一下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()