python游戏1——俄罗斯方块

1.创造方块:

1.1使用string和vector去创造对象

# creating assets
tetromino = []
object1 = "..X." \
           "..X." \
           "..X." \
           "..X."
object2 =  "..X." \
           ".XX." \
           ".X.." \
           "...."
object3 =  ".X.." \
           ".XX." \
           "..X." \
           "...."
object4 = "...." \
           ".XX." \
           ".XX." \
           "...."
object5 =  "..X." \
           ".XX." \
           "..X." \
           "...."
object6 = "...." \
           ".XX." \
           "..X." \
           "..X."
object7 = "...." \
           ".XX." \
           ".X.." \
           ".X.."

tetromino.append(object1)
tetromino.append(object2)
tetromino.append(object3)
tetromino.append(object4)
tetromino.append(object5)
tetromino.append(object6)
tetromino.append(object7)

2.旋转方块

在俄罗斯方块中,我们总是希望可以将方块旋转。

2.1使用一维向量来表示二维向量

比如有下图这样的二维向量:

可以很轻松的通过x和y两个参数来进行索引(a[x][y])。同样也可以使用一个参数来索引,只要我们给每个位置一个数字作为索引,正如上图所示。例如,我们可以通过i[10]来索引a[2][2]。这里的10可以表示为2*4+2. 即以左上角为坐标原点,y*width+x。

2.2旋转方块

现在旋转方块90°后:

如果我们还是想索引a[2][2]这个位置,如图易得现在需要用i[6]来索引。 

通过图片可以轻易的看出,每当横坐标增加1,一维索引的数字就减少4;每当纵坐标增加1,一维索引的数字就增加1。因此旋转90°,索引数字为12-4x+y。

同样的:

 旋转180°索引数字为15-4y-x。

 旋转270°,索引数字为3-y+4x。

def rotate(px, py, r):
    if r == 0:
        return py*4+px
    elif r == 1:
        return 12+py-4*px
    elif r == 2:
        return 15-4*py-px
    elif r == 3:
        return 3-py+4*px

3.界面

在本节中,我将构建一个12*18字段。我会用9来表示边缘。

# Field
fieldwidth = 12
fieldheight = 18

pField = [0 for i in range(fieldwidth*fieldheight)]
# print(len(pField))
for x in range(fieldwidth):
    for y in range(fieldheight):
        # print(y*fieldwidth+x)
        pField[y*fieldwidth+x] = 9 if (x == 0 or x == fieldwidth-1 or y == fieldheight-1) else 0

在这个项目中,我使用pygame来实现输出。

1 import pygame
2 # game window
3 pygame.init()
4 window_width = 480
5 window_height = 720
6 window_title = "Tetris"
7
8 screen = pygame.display.set_mode((window_width, window_height))
9 pygame.display.set_caption(window_title)
10
11 running = True
12 while running:
13 for event in pygame.event.get():
14 if event.type == pygame.QUIT:
15 running = False
16
17 # fill in black background
18 screen.fill((0, 0, 0))
19 # implement the game interface and blocks here
20 for x in range(fieldwidth):
21 for y in range(fieldheight):
22 # print(y*fieldwidth+x)
23 if pField[y*fieldwidth+x] == 9:
24 pygame.draw.rect(screen, (255,255,255), (x*40, y*40, 40,
40))
25 else:
26 ...
27 pygame.display.update()
28 pygame.quit()

输出结果如下图:

 4.游戏循环

游戏循环是游戏引擎中最重要的部分。这些都是一切发生的顺序。像俄罗斯方块这样的简单游戏并不是大规模的事件驱动应用程序。它包括一些元素,如时间、用户输入、游戏逻辑的日期,然后将其绘制在屏幕上。它一直这样做,直到游戏结束或用户退出为止。

4.1判断位置是否正确

此函数有四个参数:

nTetromino:它代表块的类型

nrotation:表示旋转角度

positionX:表示四阶块的水平坐标

positionY:表示四阶块的垂直坐标

# judge if the position of the piece fit the rule
def doesPieceFit(nTetromino, nrotation, positionX, positionY):
    for px in range(4):
        for py in range(4):
            # get the index
            pi = rotate(px, py, nrotation)
            # print(pi)
            # exit(0)
            #get index into field
            fi = int((positionY+py)*12+positionX+px)
            # print(fi)
            # exit(0)
            if (positionX+px >= 0) and (positionX+px <= 11):
                if (positionY+py >= 0) and (positionY+py <= 18):
                    if tetromino[nTetromino][pi] == 'X' and ((pField[fi] == 1) or (pField[fi] == 9)):
                        return False

    return True

4.2输入和相应的操作

        俄罗斯方块中有四种类型的输入:左移、右移、下移和旋转在程序中,我使用左、右、下和z键来对应四种不同的操作。

1 for event in pygame.event.get():
2 if event.type == pygame.QUIT:
3 gameover = True
4 elif event.type == pygame.KEYDOWN:
5 if event.key == pygame.K_LEFT:
6 bKey = 1
7 elif event.key == pygame.K_RIGHT:
8 bKey = 2
9 elif event.key == pygame.K_DOWN:
10 bKey = 3
11 elif event.key == pygame.K_z:
12 bKey = 4
13 # Game logic ======================================================
14 if bKey == 1 and doesPieceFit(nCurrentPiece, nCurrentRotation,
nCurrentX-1, nCurrentY):
15 nCurrentX = nCurrentX - 1
16 bKey = 0
17
18 if bKey == 2 and doesPieceFit(nCurrentPiece, nCurrentRotation,
nCurrentX+1, nCurrentY):
19 nCurrentX = nCurrentX + 1
20 bKey = 0
21
22 if bKey == 3 and doesPieceFit(nCurrentPiece, nCurrentRotation,
nCurrentX, nCurrentY+1):
23 nCurrentY = nCurrentY + 1
24 bKey = 0
25
26 if bKey == 4 and doesPieceFit(nCurrentPiece, (nCurrentRotation+1)%4,
nCurrentX, nCurrentY):
27 nCurrentRotation += 1
28 nCurrentRotation %= 4
29 bKey = 0

但是,如果使用上述方法,每轮只能输入一个按钮,所以我使用了一个列表来保存每轮的输入。

# input ===========================================================
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            gameover = True
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT:
                # 用户按下了左箭头键
                # print("左箭头键被按下")
                # bKey = 1
                bKey.append(1)
            elif event.key == pygame.K_RIGHT:
                # 用户按下了右箭头键
                # print("右箭头键被按下")
                # bKey = 2
                bKey.append(2)
            elif event.key == pygame.K_DOWN:
                # 用户按下了下箭头键
                # print("下箭头键被按下")
                # bKey = 3
                bKey.append(3)
            elif event.key == pygame.K_z:
                # 用户按下了上箭头键
                # print("上箭头键被按下")
                # bKey = 4
                bKey.append(4)
# Game logic ======================================================
    if (1 in bKey) and doesPieceFit(nCurrentPiece, nCurrentRotation, nCurrentX-1, nCurrentY):
        nCurrentX = nCurrentX - 1
        bKey.remove(1)
        # bKey=0

    if (2 in bKey) and doesPieceFit(nCurrentPiece, nCurrentRotation, nCurrentX+1, nCurrentY):
        nCurrentX = nCurrentX + 1
        bKey.remove(2)
        # bKey=0

    if (3 in bKey) and doesPieceFit(nCurrentPiece, nCurrentRotation, nCurrentX, nCurrentY+1):
        nCurrentY = nCurrentY + 1
        bKey.remove(3)
        # bKey=0

    if (4 in bKey) and doesPieceFit(nCurrentPiece, (nCurrentRotation+1)%4, nCurrentX, nCurrentY):
        nCurrentRotation += 1
        nCurrentRotation %= 4
        bKey.remove(4)
        # bKey=0

4.3物块下降

在俄罗斯方块中,方块会周期性地掉落。下落功能可以通过添加时间判断来容易地实现。

fall_time = 0.5
last_fall_time = time.time()
if current_time - last_fall_time >= fall_time:
    if doesPieceFit(nCurrentPiece, nCurrentRotation, nCurrentX,nCurrentY + 1):
        nCurrentY += 1
        last_fall_time = current_time

当然,我们还需要增加一个判断功能,看看方块是否可以继续下降。如果它不能下降,我们需要完成一系列的事情。

首先,我们需要将当前方块锁定到字段中。然后,检查我们是否有线路。最后,我们选择下一块,并检查它是否合适。

else:
2 # lock the current piece into the field.
3 for px in range(4):
4 for py in range(4):
5 if tetromino[nCurrentPiece][rotate(px, py,
nCurrentRotation)] == ’X’:
6 # print(len(pField))
7 #
print(int((nCurrentY+py)*fieldwidth+nCurrentX+px))
8 pField[int((nCurrentY+py)*fieldwidth+nCurrentX+px)]= 1
9 # check if we get any lines.
10 for py in range(4):
11 if nCurrentY+py<fieldheight-1:
12 bline = True
13 for px in range(1, fieldwidth):
14 if pField[(nCurrentY+py)*fieldwidth+px] == 0:
15 bline = False
16 if bline:
17 # remove the line
18 for px in range(1, fieldwidth-1):
19 pField[(nCurrentY + py) * fieldwidth + px] = 8
20 vline.append(nCurrentY+py)
21 # Finally, we choose next piece
22 nCurrentX = fieldwidth//2
23 nCurrentY = 0
24 nCurrentRotation = 0
25 nCurrentPiece = random.randint(0, 6)
26 # check if piece fits.
27 gameover = not doesPieceFit(nCurrentPiece, nCurrentRotation,nCurrentX, nCurrentY)
1 if len(vline) != 0:
2 pField_copy = copy.deepcopy(pField)
3 for dy in vline:
4 for dx in range(1, fieldwidth-1):
5 pField[dy*fieldwidth+dx] = 0
6 for dy in range(0, vline[0]):
7 for dx in range(1, fieldwidth-1):
8 pField[(dy + len(vline)) * fieldwidth + dx] = 0
9 for dy in range(0, vline[0]):
10 for dx in range(1, fieldwidth-1):
11 if pField_copy[dy*fieldwidth+dx] == 1:
12 pField[(dy+len(vline))*fieldwidth+dx] = 1
13 vline = []

5.附加功能:增加难度和分数显示

很容易可以通过以下代码实现:

1 nPieceCount = 1
2 nscore = 0
3
4 for py in range(4):
5 if nCurrentY+py<fieldheight-1:
6 bline = True
7 for px in range(1, fieldwidth):
8 if pField[(nCurrentY+py)*fieldwidth+px] == 0:
9 bline = False
10 if bline:
11 # remove the line
12 for px in range(1, fieldwidth-1):
13 pField[(nCurrentY + py) * fieldwidth + px] = 8
14 vline.append(nCurrentY+py)
15 # Finally, we choose next piece
16 nCurrentX = fieldwidth//2
17 nCurrentY = 0
18 nCurrentRotation = 0
19 nCurrentPiece = random.randint(0, 6)
20 nPieceCount += 1
21 nscore += 100
22
23 font = pygame.font.Font(None, 36)
24 score_text = font.render("Score: " + str(nscore), True, (255, 135,207))
25 screen.blit(score_text, (10, 10))

全部的代码如下:

import random
import copy
import pygame
import time
# creating assets
tetromino = []
object1 = "..X." \
           "..X." \
           "..X." \
           "..X."
object2 =  "..X." \
           ".XX." \
           ".X.." \
           "...."
object3 =  ".X.." \
           ".XX." \
           "..X." \
           "...."
object4 = "...." \
           ".XX." \
           ".XX." \
           "...."
object5 =  "..X." \
           ".XX." \
           "..X." \
           "...."
object6 = "...." \
           ".XX." \
           "..X." \
           "..X."
object7 = "...." \
           ".XX." \
           ".X.." \
           ".X.."

tetromino.append(object1)
tetromino.append(object2)
tetromino.append(object3)
tetromino.append(object4)
tetromino.append(object5)
tetromino.append(object6)
tetromino.append(object7)
# print(tetromino[1])
#
# exit(0)
# rotate
def rotate(px, py, r):
    if r == 0:
        return py*4+px
    elif r == 1:
        return 12+py-4*px
    elif r == 2:
        return 15-4*py-px
    elif r == 3:
        return 3-py+4*px


# Field
fieldwidth = 12
fieldheight = 18

pField = [0 for i in range(fieldwidth*fieldheight)]
# print(len(pField))
for x in range(fieldwidth):
    for y in range(fieldheight):
        # print(y*fieldwidth+x)
        pField[y*fieldwidth+x] = 9 if (x == 0 or x == fieldwidth-1 or y == fieldheight-1) else 0

# print(pField)
# game window
pygame.init()
window_width = 480
window_height = 720
window_title = "Tetris"

screen = pygame.display.set_mode((window_width, window_height))
pygame.display.set_caption(window_title)

# judge if the position of the piece fit the rule
def doesPieceFit(nTetromino, nrotation, positionX, positionY):
    for px in range(4):
        for py in range(4):
            # get the index
            pi = rotate(px, py, nrotation)
            # print(pi)
            # exit(0)
            #get index into field
            fi = int((positionY+py)*12+positionX+px)
            # print(fi)
            # exit(0)
            if (positionX+px >= 0) and (positionX+px <= 11):
                if (positionY+py >= 0) and (positionY+py <= 18):
                    if tetromino[nTetromino][pi] == 'X' and ((pField[fi] == 1) or (pField[fi] == 9)):
                        return False

    return True

# Game logic stuff======================================================
gameover = False
# which piece is falling
nCurrentPiece = random.randint(0, 6)
# is it rotate
nCurrentRotation = 0
nCurrentX = fieldwidth/2
nCurrentY = 0

bKey = []
clock = pygame.time.Clock()
fps = 60
wait_time = 100

fall_time = 0.5
last_fall_time = time.time()  # 初始化最后一次下降时间点
vline = []

nPieceCount = 1
nscore = 0
# Render output ===================================================

while not gameover:
    # game timing =====================================================
    clock.tick(fps)
    current_time = time.time()  # 当前时间
    # input ===========================================================
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            gameover = True
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT:
                # 用户按下了左箭头键
                # print("左箭头键被按下")
                # bKey = 1
                bKey.append(1)
            elif event.key == pygame.K_RIGHT:
                # 用户按下了右箭头键
                # print("右箭头键被按下")
                # bKey = 2
                bKey.append(2)
            elif event.key == pygame.K_DOWN:
                # 用户按下了下箭头键
                # print("下箭头键被按下")
                # bKey = 3
                bKey.append(3)
            elif event.key == pygame.K_z:
                # 用户按下了上箭头键
                # print("上箭头键被按下")
                # bKey = 4
                bKey.append(4)
    # Game logic ======================================================
    if (1 in bKey) and doesPieceFit(nCurrentPiece, nCurrentRotation, nCurrentX-1, nCurrentY):
        nCurrentX = nCurrentX - 1
        bKey.remove(1)
        # bKey=0

    if (2 in bKey) and doesPieceFit(nCurrentPiece, nCurrentRotation, nCurrentX+1, nCurrentY):
        nCurrentX = nCurrentX + 1
        bKey.remove(2)
        # bKey=0

    if (3 in bKey) and doesPieceFit(nCurrentPiece, nCurrentRotation, nCurrentX, nCurrentY+1):
        nCurrentY = nCurrentY + 1
        bKey.remove(3)
        # bKey=0

    if (4 in bKey) and doesPieceFit(nCurrentPiece, (nCurrentRotation+1)%4, nCurrentX, nCurrentY):
        nCurrentRotation += 1
        nCurrentRotation %= 4
        bKey.remove(4)
        # bKey=0

    if current_time - last_fall_time >= fall_time:
        # 执行方块的下降操作
        if doesPieceFit(nCurrentPiece, nCurrentRotation, nCurrentX, nCurrentY + 1):
            nCurrentY += 1
            last_fall_time = current_time  # 更新最后一次下降时间点
        else:
            # lock the current piece into the field.
            for px in range(4):
                for py in range(4):
                    if tetromino[nCurrentPiece][rotate(px, py, nCurrentRotation)] == 'X':
                        # print(len(pField))
                        # print(int((nCurrentY+py)*fieldwidth+nCurrentX+px))
                        pField[int((nCurrentY+py)*fieldwidth+nCurrentX+px)] = 1
            # check if we get any lines.
            for py in range(4):
                if nCurrentY+py<fieldheight-1:
                    bline = True
                    for px in range(1, fieldwidth):
                        if pField[(nCurrentY+py)*fieldwidth+px] == 0:
                            bline = False
                    if bline:
                        # remove the line
                        for px in range(1, fieldwidth-1):
                            pField[(nCurrentY + py) * fieldwidth + px] = 8
                        vline.append(nCurrentY+py)
            # Finally, we choose next piece
            nCurrentX = fieldwidth//2
            nCurrentY = 0
            nCurrentRotation = 0
            nCurrentPiece = random.randint(0, 6)
            nPieceCount += 1
            nscore += 100
            # check if piece fits.
            gameover = not doesPieceFit(nCurrentPiece, nCurrentRotation, nCurrentX, nCurrentY)
    # Fill in black background
    screen.fill((0, 0, 0))
    # Draw field pygame.draw.rect(screen, (255, 0, 0), (block_x, block_y, block_size, block_size))
    # pygame.draw.rect(screen, (255, 0, 0), (0, 0, 10, 10))
    for x in range(fieldwidth):
        for y in range(fieldheight):
            # print(y*fieldwidth+x)
            if pField[y*fieldwidth+x] == 9:
                pygame.draw.rect(screen, (255, 255, 179), (x*40, y*40, 40, 40))
            if pField[y*fieldwidth+x] == 1:
                pygame.draw.rect(screen, (255, 255, 255), (x*40, y*40, 40, 40))
            if pField[y*fieldwidth+x] == 8:
                pygame.draw.rect(screen, (255, 209, 224), (x*40, y*40, 40, 40))
    # draw current piece
    else:
        for px in range(4):
            for py in range(4):
                if tetromino[nCurrentPiece][rotate(px, py, nCurrentRotation)] == 'X':
                    pygame.draw.rect(screen, (255, 255, 255), ((nCurrentX+px) * 40, (nCurrentY+py) * 40, 40, 40))

    if len(vline) != 0:
        pField_copy = copy.deepcopy(pField)
        nscore += 114514*len(vline)
        for dy in vline:
            for dx in range(1, fieldwidth-1):
                pField[dy*fieldwidth+dx] = 0
        for dy in range(0, vline[0]):
            for dx in range(1, fieldwidth-1):
                pField[(dy + len(vline)) * fieldwidth + dx] = 0
        for dy in range(0, vline[0]):
            for dx in range(1, fieldwidth-1):
                if pField_copy[dy*fieldwidth+dx] == 1:
                    pField[(dy+len(vline))*fieldwidth+dx] = 1
        vline = []

    if (nPieceCount%10 == 0) and fall_time>0.2:
        fall_time -= 0.01


    font = pygame.font.Font(None, 36)  # 创建字体对象
    score_text = font.render("Score: " + str(nscore), True, (255, 135, 207))  # 渲染分数文本
    screen.blit(score_text, (10, 10))  # 在屏幕上绘制分数文本的位置

    # pygame.time.delay(wait_time)
    pygame.display.update()



pygame.quit()

# print(tetromino)






运行视频:

...

github链接:

https://github.com/NanGongXun/Python_Tetris.git

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

在下跳跳

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值