【Dison夏令营 Day 16】如何使用 Python 中的 PyGame 制作俄罗斯方块游戏

俄罗斯方块(Tetris)是一款经典的益智游戏,游戏的目的是将落下的几何图形片(称为 “俄罗斯方块”)排列起来,填满水平线,不留空隙。当一条线被完全填满时,它就被清除了,玩家就能获得分数。随着四角棋的下落速度加快,游戏的挑战性也逐渐增加,及时排列四角棋也变得更加困难。

在这里插入图片描述

在本教程中,我们将指导您使用 PyGame 逐步创建一个简单的俄罗斯方块游戏。我们将介绍以下内容:

  • 设置开发环境并安装 Pygame
  • 创建游戏窗口并定义俄罗斯方块棋子
  • 实现俄罗斯方块和俄罗斯方块类以管理游戏逻辑
  • 处理用户输入和游戏事件
  • 在屏幕上绘制游戏状态、分数和游戏结束信息
  • 执行主游戏循环

在本教程结束时,您将拥有一个功能齐全的俄罗斯方块游戏,并可以进行游戏和自定义。这个项目还将帮助您了解使用 Python 和 Pygame 开发游戏的基础知识,并为创建其他类型的游戏打下基础。下面是它的外观:

在这里插入图片描述

入门

安装好 Python 后,打开终端或命令提示符,输入以下命令安装 Pygame 库:

$ pip install pygame

创建游戏窗口

现在我们的开发环境已经准备就绪,让我们开始创建游戏窗口并定义四面体形状。

首先,我们需要导入俄罗斯方块游戏所需的模块。在 Python 脚本中添加以下几行:

import sys
import pygame
import random

要使用 Pygame 库,我们需要对其进行初始化。在 import 语句后添加以下一行:

pygame.init()

定义屏幕尺寸、颜色和四角棋形状

接下来,我们需要为游戏定义一些常量,如屏幕尺寸、网格大小、颜色和四角棋的形状。在脚本中添加以下几行:

# Screen dimensions
WIDTH, HEIGHT = 800, 600
GRID_SIZE = 25

# Colors
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
GREEN = (0, 255, 0)
COLORS = [RED, BLUE, GREEN]

# Tetromino shapes
SHAPES = [
    [
        ['.....',
         '.....',
         '.....',
         'OOOO.',
         '.....'],
        ['.....',
         '..O..',
         '..O..',
         '..O..',
         '..O..']
    ],
    [
        ['.....',
         '.....',
         '..O..',
         '.OOO.',
         '.....'],
        ['.....',
         '..O..',
         '.OO..',
         '..O..',
         '.....'],
        ['.....',
         '.....',
         '.OOO.',
         '..O..',
         '.....'],
        ['.....',
         '..O..',
         '..OO.',
         '..O..',
         '.....']
    ],
    [
        [
         '.....',
         '.....',
         '..OO.',
         '.OO..',
         '.....'],
        ['.....',
         '.....',
         '.OO..',
         '..OO.',
         '.....'],
        ['.....',
         '.O...',
         '.OO..',
         '..O..',
         '.....'],
        ['.....',
         '..O..',
         '.OO..',
         '.O...',
         '.....']
    ],
    [
        ['.....',
         '..O..',
         '..O.',
         '..OO.',
         '.....'],
        ['.....',
         '...O.',
         '.OOO.',
         '.....',
         '.....'],
        ['.....',
         '.OO..',
         '..O..',
         '..O..',
         '.....'],
        ['.....',
         '.....',
         '.OOO.',
         '.O...',
         '.....']
    ],
]


在这里,我们定义了游戏窗口的宽度和高度、四角棋的网格大小以及游戏使用的颜色(白色、黑色和红色)。SHAPES 列表包含以字符串表示的四面体形状,您可以随意添加/编辑。您还可以随意调整颜色和网格大小。

实现 Tetromino 和 Tetris 类以管理游戏逻辑

现在,我们已经定义了游戏窗口和俄罗斯方块形状,让我们创建两个类来管理游戏逻辑:Tetromino 类和 Tetris 方块类。

创建 Tetromino 类

Tetromino 类将表示单个四面体,并具有位置、形状、颜色和旋转属性。在脚本中添加以下代码:

class Tetromino:
    def __init__(self, x, y, shape):
        self.x = x
        self.y = y
        self.shape = shape
        self.color = random.choice(COLORS) # You can choose different colors for each shape
        self.rotation = 0

创建 Tetris 类

俄罗斯方块类将处理主要的游戏逻辑。它将包含用于创建新棋子、检查走棋是否有效、清线、锁定棋子、更新游戏状态和绘制游戏的方法。在脚本中添加以下代码:

class Tetris:
    def __init__(self, width, height):
        self.width = width
        self.height = height
        self.grid = [[0 for _ in range(width)] for _ in range(height)]
        self.current_piece = self.new_piece()
        self.game_over = False
        self.score = 0  # Add score attribute

让我们来制作一个新的 Tetromino 棋子:

    def new_piece(self):
        # Choose a random shape
        shape = random.choice(SHAPES)
        # Return a new Tetromino object
        return Tetromino(self.width // 2, 0, shape)

接下来,valid_move() 方法会检查棋子是否能移动到指定位置:

    def valid_move(self, piece, x, y, rotation):
        """Check if the piece can move to the given position"""
        for i, row in enumerate(piece.shape[(piece.rotation + rotation) % len(piece.shape)]):
            for j, cell in enumerate(row):
                try:
                    if cell == 'O' and (self.grid[piece.y + i + y][piece.x + j + x] != 0):
                        return False
                except IndexError:
                    return False
        return True

clear_lines() 方法会清除已满的行,并返回已清除的行数,以便我们稍后计算分数:

    def clear_lines(self):
        """Clear the lines that are full and return the number of cleared lines"""
        lines_cleared = 0
        for i, row in enumerate(self.grid[:-1]):
            if all(cell != 0 for cell in row):
                lines_cleared += 1
                del self.grid[i]
                self.grid.insert(0, [0 for _ in range(self.width)])
        return lines_cleared

现在是锁定棋子的方法:

    def lock_piece(self, piece):
        """Lock the piece in place and create a new piece"""
        for i, row in enumerate(piece.shape[piece.rotation % len(piece.shape)]):
            for j, cell in enumerate(row):
                if cell == 'O':
                    self.grid[piece.y + i][piece.x + j] = piece.color
        # Clear the lines and update the score
        lines_cleared = self.clear_lines()
        self.score += lines_cleared * 100  # Update the score based on the number of cleared lines
        # Create a new piece
        self.current_piece = self.new_piece()
        # Check if the game is over
        if not self.valid_move(self.current_piece, 0, 0, 0):
            self.game_over = True
        return lines_cleared

update() 函数将四叶草向下移动一格:

    def update(self):
        """Move the tetromino down one cell"""
        if not self.game_over:
            if self.valid_move(self.current_piece, 0, 1, 0):
                self.current_piece.y += 1
            else:
                self.lock_piece(self.current_piece)

最后,我们制作了绘制游戏网格和当前棋子的方法:

    def draw(self, screen):
        """Draw the grid and the current piece"""
        for y, row in enumerate(self.grid):
            for x, cell in enumerate(row):
                if cell:
                    pygame.draw.rect(screen, cell, (x * GRID_SIZE, y * GRID_SIZE, GRID_SIZE - 1, GRID_SIZE - 1))

        if self.current_piece:
            for i, row in enumerate(self.current_piece.shape[self.current_piece.rotation % len(self.current_piece.shape)]):
                for j, cell in enumerate(row):
                    if cell == 'O':
                        pygame.draw.rect(screen, self.current_piece.color, ((self.current_piece.x + j) * GRID_SIZE, (self.current_piece.y + i) * GRID_SIZE, GRID_SIZE - 1, GRID_SIZE - 1))

为在游戏中绘制文本创建辅助函数

既然我们已经创建了俄罗斯方块类,那么让我们来创建两个绘制文本的辅助函数,一个用于显示得分,另一个用于显示 “Game Over!”:

def draw_score(screen, score, x, y):
    """Draw the score on the screen"""
    font = pygame.font.Font(None, 36)
    text = font.render(f"Score: {score}", True, WHITE)
    screen.blit(text, (x, y))
    
    
def draw_game_over(screen, x, y):
    """Draw the game over text on the screen"""
    font = pygame.font.Font(None, 48)
    text = font.render("Game Over", True, RED)
    screen.blit(text, (x, y))

这些函数需要四个参数(draw_game_over()需要三个):

  • screen:要绘制的 Pygame 表面。
  • score:当前游戏得分。
  • x:分数显示位置的 x 坐标。
  • y:分数显示位置的 y 坐标。

这些函数使用 pygame.font.Font(None, 36) 创建字体对象,使用 font.render() 方法渲染得分文本,然后使用 screen.blit() 将文本显示在屏幕上。

使游戏循环

在运行循环之前,我们首先初始化 Pygame,创建时钟对象,然后创建游戏屏幕和游戏,即俄罗斯方块对象:

def main():
    # Initialize pygame
    screen = pygame.display.set_mode((WIDTH, HEIGHT))
    pygame.display.set_caption('Tetris')
    # Create a clock object
    clock = pygame.time.Clock()
    # Create a Tetris object
    game = Tetris(WIDTH // GRID_SIZE, HEIGHT // GRID_SIZE)

    fall_time = 0
    fall_speed = 50  # You can adjust this value to change the falling speed, it's in milliseconds

您可以调整 fall_speed 来降低它(使其更快),或增加它使其更慢。

下面是游戏循环:

    while True:
        # Fill the screen with black
        screen.fill(BLACK) 
        for event in pygame.event.get():
            # Check for the QUIT event
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
            # Check for the KEYDOWN event
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_LEFT:
                    if game.valid_move(game.current_piece, -1, 0, 0):
                        game.current_piece.x -= 1 # Move the piece to the left
                if event.key == pygame.K_RIGHT:
                    if game.valid_move(game.current_piece, 1, 0, 0):
                        game.current_piece.x += 1 # Move the piece to the right
                if event.key == pygame.K_DOWN:
                    if game.valid_move(game.current_piece, 0, 1, 0):
                        game.current_piece.y += 1 # Move the piece down
                if event.key == pygame.K_UP:
                    if game.valid_move(game.current_piece, 0, 0, 1):
                        game.current_piece.rotation += 1 # Rotate the piece
                if event.key == pygame.K_SPACE:
                    while game.valid_move(game.current_piece, 0, 1, 0):
                        game.current_piece.y += 1 # Move the piece down until it hits the bottom
                    game.lock_piece(game.current_piece) # Lock the piece in place
        # Get the number of milliseconds since the last frame
        delta_time = clock.get_rawtime() 
        # Add the delta time to the fall time
        fall_time += delta_time 
        if fall_time >= fall_speed:
            # Move the piece down
            game.update()
            # Reset the fall time
            fall_time = 0
        # Draw the score on the screen
        draw_score(screen, game.score, 10, 10)
        # Draw the grid and the current piece
        game.draw(screen)
        if game.game_over:
            # Draw the "Game Over" message
            draw_game_over(screen, WIDTH // 2 - 100, HEIGHT // 2 - 30)  # Draw the "Game Over" message
            # You can add a "Press any key to restart" message here
            # Check for the KEYDOWN event
            if event.type == pygame.KEYDOWN:
                # Create a new Tetris object
                game = Tetris(WIDTH // GRID_SIZE, HEIGHT // GRID_SIZE)
        # Update the display
        pygame.display.flip()
        # Set the framerate
        clock.tick(60)

主游戏循环是一个 while 循环,不断执行以下步骤:

  1. 用黑色填充屏幕,清空屏幕。
  2. 处理 Pygame 事件,如退出游戏或处理移动和旋转俄罗斯方块棋子的键盘输入。
  3. 更新游戏状态,包括将当前棋子向下移动一行,并在棋子到达底部时将其锁定。
  4. 在屏幕上绘制当前分数、游戏网格和当前棋子。
  5. 如果游戏结束,显示 "游戏结束 "信息。
  6. 更新 Pygame 显示屏,并使用时钟对象控制帧频。

最后的话

就是这样!俄罗斯方块游戏的简单实现现在应该可以运行了。您可以运行脚本来玩游戏,并对游戏玩法、图形或控件进行任何所需的定制。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/793927.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【企业级监控】源码部署Zabbix与监控主机

Zabbix企业级分布式监控 文章目录 Zabbix企业级分布式监控资源列表基础环境一、LNMP环境搭建(在zbx主机上)1.1、配置Yum仓库1.1.1、下载阿里云的仓库文件1.2.2、安装PHP7的仓库1.2.3、生成Mariadb10.11的仓库文件1.2.4、快速重建Yum缓存 1.2、安装PHP7.4…

小巧低调的黑盒子,打造个性化音乐体验,欧尼士ONIX Alpha小尾巴上手

欧尼士ONIX的产品很有辨识度,这家来自英国的品牌,有着鲜明的黑金设计色彩,以及低调奢华的质感,当然最重要的是,欧尼士的音质表现非常出色,因此深受音乐爱好者的喜爱。在以手机等设备为载体的流媒体音乐盛行…

uniapp中使用uni-ui组件库

src目录下新建components目录从uni-ui引入对应的组件目录&#xff0c;如下图 直接使用组件&#xff0c;demo <template><view id"my" data-name"王五" data-age"18">my页面</view><uni-data-select :localdata"local…

WebDriver与浏览器通信的深度剖析与探索

在自动化测试的世界里&#xff0c;WebDriver无疑是连接测试脚本与浏览器之间的桥梁&#xff0c;它让复杂的自动化测试成为可能。本文将深入探讨WebDriver与浏览器之间的通信机制&#xff0c;揭示它们之间如何协同工作&#xff0c;以及这一过程中涉及的关键技术和挑战。 一、We…

sqlmap使用之-post注入、head注入(ua、cookie、referer)

1、post注入 1.1、方法一&#xff0c;通过保存数据包文件进行注入 bp抓包获取post数据 将数据保存到post.txt文件 加上-r指定数据文件 1.2、方法二、通过URL注入 D:\Python3.8.6\SQLmap>python sqlmap.py -u "http://localhost/login.php" --data "userna…

2024年06月CCF-GESP编程能力等级认证C++编程三级真题解析

本文收录于专栏《C等级认证CCF-GESP真题解析》&#xff0c;专栏总目录&#xff1a;点这里。订阅后可阅读专栏内所有文章。 一、单选题&#xff08;每题 2 分&#xff0c;共 30 分&#xff09; 第 1 题 小杨父母带他到某培训机构给他报名参加CCF组织的GESP认证考试的第1级&…

【JavaScript 报错】未捕获的范围错误:Uncaught RangeError

&#x1f525; 个人主页&#xff1a;空白诗 文章目录 一、错误原因分析1. 递归调用次数过多2. 数组长度超出限制3. 数值超出允许范围 二、解决方案1. 限制递归深度2. 控制数组长度3. 检查数值范围 三、实例讲解四、总结 Uncaught RangeError 是JavaScript中常见的一种错误&…

k8s集群离线部署

K8s离线部署 环境 目标 k8s离线部署 步骤 部署docker 详情见文章&#xff1a;《离线安装docker及后端项目离线打包》 https://blog.csdn.net/qq_45371023/article/details/140279746?spm1001.2014.3001.5501 所用到的所有文件在&#xff1a; 链接&#xff1a;https://pan…

9.Python学习:Socket

1.网络通信要素&#xff08;IP端口传输协议&#xff09; 2.Socket编程 2.1TCP、UDP协议了解 2.2 Socket流程 服务端有两个socket对象&#xff0c;客户端有一个 3.Socket实战 服务端代码&#xff1a; import socket #创建Socket对象 sksocket.socket() #绑定ip与端口号-使…

上机算法刷题暑期篇(一) —— AcWing 3692. 最长连续公共子序列(西电)

题目链接 AcWing 3692. 最长连续公共子序列 题目详情 题目解析 我们一看到题目,最长和连续子串&#xff0c;我们第一反应应该是什么?没错,就是dp,一般来说&#xff0c;子串问题常见的解法有两种&#xff1a; 双指针 dp 这道题无疑就是一道最常见的dp问题&#xff0c;而dp问…

vue + echart 饼形图

图表配置&#xff1a; import { EChartsOption, graphic } from echarts import rightCircle from /assets/imgs/index/right_circle.png export const pieOption: EChartsOption {title: {text: 100%,subtext: 游客加量,left: 19%,top: 42%,textStyle: {fontSize: 24,color:…

唐刘:当 SaaS 爱上 TiDB(一)- 行业挑战与 TiDB 的应对之道

导读 在 TiDB 8.1 发布后&#xff0c;TiDB 展现了强大的支持 SaaS 业务的能力&#xff0c;成为 SaaS 业务数据库的优先选择之一。 本文为“当 SaaS 爱上 TiDB”系列文章的第一篇&#xff0c;系列文章将从技术原理和真实用户体验两个角度深入探讨 TiDB 在 SaaS 业务中的表现&a…

kafka发送消息流程

配置props.put(ProducerConfig.PARTITIONER_CLASS_CONFIG, RoundRobinPartitioner.class); public Map<String,Object> producerConfigs(){Map<String,Object> props new HashMap<>();props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,bootstrapServers…

ISO/OIS的七层模型②

OSI模型是一个分层的模型&#xff0c;每一个部分称为一层&#xff0c;每一层扮演固定的角色&#xff0c;互不干扰。OSI有7层&#xff0c;从上到下分别是&#xff1a; 一&#xff0c;每层功能 7.应用层&#xff08;Application layer &#xff09;&#xff1a;应用层功能&#x…

计算机基础 进制转化

&#x1f4d1;打牌 &#xff1a; da pai ge的个人主页 &#x1f324;️个人专栏 &#xff1a; da pai ge的博客专栏 ☁️宝剑锋从磨砺出&#xff0c;梅花香自苦寒来 ☁️运维工程师的职责&#xff1a;监…

用Apipost压力测试接口

用Apipost压力测试接口 1.点击自动化测试 2.选择要测试的接口 3.如果没有接口&#xff0c;就先在api调试中添加要测试的接口 4.根据自己的需求设置相应的参数&#xff0c;这里我压测10次 5.这样就可以压测接口了&#xff0c;非常nice

c++ 建造者模式

文章目录 建造者模式为什么使用建造者模式建造者模式实现步骤实现示例建造者模式优缺点 建造者模式 建造者模式&#xff08;Builder Pattern&#xff09;是面向对象设计模式中的一种&#xff0c;主要用于创建复杂对象。这种模式将对象的构建过程与其表示分离&#xff0c;允许用…

maven6——生命周期与插件

生命周期 生命周期&#xff1a;指运行的阶段&#xff08;比如几岁&#xff09; maven有三个生命周期如下&#xff0c;每个生命周期大概做的事情如下&#xff1a; 注意&#xff1a;每次执行某个&#xff0c;他会把上面的都执行一遍 插件&#xff1a; 每一个插件&#xf…

【JavaScript 报错】未捕获的加载错误:Uncaught LoadError

&#x1f525; 个人主页&#xff1a;空白诗 文章目录 一、错误原因分析1. 资源路径错误2. 资源不存在3. 网络问题 二、解决方案1. 检查资源路径2. 确保资源存在3. 处理网络问题 三、实例讲解四、总结 在JavaScript应用程序中&#xff0c;未捕获的加载错误&#xff08;Uncaught …

Echarts实现github提交记录图

最近改个人博客&#xff0c;看了github的提交记录&#xff0c;是真觉得好看。可以移植到自己的博客上做文章统计 效果如下 代码如下 <!DOCTYPE html> <html lang"en" style"height: 100%"><head><meta charset"utf-8"> …