Python使用Pygeme实现小球自由落体

        Pygame 是一个开源的 Python 库,专门用于开发 2D 游戏和多媒体应用程序。它基于 Simple DirectMedia Layer (SDL) 库构建,提供了对图形、声音、输入设备(如键盘、鼠标和游戏手柄)等功能的简单访问。Pygame 非常适合初学者学习游戏开发,同时也足够强大,可以用于创建复杂的游戏项目。


1、Pygame 的主要特点

  1. 跨平台支持
    Pygame 可以在 Windows、macOS、Linux 等操作系统上运行。

  2. 简单易用
    Pygame 的 API 设计简洁,适合初学者快速上手。

  3. 丰富的功能

    • 图形绘制(形状、图像、文本)

    • 声音播放(背景音乐、音效)

    • 输入处理(键盘、鼠标、游戏手柄)

    • 碰撞检测

    • 动画和精灵管理

  4. 活跃的社区
    Pygame 拥有一个活跃的社区,提供了大量的教程、示例和第三方工具。

  5. 免费和开源
    Pygame 是完全免费且开源的,遵循 LGPL 许可证。

  6. 优、缺点

    • 简单易学,适合初学者。跨平台支持。

    • 性能有限,不适合开发复杂的 3D 游戏。

    • 对于大型项目,可能需要手动优化性能。

    • 官方文档虽然全面,但部分内容可能不够详细。

    • 功能丰富,足以开发 2D 游戏。


2、Pygame 的核心模块

Pygame 由多个模块组成,每个模块负责不同的功能:

模块名称功能描述
pygame.display管理窗口和屏幕显示,支持全屏模式和硬件加速。
pygame.event处理用户输入事件(如键盘、鼠标、窗口关闭等)。
pygame.draw绘制基本图形(如矩形、圆形、线条等)。
pygame.image加载和保存图像文件(如 PNG、JPG 等)。
pygame.mixer播放声音和音乐,支持 WAV、MP3、OGG 等格式。
pygame.font渲染文本,支持 TrueType 字体。
pygame.time管理时间和帧率控制。
pygame.sprite提供精灵(Sprite)和精灵组(Group)功能,用于管理游戏对象。
pygame.key处理键盘输入。
pygame.mouse处理鼠标输入。
pygame.joystick处理游戏手柄输入。

3、Pygame 的基本结构

一个典型的 Pygame 程序通常包括以下步骤:

  1. 初始化 Pygame
    使用 pygame.init() 初始化所有模块。

  2. 创建窗口
    使用 pygame.display.set_mode() 创建一个窗口。

  3. 游戏主循环
    游戏的核心逻辑通常在一个循环中运行,直到用户退出游戏。

  4. 处理事件
    使用 pygame.event.get() 获取用户输入事件(如键盘、鼠标)。

  5. 更新游戏状态
    根据输入和游戏逻辑更新游戏对象的状态(如位置、速度等)。

  6. 绘制图形
    使用 pygame.draw 或 pygame.image 绘制图形和图像。

  7. 更新显示
    使用 pygame.display.flip() 或 pygame.display.update() 更新屏幕显示。

  8. 控制帧率
    使用 pygame.time.Clock().tick(FPS) 控制游戏的帧率。

  9. 退出游戏
    使用 pygame.quit() 退出 Pygame 并释放资源。


4、Pygame 应用场景

  1. 2D 游戏开发
    Pygame 非常适合开发 2D 游戏,如平台游戏、射击游戏、益智游戏等。

  2. 教育和学习
    Pygame 是学习游戏开发和编程的绝佳工具,许多学校和课程使用它来教授编程。

  3. 原型开发
    开发者可以使用 Pygame 快速构建游戏原型,验证游戏机制。

  4. 多媒体应用程序
    Pygame 可以用于开发简单的多媒体应用程序,如图像查看器、音乐播放器等。

5、安装 Pygame

首先,你需要安装 Pygame。如果你还没有安装,可以使用 pip 来安装:

pip install pygame

6、简单的 Pygame 示例

import pygame
import math
import sys

# 初始化pygame
pygame.init()

# 设置屏幕参数
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))

# 颜色定义
BACKGROUND_COLOR = (255, 255, 255)
HEXAGON_COLOR = (0, 0, 0)
BALL_COLOR = (255, 0, 0)

# 六边形参数
HEX_RADIUS = 200            # 六边形外接圆半径
HEX_CENTER = (SCREEN_WIDTH//2, SCREEN_HEIGHT//2)
ROTATION_SPEED = 1          # 旋转速度(度/帧)

# 小球参数
BALL_RADIUS = 20
BALL_MASS = 1.0
GRAVITY = 0.5               # 重力加速度(向下)
RESTITUTION = 0.8           # 碰撞弹性系数(0-1)

class RotatingHexagon:
    def __init__(self, center, radius):
        self.center = center
        self.radius = radius
        self.angle = 0       # 当前旋转角度(度)

    def get_vertices(self):
        """计算当前旋转角度下的六个顶点坐标"""
        return [
            (
                self.center[0] + self.radius * math.cos(math.radians(60*i + self.angle)),
                self.center[1] + self.radius * math.sin(math.radians(60*i + self.angle))
            )
            for i in range(6)
        ]

    def rotate(self):
        """更新旋转角度"""
        self.angle = (self.angle + ROTATION_SPEED) % 360

class Ball:
    def __init__(self, position, radius):
        self.pos = list(position)
        self.vel = [0.0, 0.0]
        self.radius = radius

    def apply_force(self, force):
        """应用外力(如重力)"""
        self.vel[0] += force[0] / BALL_MASS
        self.vel[1] += force[1] / BALL_MASS

    def update_position(self):
        """更新位置"""
        self.pos[0] += self.vel[0]
        self.pos[1] += self.vel[1]

def closest_point_on_segment(p, a, b):
    """计算点p到线段ab的最近点"""
    ap = (p[0]-a[0], p[1]-a[1])
    ab = (b[0]-a[0], b[1]-a[1])
    
    t = (ap[0]*ab[0] + ap[1]*ab[1]) / (ab[0]**2 + ab[1]**2 + 1e-6)
    t = max(0, min(1, t))
    
    return (
        a[0] + t * ab[0],
        a[1] + t * ab[1]
    )

def resolve_collision(ball, hex_vertices):
    """处理小球与六边形边的碰撞"""
    for i in range(6):
        # 获取当前边和法向量
        a = hex_vertices[i]
        b = hex_vertices[(i+1)%6]
        
        # 计算线段方向向量
        edge_dir = (b[0]-a[0], b[1]-a[1])
        edge_length = math.hypot(edge_dir[0], edge_dir[1])
        if edge_length < 1e-6:
            continue
        
        # 计算法向量(指向六边形内部)
        normal = (-edge_dir[1]/edge_length, edge_dir[0]/edge_length)
        
        # 计算小球中心到线段的最短距离
        closest = closest_point_on_segment(ball.pos, a, b)
        distance = math.hypot(ball.pos[0]-closest[0], ball.pos[1]-closest[1])
        
        # 如果发生穿透
        if distance < BALL_RADIUS:
            # 计算穿透深度
            penetration = BALL_RADIUS - distance
            
            # 修正位置:将小球推回内部
            ball.pos[0] += normal[0] * penetration
            ball.pos[1] += normal[1] * penetration
            
            # 计算速度的垂直分量(沿法线方向)
            vel_normal = ball.vel[0]*normal[0] + ball.vel[1]*normal[1]
            
            # 如果速度方向朝外,则进行反弹
            if vel_normal < 0:
                # 应用弹性碰撞
                ball.vel[0] -= (1 + RESTITUTION) * vel_normal * normal[0]
                ball.vel[1] -= (1 + RESTITUTION) * vel_normal * normal[1]

# 初始化对象
hexagon = RotatingHexagon(HEX_CENTER, HEX_RADIUS)
ball = Ball(HEX_CENTER, BALL_RADIUS)

# 主循环
clock = pygame.time.Clock()
running = True
while running:
    # 事件处理
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    # 物理更新 - - - - - - - - - - - - - - - - - - -
    
    # 应用重力(全局向下)
    ball.apply_force((0, GRAVITY * BALL_MASS))
    
    # 更新小球位置
    ball.update_position()
    
    # 旋转六边形
    hexagon.rotate()
    current_vertices = hexagon.get_vertices()
    
    # 处理碰撞
    resolve_collision(ball, current_vertices)
    
    # 图形绘制 - - - - - - - - - - - - - - - - - - -
    screen.fill(BACKGROUND_COLOR)
    
    # 绘制六边形
    pygame.draw.polygon(screen, HEXAGON_COLOR, current_vertices, 2)
    
    # 绘制小球
    pygame.draw.circle(screen, BALL_COLOR, 
                      (int(ball.pos[0]), int(ball.pos[1])),
                      BALL_RADIUS)
    
    pygame.display.flip()
    clock.tick(60)

pygame.quit()
sys.exit()

6.1、关键改进说明

  1. 物理精确的碰撞处理

    • 使用矢量数学计算小球到每条边的最短距离。

    • 当检测到穿透时,先修正小球位置(推回到六边形内部),再进行速度反弹计算。

    • 法向量始终指向六边形内部,确保碰撞响应方向正确。

  2. 能量守恒与损耗

    • 通过RESTITUTION参数控制碰撞能量损失(0.8表示每次碰撞保留80%的动能)。

    • 只有当速度方向朝外时才触发反弹,避免"粘墙"现象。

  3. 旋转坐标系处理

    • 六边形的旋转直接通过顶点坐标更新实现,无需额外坐标变换。

    • 每次碰撞检测都基于当前帧的六边形实际位置,确保旋转时的准确性。

  4. 重力作用

    • 重力始终作用在全局向下方向(屏幕坐标系),与六边形旋转无关。

    • 六边形旋转时,其边的位置变化会自然影响小球的运动轨迹。


6.2、运行效果

  1. 初始状态
    小球静止在六边形中心,六边形开始旋转。

  2. 重力作用
    小球向下加速,碰到六边形的底边后反弹。

  3. 旋转影响

    • 当六边形旋转时,原本的"底边"位置改变,导致小球继续向新的最低点运动。

    • 小球会在六边形内不断滚动,形成动态平衡。

  4. 能量衰减
    由于碰撞能量损失,小球最终会在某个边的附近做微小振动。


6.3、参数调整建议

参数说明示例值
ROTATION_SPEED六边形旋转速度(度/帧)1-5
GRAVITY重力强度(建议范围0.1-2.0)0.5
RESTITUTION碰撞弹性(0.0-1.0,1为完全弹性)0.8
BALL_RADIUS小球大小(需小于六边形半径)20
HEX_RADIUS六边形大小(需远大于小球半径)200

6.4、常见问题解决

Q1: 小球有时会轻微穿透边线

  • 原因:离散时间步长导致的积分误差。

  • 解决:可以减小时间步长(提高帧率),或增加碰撞检测的迭代次数。

Q2: 快速旋转时碰撞响应不稳定

  • 原因:六边形旋转速度过快,单帧位移过大。

  • 解决:降低ROTATION_SPEED或提高帧率(clock.tick(60)改为更高值)。

Q3: 小球能量逐渐增加

  • 原因:六边形旋转带入能量(类似离心机效应)。

  • 解决:这是预期行为,可通过减小RESTITUTION来抑制。


此版本实现了小球在旋转六边形内的真实物理行为,如需进一步优化,可以考虑添加摩擦力或空气阻力项。

7、总结

Pygame 是一个功能强大且易于上手的 2D 游戏开发库,适合初学者和中级开发者。虽然它在性能上可能不如一些专业的游戏引擎(如 Unity 或 Godot),但它仍然是学习游戏开发和快速原型设计的绝佳工具。如果你对游戏开发感兴趣,Pygame 是一个非常好的起点!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

qq_755682240

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

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

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

打赏作者

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

抵扣说明:

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

余额充值