Blender中国社区
  • 关于
  • 新闻
  • 下载/汉化
    • 关于Blender
    • 下载Blender
    • 汉化Blender
    • 下载Blender LTS
    • 自定义启动界面
    • 修复文件预览
  • 插件NEW
  • 分享
  • 斑斓小知识
  • 中文手册
  • 斑斓视界
  • 官方视频
  • 赞助
2026/01/14
分享, 新闻

Blender里玩俄罗斯方块(源码)

Blender里玩俄罗斯方块(源码)
2026/01/14
分享, 新闻

工作累了,来个小游戏舒缓一下,俄罗斯方块非常经典的小游戏。

把下方的代码拷贝进Blender文本编辑器,点击三角运行,再去3D视口N面板中找到“俄方块”面板,开始即可。渲染面板中采样分类里时序重投影推荐关闭,不然会有闪烁。

				
					import bpy
import random
import math
from mathutils import Vector, Matrix
from bpy.types import Operator, Panel
import time

TETROMINOS = {
    'I': [
        [[0,0,0,0],
         [1,1,1,1],
         [0,0,0,0],
         [0,0,0,0]],
        [[0,0,1,0],
         [0,0,1,0],
         [0,0,1,0],
         [0,0,1,0]]
    ],
    'O': [
        [[1,1],
         [1,1]]
    ],
    'T': [
        [[0,1,0],
         [1,1,1],
         [0,0,0]],
        [[0,1,0],
         [0,1,1],
         [0,1,0]],
        [[0,0,0],
         [1,1,1],
         [0,1,0]],
        [[0,1,0],
         [1,1,0],
         [0,1,0]]
    ],
    'S': [
        [[0,1,1],
         [1,1,0],
         [0,0,0]],
        [[0,1,0],
         [0,1,1],
         [0,0,1]]
    ],
    'Z': [
        [[1,1,0],
         [0,1,1],
         [0,0,0]],
        [[0,0,1],
         [0,1,1],
         [0,1,0]]
    ],
    'J': [
        [[1,0,0],
         [1,1,1],
         [0,0,0]],
        [[0,1,1],
         [0,1,0],
         [0,1,0]],
        [[0,0,0],
         [1,1,1],
         [0,0,1]],
        [[0,1,0],
         [0,1,0],
         [1,1,0]]
    ],
    'L': [
        [[0,0,1],
         [1,1,1],
         [0,0,0]],
        [[0,1,0],
         [0,1,0],
         [0,1,1]],
        [[0,0,0],
         [1,1,1],
         [1,0,0]],
        [[1,1,0],
         [0,1,0],
         [0,1,0]]
    ]
}

TETROMINO_COLORS = {
    'I': (0.0, 1.0, 1.0, 1.0),
    'O': (1.0, 1.0, 0.0, 1.0),
    'T': (0.8, 0.0, 0.8, 1.0),
    'S': (0.0, 1.0, 0.0, 1.0),
    'Z': (1.0, 0.0, 0.0, 1.0),
    'J': (0.0, 0.0, 1.0, 1.0),
    'L': (1.0, 0.5, 0.0, 1.0)
}

class TetrisBlock:
    """单个方块块的类"""
    def __init__(self, position, color, is_preview=False):
        self.position = Vector(position)
        self.color = color
        self.is_preview = is_preview
        self.mesh = None
        self.mesh_name = f"Tetris_Block_{id(self)}_{'preview' if is_preview else 'active'}"
        self.create_mesh()
    
    def create_mesh(self):
        """创建方块块的3D网格"""
        bpy.ops.mesh.primitive_cube_add(size=0.9, location=self.position)
        self.mesh = bpy.context.active_object
        self.mesh.name = self.mesh_name
        
        # 创建适用于渲染引擎的材质
        mat_name = f"Block_{id(self)}_{'preview' if self.is_preview else 'active'}"
        if mat_name in bpy.data.materials:
            mat = bpy.data.materials[mat_name]
        else:
            mat = bpy.data.materials.new(name=mat_name)
            
            mat.use_nodes = True
            nodes = mat.node_tree.nodes
            links = mat.node_tree.links
            
            for node in nodes:
                nodes.remove(node)

            principled = nodes.new(type='ShaderNodeBsdfPrincipled')
            output = nodes.new(type='ShaderNodeOutputMaterial')
            principled.inputs['Base Color'].default_value = self.color
            principled.inputs['Metallic'].default_value = 0.1 
            principled.inputs['Roughness'].default_value = 0.3 
            
            # 如果是预览方块,调整透明度
            if self.is_preview:
                principled.inputs['Alpha'].default_value = 0.6
                mat.blend_method = 'BLEND'
            
            links.new(principled.outputs['BSDF'], output.inputs['Surface'])
            
            principled.location = (-200, 0)
            output.location = (0, 0)
        
        if self.mesh.data.materials:
            self.mesh.data.materials[0] = mat
        else:
            self.mesh.data.materials.append(mat)
    
    def move_to(self, new_position):
        """移动方块到新位置"""
        self.position = Vector(new_position)
        if self.mesh_name in bpy.data.objects:
            bpy.data.objects[self.mesh_name].location = self.position
    
    def remove(self):
        """删除方块"""
        if self.mesh_name in bpy.data.objects:
            bpy.data.objects.remove(bpy.data.objects[self.mesh_name], do_unlink=True)
        self.mesh = None

class TetrisPiece:
    """俄罗斯方块棋子类"""
    def __init__(self, piece_type, position=(0, 0, 0), is_preview=False):
        self.type = piece_type
        self.position = Vector(position)
        self.rotation = 0
        self.color = TETROMINO_COLORS[piece_type]
        self.is_preview = is_preview
        self.blocks = []
        self.create_blocks()
    
    def create_blocks(self):
        """根据形状创建方块块"""
        shape = self.get_current_shape()
        
        for y in range(len(shape)):
            for x in range(len(shape[y])):
                if shape[y][x]:
                    block_pos = Vector((
                        self.position.x + x,
                        self.position.y + y,
                        0
                    ))
                    block = TetrisBlock(block_pos, self.color, self.is_preview)
                    self.blocks.append(block)
    
    def get_current_shape(self):
        """获取当前旋转状态下的形状"""
        shapes = TETROMINOS[self.type]
        return shapes[self.rotation % len(shapes)]
    
    def rotate(self, clockwise=True):
        """旋转方块(不移动位置,只改变形状)"""
        old_rotation = self.rotation
        shapes = TETROMINOS[self.type]
        
        if clockwise:
            self.rotation = (self.rotation + 1) % len(shapes)
        else:
            self.rotation = (self.rotation - 1) % len(shapes)
        
        # 更新所有方块的位置
        self.update_blocks_positions()
        
        return True
    
    def update_blocks_positions(self):
        """根据当前形状更新所有方块的位置"""
        shape = self.get_current_shape()
        block_idx = 0
        
        for y in range(len(shape)):
            for x in range(len(shape[y])):
                if shape[y][x]:
                    if block_idx < len(self.blocks):
                        new_pos = Vector((
                            self.position.x + x,
                            self.position.y + y,
                            0
                        ))
                        self.blocks[block_idx].move_to(new_pos)
                        block_idx += 1
    
    def move(self, dx, dy):
        """移动方块"""
        self.position += Vector((dx, dy, 0))
        
        # 更新所有方块的位置
        for block in self.blocks:
            if block.mesh_name in bpy.data.objects:
                block.move_to(block.position + Vector((dx, dy, 0)))
    
    def get_block_positions(self):
        """获取所有方块的世界坐标位置"""
        positions = []
        for block in self.blocks:
            if block.mesh_name in bpy.data.objects:
                positions.append(block.position.copy())
        return positions
    
    def get_block_grid_positions(self):
        """获取所有方块在网格中的坐标"""
        positions = self.get_block_positions()
        grid_positions = []
        for pos in positions:
            grid_positions.append((int(pos.x), int(pos.y)))
        return grid_positions
    
    def remove(self):
        """删除整个方块"""
        for block in self.blocks:
            block.remove()
        self.blocks = []

class TetrisGame3D:
    """3D俄罗斯方块游戏类"""
    def __init__(self, width=10, height=20):
        self.width = width
        self.height = height
        self.grid = [[None for _ in range(width)] for _ in range(height)]
        self.current_piece = None
        self.next_piece_type = None
        self.next_preview = None
        self.score = 0
        self.level = 1
        self.lines_cleared = 0  # 已清除的行数
        self.game_over = False
        self.paused = False  # 暂停状态
        self.last_drop_time = time.time()
        self.drop_interval = 1.0
        self.grid_objects = []
        self.create_grid()
        self.init_game()
    
    def create_grid(self):
        """创建游戏网格(只创建网格线,不创建背景板)"""
        # 删除旧的网格对象
        for obj_name in list(bpy.data.objects.keys()):
            if obj_name.startswith("Grid_"):
                bpy.data.objects.remove(bpy.data.objects[obj_name], do_unlink=True)
        
        # 创建网格线
        grid_color = (0.5, 0.5, 0.5, 0.3)
        for x in range(self.width + 1):
            bpy.ops.mesh.primitive_cube_add(size=0.02)
            line = bpy.context.active_object
            line.name = f"Grid_Line_X_{x}"
            line.location = (x - 0.5, self.height/2 - 0.5, -0.5)
            line.dimensions = (0.02, self.height, 0.02)
            
            mat = bpy.data.materials.new(name=f"Grid_Line_Mat_X_{x}")
            mat.diffuse_color = grid_color
            line.data.materials.append(mat)
            self.grid_objects.append(line.name)
        for y in range(self.height + 1):
            bpy.ops.mesh.primitive_cube_add(size=0.02)
            line = bpy.context.active_object
            line.name = f"Grid_Line_Y_{y}"
            line.location = (self.width/2 - 0.5, y - 0.5, -0.5)
            line.dimensions = (self.width, 0.02, 0.02)
            
            mat = bpy.data.materials.new(name=f"Grid_Line_Mat_Y_{y}")
            mat.diffuse_color = grid_color
            line.data.materials.append(mat)
            self.grid_objects.append(line.name)
    
    def init_game(self):
        """初始化游戏"""
        self.cleanup_old_blocks()
        self.grid = [[None for _ in range(self.width)] for _ in range(self.height)]
        
        self.score = 0
        self.level = 1
        self.lines_cleared = 0
        self.game_over = False
        self.paused = False
        self.drop_interval = 1.0
        
        # 创建新方块
        self.next_piece_type = self.get_random_piece_type()
        self.spawn_new_piece()
    
    def cleanup_old_blocks(self):
        """清理旧的游戏方块"""
        # 清理固定方块和移动方块
        objects_to_remove = []
        for obj in bpy.data.objects:
            if (obj.name.startswith("Fixed_Block_") or 
                obj.name.startswith("Tetris_Block_")):
                objects_to_remove.append(obj)
        
        for obj in objects_to_remove:
            bpy.data.objects.remove(obj, do_unlink=True)
        
        # 清理游戏结束文本和暂停文本
        for text_name in ["Game_Over_Text", "Paused_Text"]:
            if text_name in bpy.data.objects:
                bpy.data.objects.remove(bpy.data.objects[text_name], do_unlink=True)
    
    def get_random_piece_type(self):
        """随机获取一个方块类型"""
        return random.choice(list(TETROMINOS.keys()))
    
    def spawn_new_piece(self):
        """生成新的当前方块"""
        # 清理旧的预览方块
        if self.next_preview:
            self.next_preview.remove()
        
        current_type = self.next_piece_type
        self.current_piece = TetrisPiece(current_type, position=(self.width//2 - 1, self.height - 4, 0))
        
        self.next_piece_type = self.get_random_piece_type()
        
        self.next_preview = TetrisPiece(self.next_piece_type, position=(11, 15, 0), is_preview=True)
    
    def check_collision(self, piece, dx=0, dy=0):
        """检查碰撞"""
        # 获取方块移动后的网格位置
        grid_positions = []
        for block in piece.blocks:
            if block.mesh_name in bpy.data.objects:
                new_x = int(block.position.x + dx)
                new_y = int(block.position.y + dy)
                grid_positions.append((new_x, new_y))
        
        for x, y in grid_positions:
            # 检查边界
            if x < 0 or x >= self.width:
                return True
            
            if y < 0:
                return True
            
            # 检查与其他方块的碰撞
            if y < self.height and self.grid[y][x] is not None:
                return True
        
        return False
    
    def move_piece(self, dx, dy):
        """移动当前方块"""
        if not self.current_piece or self.game_over or self.paused:
            return False
        
        if not self.check_collision(self.current_piece, dx, dy):
            self.current_piece.move(dx, dy)
            return True
        return False
    
    def rotate_piece(self, clockwise=True):
        """旋转当前方块"""
        if not self.current_piece or self.game_over or self.paused:
            return False
        
        # 保存当前旋转状态
        old_rotation = self.current_piece.rotation
        
        # 尝试旋转
        if clockwise:
            self.current_piece.rotation = (self.current_piece.rotation + 1) % len(TETROMINOS[self.current_piece.type])
        else:
            self.current_piece.rotation = (self.current_piece.rotation - 1) % len(TETROMINOS[self.current_piece.type])
        
        self.current_piece.update_blocks_positions()
        
        # 如果旋转后碰撞,尝试左右移动来避免碰撞(墙壁踢)
        if self.check_collision(self.current_piece):
            # 尝试向右移动
            if not self.check_collision(self.current_piece, 1, 0):
                self.current_piece.move(1, 0)
            # 尝试向左移动
            elif not self.check_collision(self.current_piece, -1, 0):
                self.current_piece.move(-1, 0)
            # 如果都不行,恢复旋转
            else:
                self.current_piece.rotation = old_rotation
                self.current_piece.update_blocks_positions()
                return False
        
        return True
    
    def hard_drop(self):
        """硬降落"""
        if not self.current_piece or self.game_over or self.paused:
            return
        
        # 一直下落直到碰撞
        while not self.check_collision(self.current_piece, 0, -1):
            self.current_piece.move(0, -1)
        
        self.place_piece()
    
    def place_piece(self):
        """将当前方块放置到网格中"""
        # 记录所有方块的位置
        placed_positions = []
        
        for block in self.current_piece.blocks:
            if block.mesh_name in bpy.data.objects:
                x = int(round(block.position.x))
                y = int(round(block.position.y))
                
                if 0 <= x < self.width and 0 <= y < self.height:
                    self.grid[y][x] = self.current_piece.color
                    placed_positions.append((x, y))
        
        # 创建固定方块
        for x, y in placed_positions:
            self.create_fixed_block(x, y, self.current_piece.color)
        
        # 检查并清除完整的行
        lines_cleared = self.clear_lines()
        
        # 更新行数和分数
        if lines_cleared > 0:
            self.lines_cleared += lines_cleared
            # 计分规则:一行100分,两行300分,三行500分,四行800分
            if lines_cleared == 1:
                self.score += 100 * self.level
            elif lines_cleared == 2:
                self.score += 300 * self.level
            elif lines_cleared == 3:
                self.score += 500 * self.level
            elif lines_cleared >= 4:
                self.score += 800 * self.level
            
            # 每清除10行升一级
            if self.lines_cleared >= self.level * 10:
                self.level += 1
                self.drop_interval = max(0.1, self.drop_interval * 0.8)  # 速度加快
        
        # 生成新方块
        self.current_piece.remove()
        self.spawn_new_piece()
        
        # 检查游戏是否结束
        if self.check_collision(self.current_piece):
            self.game_over = True
            self.create_game_over_text()
    
    def create_fixed_block(self, x, y, color):
        """创建单个固定方块"""
        block_name = f"Fixed_Block_{x}_{y}_{id(self)}"
        
        # 检查是否已经有方块
        if block_name not in bpy.data.objects:
            bpy.ops.mesh.primitive_cube_add(size=0.9)
            block = bpy.context.active_object
            block.name = block_name
            block.location = (x, y, 0)
            
            # 创建适用于渲染的材质
            mat_name = f"Fixed_Mat_{x}_{y}_{id(self)}"
            if mat_name in bpy.data.materials:
                mat = bpy.data.materials[mat_name]
            else:
                mat = bpy.data.materials.new(name=mat_name)
                mat.use_nodes = True
                nodes = mat.node_tree.nodes
                links = mat.node_tree.links
                
                # 清除默认节点
                for node in nodes:
                    nodes.remove(node)
                
                # 创建新的材质节点
                principled = nodes.new(type='ShaderNodeBsdfPrincipled')
                output = nodes.new(type='ShaderNodeOutputMaterial')
                
                # 设置颜色和属性
                principled.inputs['Base Color'].default_value = color
                principled.inputs['Metallic'].default_value = 0.15
                principled.inputs['Roughness'].default_value = 0.25
                
                # 连接节点
                links.new(principled.outputs['BSDF'], output.inputs['Surface'])
                
                # 设置节点位置
                principled.location = (-200, 0)
                output.location = (0, 0)
            
            block.data.materials.append(mat)
        
    
    def clear_lines(self):
        """清除完整的行并返回清除的行数"""
        lines_to_clear = []
        
        for y in range(self.height):
            if all(cell is not None for cell in self.grid[y]):
                lines_to_clear.append(y)
        
        # 清除行
        for line in sorted(lines_to_clear, reverse=True):
            # 删除该行的3D方块
            for x in range(self.width):
                # 查找并删除所有以Fixed_Block_x_line开头的对象
                objects_to_remove = []
                for obj in bpy.data.objects:
                    if obj.name.startswith(f"Fixed_Block_{x}_{line}_"):
                        objects_to_remove.append(obj)
                
                for obj in objects_to_remove:
                    bpy.data.objects.remove(obj, do_unlink=True)
            
            # 删除该行数据
            del self.grid[line]
            # 在顶部添加新的空行
            self.grid.append([None for _ in range(self.width)])
        
        # 重新创建所有固定方块
        if lines_to_clear:
            self.recreate_all_fixed_blocks()
        
        return len(lines_to_clear)
    
    def recreate_all_fixed_blocks(self):
        """重新创建所有固定方块"""
        # 删除所有固定方块
        objects_to_remove = []
        for obj in bpy.data.objects:
            if obj.name.startswith("Fixed_Block_"):
                objects_to_remove.append(obj)
        
        for obj in objects_to_remove:
            bpy.data.objects.remove(obj, do_unlink=True)
        
        # 重新创建
        for y in range(self.height):
            for x in range(self.width):
                if self.grid[y][x] is not None:
                    self.create_fixed_block(x, y, self.grid[y][x])
    
    def create_game_over_text(self):
        """创建游戏结束文本"""
        bpy.ops.object.text_add()
        text_obj = bpy.context.active_object
        text_obj.name = "Game_Over_Text"
        text_obj.data.body = "菜鸡\n按 R 重开"
        text_obj.data.align_x = 'CENTER'
        text_obj.data.align_y = 'CENTER'
        text_obj.location = (self.width/2 - 0.5, self.height/2 - 0.5, 2)
        text_obj.scale = (0.5, 0.5, 0.5)
        
        mat = bpy.data.materials.new(name="Game_Over_Text_Mat")
        mat.diffuse_color = (1.0, 0.0, 0.0, 1.0)
        text_obj.data.materials.append(mat)
    
    def toggle_pause(self):
        """切换暂停状态"""
        self.paused = not self.paused
        
        if self.paused:
            # 创建暂停文本
            bpy.ops.object.text_add()
            text_obj = bpy.context.active_object
            text_obj.name = "Paused_Text"
            text_obj.data.body = "暂停\n按 P 恢复"
            text_obj.data.align_x = 'CENTER'
            text_obj.data.align_y = 'CENTER'
            text_obj.location = (self.width/2 - 0.5, self.height/2 - 0.5, 2)
            text_obj.scale = (0.5, 0.5, 0.5)
            
            # 创建材质
            mat = bpy.data.materials.new(name="Paused_Text_Mat")
            mat.diffuse_color = (1.0, 1.0, 0.0, 1.0)
            text_obj.data.materials.append(mat)
        else:
            # 删除暂停文本
            if "Paused_Text" in bpy.data.objects:
                bpy.data.objects.remove(bpy.data.objects["Paused_Text"], do_unlink=True)
    
    def update(self):
        """更新游戏状态"""
        if self.game_over or self.paused:
            return
        
        current_time = time.time()
        if current_time - self.last_drop_time > self.drop_interval:
            if not self.move_piece(0, -1):
                self.place_piece()
            self.last_drop_time = current_time
    
    def restart(self):
        """重新开始游戏"""
        self.cleanup_old_blocks()
        if self.next_preview:
            self.next_preview.remove()
        
        self.init_game()

# 全局游戏实例
tetris_game_3d = None

class TETRIS_OT_StartGame(Operator):
    """开始游戏"""
    bl_idname = "tetris.start_game"
    bl_label = "Start 3D Tetris"
    bl_description = "Start a 3D Tetris game in the viewport"
    
    timer = None
    
    def modal(self, context, event):
        global tetris_game_3d
        
        if not tetris_game_3d:
            return {'CANCELLED'}
        
        # 游戏控制
        if event.type == 'R' and event.value == 'PRESS':
            if tetris_game_3d.game_over or tetris_game_3d.paused:
                tetris_game_3d.restart()
            return {'RUNNING_MODAL'}
        
        if event.type == 'P' and event.value == 'PRESS':  # 暂停/继续
            tetris_game_3d.toggle_pause()
            return {'RUNNING_MODAL'}
        
        if event.type in {'LEFT_ARROW', 'A'} and event.value == 'PRESS':  # 左移
            tetris_game_3d.move_piece(-1, 0)
            return {'RUNNING_MODAL'}
        
        if event.type in {'RIGHT_ARROW', 'D'} and event.value == 'PRESS':  # 右移
            tetris_game_3d.move_piece(1, 0)
            return {'RUNNING_MODAL'}
        
        if event.type in {'DOWN_ARROW', 'S'} and event.value == 'PRESS':  # 加速下落
            tetris_game_3d.move_piece(0, -1)
            return {'RUNNING_MODAL'}
        
        if event.type == 'SPACE' and event.value == 'PRESS':  # 硬降落
            tetris_game_3d.hard_drop()
            return {'RUNNING_MODAL'}
        
        if event.type in {'UP_ARROW', 'J', 'W'} and event.value == 'PRESS':  # 旋转
            tetris_game_3d.rotate_piece(clockwise=True)
            return {'RUNNING_MODAL'}

        
        if event.type == 'TIMER':
            tetris_game_3d.update()
        
        return {'PASS_THROUGH'}
    
    def execute(self, context):
        global tetris_game_3d
        
        # 清理可能存在的旧游戏
        if tetris_game_3d:
            tetris_game_3d = None
        
        self.cleanup_game_objects()
        
        # 创建新游戏
        tetris_game_3d = TetrisGame3D(width=10, height=20)
        
        
        # 启动定时器
        wm = context.window_manager
        self.timer = wm.event_timer_add(0.1, window=context.window)
        wm.modal_handler_add(self)
        
        self.report({'INFO'}, "3D Tetris Game Started! Use arrow keys or WASD to move, Z/X or J/K to rotate, P to pause")
        return {'RUNNING_MODAL'}
    
    def cleanup_game_objects(self):
        """清理游戏对象"""
        objects_to_remove = []
        for obj in bpy.data.objects:
            if any(name in obj.name for name in ["Tetris_", "Grid_", "Fixed_", "Game_Over", "Paused_", "Score_", "Level_", "Lines_", "Preview_", "Next_"]):
                objects_to_remove.append(obj)
        
        for obj in objects_to_remove:
            bpy.data.objects.remove(obj, do_unlink=True)
    
    
    def cancel(self, context):
        """取消游戏"""
        global tetris_game_3d
        
        wm = context.window_manager
        if self.timer:
            wm.event_timer_remove(self.timer)
        
        # 清理游戏对象
        if tetris_game_3d:
            try:
                tetris_game_3d.cleanup_old_blocks()
                if tetris_game_3d.next_preview:
                    tetris_game_3d.next_preview.remove()
            except:
                pass  # 忽略清理错误
        
        self.report({'INFO'}, "Game stopped")
        return {'CANCELLED'}

class TETRIS_OT_ResetGame(Operator):
    """重置游戏"""
    bl_idname = "tetris.reset_game"
    bl_label = "Reset 3D Tetris"
    bl_description = "Reset the 3D Tetris game"
    
    def execute(self, context):
        global tetris_game_3d
        
        if tetris_game_3d:
            tetris_game_3d.restart()
            self.report({'INFO'}, "Game reset! Score cleared to 0.")
        else:
            self.report({'WARNING'}, "No game to reset!")
        
        return {'FINISHED'}

class TETRIS_OT_TogglePause(Operator):
    """暂停/继续游戏"""
    bl_idname = "tetris.toggle_pause"
    bl_label = "Pause/Resume Game"
    bl_description = "暂停和恢复游戏"
    
    def execute(self, context):
        global tetris_game_3d
        
        if tetris_game_3d:
            tetris_game_3d.toggle_pause()
            status = "paused" if tetris_game_3d.paused else "resumed"
            self.report({'INFO'}, f"Game {status}!")
        else:
            self.report({'WARNING'}, "No game to pause!")
        
        return {'FINISHED'}

class TETRIS_OT_CleanupGame(Operator):
    """清理游戏"""
    bl_idname = "tetris.cleanup_game"
    bl_label = "Cleanup Game"
    bl_description = "移除所有游戏物体"
    
    def execute(self, context):
        global tetris_game_3d
        
        objects_to_remove = []
        for obj in bpy.data.objects:
            if any(name in obj.name for name in [
                "Tetris_", "Grid_", "Fixed_", "Game_Over", 
                "Paused_", "Score_", "Level_", "Lines_", 
                "Next_", "Preview_"
            ]):
                objects_to_remove.append(obj)
        
        removed_count = 0
        for obj in objects_to_remove:
            try:
                bpy.data.objects.remove(obj, do_unlink=True)
                removed_count += 1
            except:
                pass

        if tetris_game_3d:
            tetris_game_3d = None
        
        self.report({'INFO'}, f"Cleaned up {removed_count} game objects")
        return {'FINISHED'}

class TETRIS_PT_GamePanel(Panel):
    bl_label = "3D Tetris Game"
    bl_idname = "TETRIS_PT_GamePanel"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_category = "俄方块"
    
    def draw(self, context):
        layout = self.layout
        global tetris_game_3d
        
        layout.operator("tetris.start_game", text="开始游戏", icon='PLAY')
        
        if tetris_game_3d:
            row = layout.row()
            row.operator("tetris.reset_game", text="重置游戏", icon='FILE_REFRESH')
            pause_text = "恢复" if tetris_game_3d.paused else "暂停"
            row.operator("tetris.toggle_pause", text=pause_text, icon='PAUSE')
        
        layout.separator()
        layout.operator("tetris.cleanup_game", text="清除游戏", icon='TRASH')
        
        if tetris_game_3d:
            box = layout.box()
            box.label(text="游戏状态:", icon='INFO')
            box.label(text=f"游戏积分: {tetris_game_3d.score}")
            box.label(text=f"游戏等级: {tetris_game_3d.level}")
            box.label(text=f"清除行数: {tetris_game_3d.lines_cleared}")
            
            if tetris_game_3d.game_over:
                box.label(text="游戏结束", icon='CANCEL')
                box.label(text="按 R 重启游戏")
            elif tetris_game_3d.paused:
                box.label(text="暂停", icon='PAUSE')
                box.label(text="按 P 恢复游戏")
        
        box = layout.box()
        box.label(text="控制:", icon='HAND')
        box.label(text="←/→ 或 A/D - 左右移动")
        box.label(text="↓ 或 S - 快速下移")
        box.label(text="W 或 ↑ 或 J- 旋转")
        box.label(text="SPACE - 坠落")
        box.label(text="P - 暂停/恢复")
        

def register():
    bpy.utils.register_class(TETRIS_OT_StartGame)
    bpy.utils.register_class(TETRIS_OT_ResetGame)
    bpy.utils.register_class(TETRIS_OT_TogglePause)
    bpy.utils.register_class(TETRIS_OT_CleanupGame)
    bpy.utils.register_class(TETRIS_PT_GamePanel)
    

def unregister():
    bpy.utils.unregister_class(TETRIS_OT_StartGame)
    bpy.utils.unregister_class(TETRIS_OT_ResetGame)
    bpy.utils.unregister_class(TETRIS_OT_TogglePause)
    bpy.utils.unregister_class(TETRIS_OT_CleanupGame)
    bpy.utils.unregister_class(TETRIS_PT_GamePanel)
    
    global tetris_game_3d
    if tetris_game_3d:
        tetris_game_3d = None
    objects_to_remove = []
    for obj in bpy.data.objects:
        if any(x in obj.name for x in ["Tetris_", "Grid_", "Fixed_", "Game_", "Paused_", "Score_", "Level_", "Lines_", "Next_", "Preview_"]):
            objects_to_remove.append(obj)
    
    for obj in objects_to_remove:
        try:
            bpy.data.objects.remove(obj, do_unlink=True)
        except:
            pass

if __name__ == "__main__":
    register()
    

				
			
上一篇不要随意打开别人的Blend文件,小心病毒

社区周边

中文入门教程pdf

小插件下载

  • 对齐2D 2025/02/25
  • 相机插件Camera Extra 2023/07/30
  • 批量渲染SendToBBR 2023/08/21
  • 资产整理插件Classify objects by collection 2023/07/16
  • 关键帧辅助插件KeyFrameMotionTools 2023/07/26
  • 一键中英切换,主题切换插件 2023/03/16

BlenderCN团队(名字不分先后)

Kidux Jollin Deathblood 老猫 阳 冰冻牡蛎 摇落月光 水经石 WeWe猫 Wiilovy Harrison Richeir 无花果 AK铁毛 索多玛城主 遥远的桥 疯子林 freemind FXZT masicblack 蓝色冰块 CG农民工 原志翔 Nihil 锐锋 毛货 Kirito John-刘 NightCandle panther Leroy 诺德 颖心欢欣 云风如我 MASSA-Ultra 小A gkuggkug豆豆 难知如阴 soul 夜海竹声 幻星孤独 Diano 盧 发霉的红地蛋 野蛮人 一钱不名 守夜人 NGENNGT 空蓝 甬|coffee 饥饿的面包 星煋★star· 习鱼 叶月葵 小闲 Klxux Kang Shark 极D创意 kirk Origin 只剩一瓶辣椒酱 maxmadzz free-king 33DE兔

友情链接

  • Blender基金会
  • Blender官方网站
  • 中文全球Blender每日动态
  • 斑斓社区淘宝店
备案号: 蜀ICP备17002929号-1 站务联系: blender@vip.qq.com