找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 52|回复: 3

[教程] renpy里的俄罗斯方块!

[复制链接]
发表于 昨天 16:10 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
本帖最后由 Aaron栩生阿龙 于 2025-10-12 21:21 编辑

前两天上课的时候突然产生了在renpy里塞俄罗斯方块的想法,而且找到了一个特别简单的方式,在群里问了一下好像之前没有人做这个,于是我花了两个小时的时间写了一个,这里放上代码
注意这个是纯Python代码
[RenPy] 纯文本查看 复制代码
"""renpy
init -2 python:
"""

import random

class Tetris():

    def __init__(self, width=10, height=20):

        self.width = width
        self.height = height

        self.shapes = [
            [[1, 1, 1, 1]],             # I
            [[1, 1], [1, 1]],           # O
            [[0, 1, 0], [1, 1, 1]],     # T
            [[0, 0, 1], [1, 1, 1]],     # L
            [[1, 0, 0], [1, 1, 1]],     # J
            [[0, 1, 1], [1, 1, 0]],     # S
            [[1, 1, 0], [0, 1, 1]],     # Z
        ]

        self.reset_game()

    def reset_game(self):

        self.board = [[0 for _ in range(self.width)] for _ in range(self.height)]
        self.score = 0
        self.gameover = False

        self.current_shape = None
        self.current_color = None
        self.current_x = 0
        self.current_y = 0

        self.next_shape = None
        self.next_color = None

        self._generate_next_shape()
        self._new_shape()

    def _generate_next_shape(self):
        self.next_shape = random.choice(self.shapes)
        self.next_color = random.randint(1, 7)

    def _new_shape(self):
        self.current_shape = self.next_shape
        self.current_color = self.next_color
        self.current_x = self.width // 2 - len(self.current_shape[0]) // 2 # 水平居中
        self.current_y = 0

        self._generate_next_shape()

        if self.check_collision(0, 0):
            self.gameover = True

    def check_collision(self, dx, dy, rotated_shape=None):
        shape = rotated_shape if rotated_shape is not None else self.current_shape

        for y, row in enumerate(shape):
            for x, cell in enumerate(row):
                if cell:
                    new_x = self.current_x + x + dx
                    new_y = self.current_y + y + dy

                    if (new_x < 0 or new_x >= self.width or         # 左右边界
                        new_y >= self.height or                     # 下边界
                        (new_y >= 0 and self.board[new_y][new_x])): # 下方的方块
                        return True
        
        return False
    
    def rotate_shape(self):
        # 这里的逻辑是将矩阵转置然后反转每一行,相当于是顺时针旋转90°
        rows = len(self.current_shape)
        cols = len(self.current_shape[0])

        rotated = [
            [self.current_shape[rows - j -1][i] for j in range(rows)] for i in range(cols)
        ]

        if not self.check_collision(0, 0, rotated):
            self.current_shape = rotated

    def move(self, dx):
        if not self.gameover and not self.check_collision(dx, 0):
            self.current_x += dx

    def drop(self):
        if not self.gameover and not self.check_collision(0, 1):
            self.current_y += 1
        else: # 这里是如果没法落下就说明碰到了下边界或者下面的方块,那么就把当前的方块固定
            self.lock_shape()
            self.clear_lines()
            self._new_shape()
        
    def lock_shape(self):
        for y, row in enumerate(self.current_shape):
            for x, cell in enumerate(row):
                if cell:
                    board_x = self.current_x + x
                    board_y = self.current_y + y
                    if board_y >= 0:
                        self.board[board_y][board_x] = self.current_color

    def clear_lines(self):
        cleared_lines = 0
        new_board = []

        for row in self.board:
            if 0 not in row:
                cleared_lines += 1
            else:
                new_board.append(row)

        for _ in range(cleared_lines):
            new_board.insert(0, [0 for _ in range(self.width)])

        self.board = new_board

        self.score += cleared_lines ** 2

    def dropping_cell(self):
        temp_board = [row.copy() for row in self.board]

        for y, row in enumerate(self.current_shape):
            for x, cell in enumerate(row):
                if cell:
                    temp_x = self.current_x + x
                    temp_y = self.current_y + y

                    if 0 <= temp_y < self.height and 0 <= temp_x < self.width:
                        temp_board[temp_y][temp_x] = self.current_color

        return temp_board


上面的是整体的逻辑部分,接下来是界面部分,这里是renpy代码
[RenPy] 纯文本查看 复制代码

image c0 = Solid('#000000', xysize=(30, 30))
image c1 = Solid('#66D2D6', xysize=(30, 30))
image c2 = Solid('#FFEE99', xysize=(30, 30))
image c3 = Solid('#A67FDB', xysize=(30, 30))
image c4 = Solid('#F9B384', xysize=(30, 30))
image c5 = Solid('#6C8CD5', xysize=(30, 30))
image c6 = Solid('#8CD98C', xysize=(30, 30))
image c7 = Solid('#E88388', xysize=(30, 30))

init python:

    tetris = Tetris()

    def tetris_board(st, at, tetris):

        cells = []
        for row in tetris.dropping_cell():
            for cell in row:
                cells.append(f'c{cell}')
                
        board = Grid(tetris.width, tetris.height, *cells)

        return board, 0.1

    def tetris_next_shape(st, at, tetris):

        cells = []
        for row in tetris.next_shape:
            for cell in row:
                if cell != 0:
                    cell = tetris.next_color
                cells.append(f'c{cell}')
        
        row = len(tetris.next_shape)
        col = len(tetris.next_shape[0])

        board = Grid(col, row, *cells)

        return board, 0.1

screen tetris_screen(tetris):

    add Solid('#FFF8', xysize=((tetris.width+2)*30, (tetris.height+2)*30)) align (0.5, 0.5)
    add DynamicDisplayable(tetris_board, tetris) align (0.5, 0.5)

    vbox:
        pos (1600, 400)
        spacing 50

        text '当前分数:[tetris.score]'
        text '下一个:'
        add DynamicDisplayable(tetris_next_shape, tetris)

    key 'K_LEFT' action Function(tetris.move, -1)
    key 'K_RIGHT' action Function(tetris.move, 1)
    key 'K_SPACE' action Function(tetris.rotate_shape)
    key 'K_DOWN' action Function(tetris.drop)

    if not tetris.gameover:
        timer 0.5 action Function(tetris.drop) repeat True
    else:
        frame:
            modal True
            align (0.5, 0.5)
            xysize (400, 300)

            vbox:
                align (0.5, 0.5)

                text 'you lose' xalign 0.5
                null height 100
                hbox:
                    xalign 0.5

                    textbutton '重新开始' action Function(tetris.reset_game)
                    textbutton '退出游戏' action Return()
    
label start:
    
    call screen tetris_screen(tetris)


然后就可以玩了
用键盘的左右方向键控制水平方向的移动,按下键可以加速方块下落(不是长按,是连按),点击空格可以旋转方块
这里暂时就先放代码,我之后会出一期视频教程发布在B站上,发布之后会在这里更新链接

评分

参与人数 1活力 +300 干货 +3 收起 理由
烈林凤 + 300 + 3 感谢分享!

查看全部评分

发表于 昨天 16:15 | 显示全部楼层
哇 太强了(掏出小本本)
这要怎么去增加音效捏
回复 支持 抱歉

使用道具 举报

 楼主| 发表于 昨天 16:16 | 显示全部楼层
zsdufigh 发表于 2025-10-12 16:15
哇 太强了(掏出小本本)
这要怎么去增加音效捏

可以在函数里加,等我之后出视频吧
回复 支持 抱歉

使用道具 举报

发表于 昨天 16:18 | 显示全部楼层
Aaron栩生阿龙 发表于 2025-10-12 16:16
可以在函数里加,等我之后出视频吧

好的好的!
回复 支持 抱歉

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|RenPy中文空间 ( 苏ICP备17067825号|苏公网安备 32092302000068号 )

GMT+8, 2025-10-13 08:15 , Processed in 0.050198 second(s), 25 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表