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