球弹跳物理学,系统似乎获得能量(弹跳得更高)

问题描述 投票:0回答:4

我正在尝试使用 pygame 在 python 中以某种模式对球的弹跳进行建模。某些因素导致物理学不正确——球获得能量,即随着时间的推移,它们的弹跳会稍微更高。 (我添加了一个“速度系数”,可以增加该系数来查看我描述的效果。)

这是我的代码:

import pygame
import math

# Window dimensions
WIDTH = 800
HEIGHT = 600

# Circle properties
RADIUS = 5
NUM_BALLS = 20
GRAVITY = 9.81  # Gravitational acceleration in m/s²
SPEED_FACTOR = 10  # Speed multiplier for animation

# Circle class
class Circle:
    def __init__(self, x, y, vx, vy, color):
        self.x = x
        self.y = y
        self.vx = vx
        self.vy = vy
        self.color = color

    def update(self, dt):
        # Update positions
        self.x += self.vx * dt
        self.y += self.vy * dt

        # Apply gravity
        self.vy += GRAVITY * dt

        # Bounce off walls
        if self.x - RADIUS < 0 or self.x + RADIUS > WIDTH:
            self.vx *= -1
            self.x = max(RADIUS, min(WIDTH - RADIUS, self.x))  # Clamp x within bounds
        if self.y - RADIUS < 0 or self.y + RADIUS > HEIGHT:
            self.vy *= -1
            self.y = max(RADIUS, min(HEIGHT - RADIUS, self.y))  # Clamp y within bounds

    def draw(self, screen):
        pygame.draw.circle(screen, self.color, (int(self.x), int(self.y)), RADIUS)

# Initialize Pygame
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))

circles = []

# Calculate circle arrangement
circle_radius = RADIUS * 2  # Diameter of an individual ball
circle_diameter = NUM_BALLS * circle_radius  # Diameter of the circle arrangement
circle_center_x = WIDTH // 2
circle_center_y = HEIGHT // 2
angle_increment = 2 * math.pi / NUM_BALLS

for i in range(NUM_BALLS):
    angle = i * angle_increment
    x = circle_center_x + math.cos(angle) * circle_diameter / 2
    y = circle_center_y + math.sin(angle) * circle_diameter / 2
    vx = 0
    vy = 0
    hue = i * (360 // NUM_BALLS)  # Calculate hue value based on the number of balls
    color = pygame.Color(0)
    color.hsla = (hue, 100, 50, 100)  # Set color using HSL color space
    circles.append(Circle(x, y, vx, vy, color))

# Game loop
running = True
clock = pygame.time.Clock()
prev_time = pygame.time.get_ticks()  # Previous frame time

while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    current_time = pygame.time.get_ticks()
    dt = (current_time - prev_time) / 1000.0  # Time elapsed in seconds since the last frame
    dt *= SPEED_FACTOR  # Multiply dt by speed factor

    prev_time = current_time  # Update previous frame time

    screen.fill((0, 0, 0))  # Clear the screen

    for circle in circles:
        circle.update(dt)
        circle.draw(screen)

    pygame.display.flip()  # Update the screen

pygame.quit()

我不完全理解重力因子是如何工作的,但假设它只是一个加速度。额外的能量从哪里进入系统?

python pygame physics energy conservation-laws
4个回答
2
投票

当您将球“夹”到边缘位置时,您会稍微向上移动它。理论上,像这样弹跳的完美球对于相同的 y 坐标将具有相同大小的上下速度,但您可以更改相同速度的位置。在返回的过程中,球取得了领先,并且可以稍微更高。

为了解决这个问题,您可以进行某种计算来确定在这么短的距离内重力会损失多少速度,并相应地减少您的

vy
,但可能有更好的方法来做到这一点

编辑

另一种解决方案是保持 y 坐标不变,只需将球的形状更改为适当大小的椭圆形,使其落在边界内。当它不再与边界相交时,将其变回圆形。你需要有足够短的时间步长,并且限制速度,否则你的椭圆可能会变得非常奇怪。


1
投票

alex_danielssen 的答案涉及模拟获得能量的原因

当您将球“夹”到边缘位置时,您会稍微向上移动它。

考虑在地板上触发反弹时会发生什么(这适用于任何墙壁,但现在我们只考虑地板):

假设当因为

self.y - RADIUS = -1
(即
< 0
)触发反弹时,我们有
self.vy = -10
。此时,
self.y
RADIUS - 1
。处理反弹后,您设置
self.vy = 10
self.y = RADIUS
。球被提升到高于其先前位置
1
单位,因此它具有更多的势能,但它仍然具有与较低时相同的动能。这是你神奇的能量增益。

要解决这个问题,您只需根据能量守恒正确计算其速度即可。

if self.y - RADIUS <= 0 or self.y + RADIUS >= HEIGHT:
    # Energy = potential.     + kinetic          ( per unit mass )
    energy = GRAVITY * self.y + 0.5 * self.vy**2
    # Update location
    self.y = max(RADIUS, min(HEIGHT - RADIUS, self.y))  # Clamp y within bounds
    # Direction of vy (1 = up, -1 = down)
    vy_direction = self.vy / abs(self.vy)
    # Recalculate velocity from energy and new location, flip sign
    self.vy = -vy_direction * (2 * (energy - GRAVITY * self.y))**0.5

0
投票

我发现在碰撞检查之前应用一半的重力,在碰撞检查之后应用一半的重力基本上解决了我的问题。

    def update(self, dt):
    # Update positions
    self.x += self.vx * dt
    self.y += self.vy * dt

    # Apply gravity step 1
    self.vy += GRAVITY/2 * dt

    # Bounce off walls
    if self.x - RADIUS < 0 or self.x + RADIUS > WIDTH:
        self.vx *= -1
        self.x = max(RADIUS, min(WIDTH - RADIUS, self.x))  # Clamp x within bounds
    if self.y - RADIUS < 0 or self.y + RADIUS > HEIGHT:
        self.vy *= -1
        self.y = max(RADIUS, min(HEIGHT - RADIUS, self.y))  # Clamp y within bounds

    # Apply gravity step 2
    self.vy += GRAVITY/2 * dt

-1
投票

我认为你的问题在于你正在进行完美弹性碰撞。我的意思是,当你击中边缘时,你的垂直速度只是乘以-1。这样做的作用是球永远不会失去垂直速度。

尝试将 vy 代码更改为:

if self.y - RADIUS < 0 or self.y + RADIUS >= HEIGHT: # You don't want it to equal the height. It should be strictly less.
    self.vy *= -0.95 # Instead of a factor of 1. This will dampen the ball thereby reducing the max height it can go.
    self.y = max(RADIUS, min(HEIGHT - RADIUS, self.y))  # Clamp y within bounds
© www.soinside.com 2019 - 2024. All rights reserved.