Физика отскока мяча, система, кажется, набирает энергию (отскок выше)

Я пытаюсь смоделировать подпрыгивание мячей по шаблону на Python с помощью pygame. Что-то является причиной неправильной физики - шары ПОЛУЧАЮТ энергию, то есть с течением времени они подпрыгивают немного выше. (Я включил «коэффициент скорости», который можно увеличить, чтобы увидеть эффект, который я описываю.)

Вот мой код:

      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()

Я не до конца понимаю, как работает фактор гравитации, но предполагаю, что это просто ускорение. Откуда в систему поступает дополнительная энергия?

3 ответа

Когда вы «зажимаете» мяч в положении на краю, вы слегка перемещаете его вверх. Теоретически идеальный мяч, подпрыгивающий таким образом, будет иметь одинаковую скорость при движении вверх и вниз по одной и той же координате Y, но вы меняете положение для той же скорости. На обратном пути мяч получает преимущество и может подняться немного выше.

Чтобы решить эту проблему, вы могли бы провести своего рода расчет, чтобы определить, какая скорость будет потеряна из-за гравитации на этом небольшом расстоянии, и уменьшить вашу скорость.vyсоответственно, но, вероятно, есть лучший способ сделать это

РЕДАКТИРОВАТЬ

Другим решением было бы оставить координату y как есть и просто изменить форму шара на эллипс подходящего размера, чтобы он попадал в пределы границы. Затем измените его обратно на круг, когда он больше не пересекает границу. вам нужно будет иметь достаточно короткие временные шаги и ограничение скорости, иначе ваши эллипсы могут стать очень странными.

Ответ 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

Я думаю, ваша проблема заключается в том, что вы совершаете совершенно упругое столкновение. Я имею в виду, что ваша вертикальная скорость просто умножается на коэффициент -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
Другие вопросы по тегам