You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Python实现多小球边界弹跳动画(边界可匀速移动)

实现带移动边界的多小球无碰撞弹跳动画

看起来你已经迈出了很棒的第一步——从单个小球的边界移动开始尝试,这是拆解复杂动画需求的绝佳切入点!我会帮你把这个扩展到多小球无碰撞弹跳+移动高度边界的完整效果,咱们一步步来:

第一步:补全并完善单个小球的移动边界逻辑

先把你代码里没写完的norme函数补全,再添加移动边界的核心逻辑。这里我默认让上边界以恒定速度向下移动,你可以轻松调整为上移或下边界移动,逻辑是通用的:

import matplotlib.pyplot as plt
from matplotlib import animation
from matplotlib.widgets import Slider
import numpy as np

# 补全向量模长计算函数
def norme(vecteur):
    return np.linalg.norm(vecteur)

# 初始化参数
fig, ax = plt.subplots()
plt.subplots_adjust(bottom=0.25)

# 边界参数:初始上下边界、边界移动速度(负数表示向下)
y_min = 0
y_max_initial = 10
boundary_speed = -0.02
y_max = y_max_initial

# 小球参数
ball_radius = 0.3
ball_pos = np.array([5.0, 5.0])  # 初始位置
ball_vel = np.array([0.5, 0.3])  # 初始速度

# 绘制小球和边界
ball, = ax.plot(ball_pos[0], ball_pos[1], 'o', color='blue', markersize=ball_radius*10)
top_boundary, = ax.plot([0, 10], [y_max, y_max], 'r--')
bottom_boundary, = ax.plot([0, 10], [y_min, y_min], 'r--')

# 设置坐标轴范围
ax.set_xlim(0, 10)
ax.set_ylim(y_min - 1, y_max_initial + 1)

# 添加边界速度调节滑块
ax_slider = plt.axes([0.25, 0.1, 0.65, 0.03])
speed_slider = Slider(ax=ax_slider, label='边界速度', valmin=-0.1, valmax=0.1, valinit=boundary_speed)

def update_boundary_speed(val):
    global boundary_speed
    boundary_speed = val

speed_slider.on_changed(update_boundary_speed)

def update(frame):
    global ball_pos, ball_vel, y_max

    # 更新移动边界的位置
    y_max += boundary_speed
    top_boundary.set_ydata([y_max, y_max])

    # 更新小球位置
    ball_pos += ball_vel

    # 边界碰撞检测与反弹
    # 左右边界反弹
    if ball_pos[0] - ball_radius < 0 or ball_pos[0] + ball_radius > 10:
        ball_vel[0] *= -1
    # 上下边界反弹(注意上边界是动态移动的)
    if ball_pos[1] - ball_radius < y_min:
        ball_vel[1] *= -1
    if ball_pos[1] + ball_radius > y_max:
        ball_vel[1] *= -1

    # 更新小球显示位置
    ball.set_xdata(ball_pos[0])
    ball.set_ydata(ball_pos[1])

    return ball, top_boundary, bottom_boundary

# 创建并启动动画
ani = animation.FuncAnimation(fig, update, frames=1000, interval=20, blit=True)
plt.show()

第二步:扩展到多小球并添加无碰撞逻辑

要实现多小球互不碰撞,我们需要在每帧更新时检测任意两个小球的距离——如果距离小于两个半径之和,就按照弹性碰撞的规则(动量守恒+动能守恒)更新它们的速度:

import matplotlib.pyplot as plt
from matplotlib import animation
from matplotlib.widgets import Slider
import numpy as np

def norme(vecteur):
    return np.linalg.norm(vecteur)

# 初始化参数
fig, ax = plt.subplots()
plt.subplots_adjust(bottom=0.25)

# 边界参数
y_min = 0
y_max_initial = 10
boundary_speed = -0.02
y_max = y_max_initial

# 多小球参数
num_balls = 5
ball_radius = 0.3
# 随机初始化小球位置和速度(避免初始重叠)
ball_positions = np.random.uniform(ball_radius, 10 - ball_radius, (num_balls, 2))
ball_velocities = np.random.uniform(-0.5, 0.5, (num_balls, 2))
# 存储所有小球的绘图对象
balls = []
for pos in ball_positions:
    ball, = ax.plot(pos[0], pos[1], 'o', markersize=ball_radius*10)
    balls.append(ball)

# 绘制边界
top_boundary, = ax.plot([0, 10], [y_max, y_max], 'r--')
bottom_boundary, = ax.plot([0, 10], [y_min, y_min], 'r--')

ax.set_xlim(0, 10)
ax.set_ylim(y_min - 1, y_max_initial + 1)

# 边界速度调节滑块
ax_slider = plt.axes([0.25, 0.1, 0.65, 0.03])
speed_slider = Slider(ax=ax_slider, label='边界速度', valmin=-0.1, valmax=0.1, valinit=boundary_speed)

def update_boundary_speed(val):
    global boundary_speed
    boundary_speed = val

speed_slider.on_changed(update_boundary_speed)

def handle_ball_collision(i, j):
    # 计算两个小球的相对位置和速度
    pos_diff = ball_positions[i] - ball_positions[j]
    vel_diff = ball_velocities[i] - ball_velocities[j]
    distance = norme(pos_diff)

    # 检测是否发生碰撞(距离小于两倍半径)
    if distance < 2 * ball_radius:
        # 计算碰撞法线方向
        normal = pos_diff / distance
        # 提取速度在法线上的分量
        vel_normal_i = np.dot(ball_velocities[i], normal)
        vel_normal_j = np.dot(ball_velocities[j], normal)

        # 弹性碰撞:交换法向速度分量(假设所有小球质量相同)
        ball_velocities[i] -= (vel_normal_i - vel_normal_j) * normal
        ball_velocities[j] -= (vel_normal_j - vel_normal_i) * normal

def update(frame):
    global ball_positions, ball_velocities, y_max

    # 更新移动边界
    y_max += boundary_speed
    top_boundary.set_ydata([y_max, y_max])

    # 更新所有小球的位置
    ball_positions += ball_velocities

    # 边界碰撞检测与反弹
    for i in range(num_balls):
        # 左右边界反弹
        if ball_positions[i, 0] - ball_radius < 0 or ball_positions[i, 0] + ball_radius > 10:
            ball_velocities[i, 0] *= -1
        # 上下边界反弹
        if ball_positions[i, 1] - ball_radius < y_min:
            ball_velocities[i, 1] *= -1
        if ball_positions[i, 1] + ball_radius > y_max:
            ball_velocities[i, 1] *= -1

    # 小球之间的碰撞检测(只检测i<j的组合,避免重复计算)
    for i in range(num_balls):
        for j in range(i + 1, num_balls):
            handle_ball_collision(i, j)

    # 更新所有小球的显示位置
    for i in range(num_balls):
        balls[i].set_xdata(ball_positions[i, 0])
        balls[i].set_ydata(ball_positions[i, 1])

    return *balls, top_boundary, bottom_boundary

ani = animation.FuncAnimation(fig, update, frames=1000, interval=20, blit=True)
plt.show()

关键细节说明

  • 移动边界处理:每帧直接修改上边界的y坐标,同时检测小球是否碰到更新后的边界,碰到就触发反弹逻辑。
  • 小球碰撞处理:默认假设所有小球质量相同,弹性碰撞时交换法向速度分量;如果需要不同质量,只需要修改碰撞后的速度计算公式即可。
  • 性能优化:碰撞检测时只遍历i < j的组合,避免重复检测;如果小球数量很多,可以考虑空间分区算法(比如网格划分)减少检测次数。

你可以根据需求自由调整小球数量、初始速度、边界移动方向/速度,甚至添加更多交互控件(比如暂停按钮、小球速度调节滑块等)。

内容的提问来源于stack exchange,提问作者Badr Al-kebsi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 04:00:40