使用pygame实现简单的俄罗斯方块,实现了强降、预降位置显示等功能。
俄罗斯方块1.0演示
import os
import pygame,sys,random,copy,time
pygame.init()
pygame.mixer.init()
pygame.display.set_caption('俄罗斯方块1.0')
'''操作设置'''
left = pygame.K_LEFT
right = pygame.K_RIGHT
down = pygame.K_DOWN
spi_r = pygame.K_UP #右旋-上键
spi_l = pygame.K_g #左旋-G
change = pygame.K_d #保留-D
plunge = pygame.K_f #强降-F
again = pygame.K_2 #重新游戏
leve = 1 #难度等级,数字越大,下落越快
'''定义屏幕大小'''
width = 500
height = 700
screen = pygame.display.set_mode((500,700))#屏幕大小
'''基础方块绘制'''
size=30#方块规格
sore = 10#分数单位
sores = [0]#记录总分
#基础方块的绘制
face = pygame.Surface((size,size),flags=pygame.HWSURFACE)
# face.fill("white")
pygame.draw.rect(face, (0, 0, 0), [0,0,size,size],1)
# pygame.draw.circle(face, (255, 0, 0), (15,15), 15, width=1)
#基础方块的投影绘制--增加透明度
face1 = pygame.Surface((size,size),flags=pygame.HWSURFACE)
# face1.fill((255,255,255,0))
pygame.draw.rect(face1, (0, 0, 0,0), [0,0,size,size],1)
# pygame.draw.circle(face1, (255, 0, 0,0), (15,15), 15, width=1)
face1.set_alpha(100)#设置透明度
#字体设置
f = pygame.font.SysFont(['方正粗黑宋简体'],30)#分数显示等需要用的字体
f1 = pygame.font.SysFont(['方正粗黑宋简体'],20)
f2 = pygame.font.SysFont(['方正粗黑宋简体'],15)
'''地图参数设置'''
origin=(5,5) #原点位置,边框的宽度
set = [4*size+origin[0],origin[1]+0] #初始生成方块位置,该坐标全局控制当前形状的位置
c_list=(2,4,6,7,14,16,19)#分别为7种大类型的方块,见方块类型函数
ch1 = [ c_list[random.randint(0,6)] for i in range(4)] #初始方块产生随机函数
ch1.append(0)#ch1为一个有装4个值的列表,前三个为即将出现的三个形状的标号,第四个为保留的形状的标号,开始时没有,定为0
filled = []#落地方块储存列表,将一落地的每一个形状的信息存入
n=[0]#控制是否能更换当前手中方块
control = [1]#控制游戏结束,0为结束
'''方块类型函数'''
#每个形状表示为四个坐标,取其中一个方块为原点算出其他方块坐标
#c为方块类型标号,a为形状的坐标。
# 该函数最终输出一个列表,其中包含6个坐标,即为一个形状(包含四个基础方块)的四个方块坐标和四个方块中最小的x,y和最大的x,y,方便之后的碰撞判断
#还要输出形状标号c(方便进行不同方块的不同颜色绘制)
def forms(c,a):
#长条
if c == 1:
x0 = 0
y0 = 0
x1 = 0
y1 = -1
x2 = 0
y2 = 2
x3 = 0
y3 = 1
if c == 2:#横条
x0 = 0
y0 = 0
x1 = -1
y1 = 0
x2 = 1
y2 = 0
x3 = 2
y3 = 0
# S
if c==3:
x0 = 0
y0 = 0
x1 = 0
y1 = 1
x2 = -1
y2 = 0
x3 = -1
y3 = -1
if c == 4:
x0 = 0
y0 = 0
x1 = -1
y1 = 0
x2 = 0
y2 = -1
x3 = 1
y3 = -1
#Z
if c==5:
x0 = 0
y0 = 0
x1 = 1
y1 = 0
x2 = 0
y2 = 1
x3 = 1
y3 = -1
if c==6:
x0 = 0
y0 = 0
x1 = -1
y1 = 0
x2 = 0
y2 = 1
x3 = 1
y3 = 1
#T
if c==7:
x0 = 0
y0 = 0
x1 = 1
y1 = 0
x2 = -1
y2 = 0
x3 = 0
y3 = -1
if c==8:
x0 = 0
y0 = 0
x1 = 0
y1 = -1
x2 = 0
y2 = 1
x3 = 1
y3 = 0
if c==9:
x0 = 0
y0 = 0
x1 = -1
y1 = 0
x2 = 0
y2 = 1
x3 = 1
y3 = 0
if c==10:
x0 = 0
y0 = 0
x1 = 0
y1 = 1
x2 = 0
y2 = -1
x3 = -1
y3 = 0
#L
if c==11:
x0 = 0
y0 = 0
x1 = 0
y1 = -1
x2 = 0
y2 = 1
x3 = 1
y3 = 1
if c==12:
x0 = 0
y0 = 0
x1 = -1
y1 = 0
x2 = 1
y2 = 0
x3 = -1
y3 = 1
if c==13:
x0 = 0
y0 = 0
x1 = 0
y1 = -1
x2 = 0
y2 = 1
x3 = -1
y3 = -1
if c==14:
x0 = 0
y0 = 0
x1 = -1
y1 = 0
x2 = 1
y2 = 0
x3 = 1
y3 = -1
#J
if c==15:
x0 = 0
y0 = 0
x1 = 0
y1 = -1
x2 = 0
y2 = 1
x3 = -1
y3 = 1
if c==16:
x0 = 0
y0 = 0
x1 = -1
y1 = -1
x2 = -1
y2 = 0
x3 = 1
y3 = 0
if c==17:
x0 = 0
y0 = 0
x1 = 0
y1 = 1
x2 = 0
y2 = -1
x3 = 1
y3 = -1
if c==18:
x0 = 0
y0 = 0
x1 = -1
y1 = 0
x2 = 1
y2 = 0
x3 = 1
y3 = 1
# 田
if c == 19:
x0 = 0
y0 = 0
x1 = 0
y1 = 1
x2 = -1
y2 = 0
x3 = -1
y3 = 1
mx = max(x0,x1,x2,x3)
my = max(y0,y1,y2,y3)
mx1 = min(x0, x1, x2,x3)
my1 = min(y0, y1, y2,y3)
Z=[[x0*size+a[0],y0*size+a[1]],[x1*size+a[0],y1*size+a[1]],[x2*size+a[0],y2*size+a[1]],[x3*size+a[0],y3*size+a[1]],[mx1*size+a[0],my1*size+a[1]],[mx*size+a[0],my*size+a[1]],c]
return Z
'''屏幕更新函数'''
"""
屏幕动态更新的核心函数
实时更新形状位置,分数,预览形状等都在该函数完成
大概思路:
1每一次都使用新的空白页面(粉色覆盖,绘制边框)覆盖之前的内容,再
2再绘制新的实时内容
a、根据filled列表中储存的形状坐标信息绘制已经降落的形状
b、根据ch1列表中储存的实时形状(正在下落的形状,预降位置,预览的形状)
c、根据sores列表记录的分数绘制新的分数
"""
def update():
# 更新前进行碰撞判断
isclear(filled)
# 游戏框绘制
screen.fill("pink")
pygame.draw.line(screen, (0, 0, 250), (0, 0), (size * 10 + 10, 0), 10)
pygame.draw.line(screen, (0, 0, 250), (0, 0), (0, size * 20 + 10), 10)
pygame.draw.line(screen, (0, 0, 250), (size * 10+10 , 0), (size * 10 +10, size * 20+15 ), 10)
pygame.draw.line(screen, (0, 0, 250), (0, 10+size * 20), (size * 10 + 10, size * 20+10 ), 10)
pygame.draw.line(screen, (0, 0, 250), (10*size+origin[0]+10,100+size), (width,100+size), 5)
pygame.draw.line(screen, (0, 0, 250), (10 * size + origin[0] + 10, 100 + 5*size), (width, 100 + 5*size), 5)
pygame.draw.line(screen, (0, 0, 250), (10 * size + origin[0] + 10, 100 + 9 * size), (width, 100 + 9 * size), 5)
pygame.draw.line(screen, (0, 0, 100), (10 * size + origin[0] + 10, 100 + 13 * size), (width, 100 + 13 * size), 5)
pygame.draw.line(screen, (0, 0, 100), (10 * size + origin[0] + 10, 100 + 17 * size), (width, 100 + 17 * size), 5)
# 操作解释绘制
sor = f2.render("操作介绍:", True, (0, 100, 10))
screen.blit(sor, (20, 610))
sor = f1.render("左:" + pygame.key.name(left), True, (0, 0, 0))
screen.blit(sor, (50, 630))
sor = f1.render("右:" + pygame.key.name(right), True, (0, 0, 0))
screen.blit(sor, (150, 630))
sor = f1.render("下:" + pygame.key.name(down), True, (0, 0, 0))
screen.blit(sor, (250, 630))
sor = f1.render("右旋:" + pygame.key.name(spi_r), True, (0, 0, 0))
screen.blit(sor, (50, 660))
sor = f1.render("左旋:" + pygame.key.name(spi_l), True, (0, 0, 0))
screen.blit(sor, (150, 660))
sor = f1.render("强降:" + pygame.key.name(plunge), True, (0, 0, 0))
screen.blit(sor, (250, 660))
sor = f1.render("保留:" + pygame.key.name(change), True, (0, 0, 0))
screen.blit(sor, (360, 660))
# 方块颜色控制函数
'''
c表示方块的编号
face表示前面定义的基础方块
'''
def d_color(c, face=face):
a = -1
colors = ((0, 160, 233), (34, 172, 56), (230, 0, 18), (146, 7, 131), (235, 97, 0), (0, 71, 157), (243, 152, 0))
if c == 1 or c == 2 or c == -2:
a = 0
face.fill(colors[0])
if c == 3 or c == 4:
face.fill(colors[1])
a = 1
if c == 5 or c == 6:
a = 2
face.fill(colors[2])
if c >= 7 and c <= 10:
a = 3
face.fill(colors[3])
if c >= 11 and c <= 14:
a = 4
face.fill(colors[4])
if c >= 15 and c <= 18:
a = 5
face.fill(colors[5])
if c == 19:
a = 6
face.fill(colors[6])
pygame.draw.rect(face, (255,255,255), [0, 0, size, size], 1)
#当前正在下落的方块绘制
d_color(ch1[0], face=face)
for i in range(4):
screen.blit(face,forms(ch1[0],set)[i])
#计算预降位置,找到当前形状与已落地方块的距离
a = 19 * size - forms(ch1[0], set)[5][1] + origin[1]
for i in filled:
for j in range(4):
for k in range(4):
if i[j][0] == forms(ch1[0], set)[k][0]:#x相同的时候,找到符合要求的最y值,即当前形状与已落地方块的距离
if a > i[j][1] - forms(ch1[0], set)[k][1] - size:
a = i[j][1] - forms(ch1[0], set)[k][1] - size
# 方块预降位置绘制
d_color(ch1[0], face=face1)
for i in range(4):
screen.blit(face1,forms(ch1[0],(set[0],set[1]+a))[i])
#将出现的方块预览绘制,可以预览3个形状
d_color(ch1[1], face=face)
for i in range(4):
screen.blit(face,forms(ch1[1],(330+2*size,120+2*size))[i])
d_color(ch1[2], face=face)
for i in range(4):
screen.blit(face,forms(ch1[2],(330+2*size,120+6*size))[i])
d_color(ch1[3], face=face)
for i in range(4):
screen.blit(face,forms(ch1[3],(330+2*size,120+10*size))[i])
# 有保留的形状时绘制,其值为0时没有保留的形状
sor = f.render("retain", True, (0, 0, 0))
screen.blit(sor, (10*size+20, 90 + 13 * size))
if ch1[4]!=0:
d_color(ch1[4], face=face)
for i in range(4):
screen.blit(face,forms(ch1[4],(330+2*size,120+14*size))[i])
#已经落地方块绘制
for i in filled:
d_color(i[-1], face=face)
for j in range(4):
screen.blit(face, i[j])
#分数绘制
sor = f.render("sore:"+str(sores[0]),True,(0,0,0),(246,127,240))
screen.blit(sor, (335, 50))
#判断游戏是否结束
if iscrash(forms(ch1[0],set),filled)[0]==0 and forms(ch1[0],set)[5][1]==origin[1]:
control[0]=0
#绘制游戏结束界面并判断游戏是否继续
if control[0]==0:
sor1 = f1.render("game over!!", True, (255, 0, 0), (255, 255, 255))
screen.blit(sor1, (100, 300))
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == again:
filled.clear()
set[0]=origin[0]+4*size
set[1]=origin[1]+0
sores[0]=0
control[0]=1
pygame.display.flip()#更新屏幕
"""方块控制函数"""
'''
控制原理为改变set列表中的横纵坐标对形状的位置进行控制
每次移动完成要及时进行碰撞判断(无论是否有按键事件都要判断)
但该版本还未解决长按连续移动的问题
'''
def move():
#获取键盘事件
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key== left:#左移动
set[0] -= size
if iscrash(forms(ch1[0], set), filled)[2] == 2:
set[0] += size
if event.key == right:
set[0] += size
if iscrash(forms(ch1[0], set), filled)[1] == 1:
set[0] -= size
if event.key == down:#加速下降
set[1] += size
if event.key == spi_r:#右旋
m = ch1[0]
spin(0)
for i in filled:
for j in range(4):
for k in range(4):
if i[j][0]==forms(ch1[0],set)[k][0] and i[j][1]==forms(ch1[0],set)[k][1]:
ch1[0]=m
if event.key == spi_l:#左旋
m = ch1[0]
spin(1)
for i in filled:
for j in range(4):
for k in range(4):
if i[j][0]==forms(ch1[0],set)[k][0] and i[j][1]==forms(ch1[0],set)[k][1]:
ch1[0]=m
if event.key == change:#保留
if n[0]==0:
if ch1[4]==0:
if ch1[0] == 19:
ch1[4]=19
if ch1[0]>=1 and ch1[0]<=2:
ch1[4] = 2
if ch1[0]>=3 and ch1[0]<=4:
ch1[4] = 4
if ch1[0]>=5 and ch1[0]<=6:
ch1[4] = 6
if ch1[0]>=7 and ch1[0]<=10:
ch1[4] = 7
if ch1[0]>=11 and ch1[0]<=14:
ch1[4] = 14
if ch1[0]>=15 and ch1[0]<=18:
ch1[4] = 16
ch1[0] = ch1[1]
ch1[1]=ch1[2]
ch1[2]=ch1[3]
ch1[3]=c_list[random.randint(0,6)]
set[0] =4 * size + origin[0]
set[1]=origin[1] + 0
n[0]=1
else:
v=ch1[0]
ch1[0] = ch1[4]
if v == 19:
ch1[4]=19
if v>=1 and v<=2:
ch1[4] = 2
if v>=3 and v<=4:
ch1[4] = 4
if v>=5 and v<=6:
ch1[4] = 6
if v>=7 and v<=10:
ch1[4] = 7
if v>=11 and v<=14:
ch1[4] = 14
if v>=15 and v<=18:
ch1[4] = 16
set[0] = 4 * size + origin[0]
set[1] = origin[1] + 0
n[0] = 1
if event.key == plunge:#强降
#原理也是计算当前方块落地的最小距离
a=20*size-forms(ch1[0],set)[5][1]+origin[1]
for i in filled:
for j in range(4):
for k in range(4):
if i[j][0]==forms(ch1[0],set)[k][0]:
if a>i[j][1]-forms(ch1[0],set)[k][1]:
a=i[j][1]-forms(ch1[0],set)[k][1]
set[1] += a
# 判断碰撞情况
if forms(ch1[0], set)[4][0] < origin[0]:
set[0] += size
if forms(ch1[0], set)[5][0] > origin[0] + 9 * size:
set[0] -= size
# 落地判断
if forms(ch1[0], set)[5][1] > origin[1] + 19 * size:
set[1] -= size
filled.append(copy.deepcopy(forms(ch1[0], set)))
set[0] = 4 * size + origin[0]
set[1] = origin[1] + 0
ch1[0] = ch1[1]
ch1[1] = ch1[2]
ch1[2] = ch1[3]
ch1[3] = c_list[random.randint(0, 6)]
n[0] = 0
#方块间的碰撞判断
if iscrash(forms(ch1[0], set), filled)[0] == 0:
set[1] -= size
filled.append(copy.deepcopy(forms(ch1[0], set)))
set[0] = 4 * size + origin[0]
set[1] = origin[1] + 0
ch1[0] = ch1[1]
ch1[1] = ch1[2]
ch1[2] = ch1[3]
ch1[3] = c_list[random.randint(0, 6)]
n[0] = 0
"""方块间的碰撞判断函数"""
def iscrash(form, form1):
a, b, m = 10, 10, 10
# 上下碰撞
for i in form1:
for c in range(4):
for j in range(4):
if i[c][0] == form[j][0] and i[c][1] - form[j][1] == 0:
a = 0
# 碰right
for i in form1:
for c in range(4):
for j in range(4):
if i[c][0] - form[j][0] == 0 and i[c][1] - form[j][1] == 0:
b = 1
# 碰left
for i in form1:
for c in range(4):
for j in range(4):
if form[j][0] - i[c][0] == 0 and i[c][1] - form[j][1] == 0:
m = 2
return a, b, m
"""形状旋转命令函数"""
""""
输入旋转命令(在move函数中调用),
直接改变ch1[0]的值(正在下落的形状标号),及直接改变当前形状
"""
def spin(a):
if a==0:#右旋
if ch1[0]==1:
ch1[0]=2
return ch1[0]
if ch1[0]==2:
ch1[0]=1
return ch1[0]
if ch1[0]==3:
ch1[0]=4
return ch1[0]
if ch1[0]==4:
ch1[0]=3
return ch1[0]
if ch1[0]==5:
ch1[0]=6
return ch1[0]
if ch1[0]==6:
ch1[0]=5
return ch1[0]
if ch1[0]==7:
ch1[0]=8
return ch1[0]
if ch1[0] == 8:
ch1[0] = 9
return ch1[0]
if ch1[0] == 9:
ch1[0] = 10
return ch1[0]
if ch1[0] == 10:
ch1[0] = 7
return ch1[0]
if ch1[0] == 11:
ch1[0] = 12
return ch1[0]
if ch1[0] == 12:
ch1[0] = 13
return ch1[0]
if ch1[0] == 13:
ch1[0] = 14
return ch1[0]
if ch1[0] == 14:
ch1[0] = 11
return ch1[0]
if ch1[0] == 15:
ch1[0] = 16
return ch1[0]
if ch1[0] == 16:
ch1[0] = 17
return ch1[0]
if ch1[0] == 17:
ch1[0] = 18
return ch1[0]
if ch1[0] == 18:
ch1[0] = 15
return ch1[0]
if ch1[0] == 19:
ch1[0] = 19
return ch1[0]
if a==1:#左旋
if ch1[0]==1:
ch1[0]=2
return ch1[0]
if ch1[0]==2:
ch1[0]=1
return ch1[0]
if ch1[0]==3:
ch1[0]=4
return ch1[0]
if ch1[0]==4:
ch1[0]=3
return ch1[0]
if ch1[0]==5:
ch1[0]=6
return ch1[0]
if ch1[0]==6:
ch1[0]=5
return ch1[0]
if ch1[0]==7:
ch1[0]=10
return ch1[0]
if ch1[0] == 10:
ch1[0] = 9
return ch1[0]
if ch1[0] == 9:
ch1[0] = 8
return ch1[0]
if ch1[0] == 8:
ch1[0] = 7
return ch1[0]
if ch1[0] == 11:
ch1[0] = 14
return ch1[0]
if ch1[0] == 14:
ch1[0] = 13
return ch1[0]
if ch1[0] == 13:
ch1[0] = 12
return ch1[0]
if ch1[0] == 12:
ch1[0] = 11
return ch1[0]
if ch1[0] == 15:
ch1[0] = 18
return ch1[0]
if ch1[0] == 18:
ch1[0] = 17
return ch1[0]
if ch1[0] == 17:
ch1[0] = 16
return ch1[0]
if ch1[0] == 16:
ch1[0] = 15
return ch1[0]
if ch1[0] == 19:
ch1[0] = 19
return ch1[0]
'''#满行判断'''
#通过及算每一行的横坐标之和来进行满行判断
def isclear(form1):
sor=0
for j in range(20):
sum = 0#保存一行的横坐标和
c = j * size+origin[1]
for i in form1:
for k in range(4):
if i[k][1] == c:
sum+=i[k][0]
if sum >= 45*size + origin[0]*10:#判断是否满行
sor+=sore
for i in form1:
for k in range(4):
if i[k][1]==c:
i[k][1]=size*30#满行后将满行的y值赋值为size*30,在屏幕上就看不见了
if i[k][1]= leve:#每隔leve下降一格
t = time.time()
set[1] += size
break
move()
update()
if control[0]==0:break
if control[0] == 0:
#游戏结束界面
while 1:
update()
if control[0] == 1:break
if __name__=="__main__":
main()
还有很多不足之处,欢迎留言。