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

huangapple go评论51阅读模式
英文:

Ball bounce physics, system seems to gain energy (bounce higher)

问题

Here is the translated code portion:

我正在尝试使用pygame在python中建模球的弹跳模式某些因素导致物理效应不正确 - 球获得了额外的能量也就是说它们会随时间略微弹得更高 我已经包括了一个速度因子”,可以增加以查看我描述的效应。)

以下是我的代码

import pygame
import math

# 窗口尺寸
WIDTH = 800
HEIGHT = 600

# 圆的属性
RADIUS = 5
NUM_BALLS = 20
GRAVITY = 9.81  # 重力加速度,单位是m/s²
SPEED_FACTOR = 10  # 动画速度乘数

# 圆类
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):
        # 更新位置
        self.x += self.vx * dt
        self.y += self.vy * dt

        # 应用重力
        self.vy += GRAVITY * dt

        # 反弹
        if self.x - RADIUS < 0 or self.x + RADIUS > WIDTH:
            self.vx *= -1
            self.x = max(RADIUS, min(WIDTH - RADIUS, self.x))  # 将x限制在边界内
        if self.y - RADIUS < 0 or self.y + RADIUS > HEIGHT:
            self.vy *= -1
            self.y = max(RADIUS, min(HEIGHT - RADIUS, self.y))  # 将y限制在边界内

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

# 初始化Pygame
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))

circles = []

# 计算圆的排列
circle_radius = RADIUS * 2  # 单个球的直径
circle_diameter = NUM_BALLS * circle_radius  # 圆排列的直径
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)  # 根据球的数量计算色调值
    color = pygame.Color(0)
    color.hsla = (hue, 100, 50, 100)  # 使用HSL颜色空间设置颜色
    circles.append(Circle(x, y, vx, vy, color))

# 游戏循环
running = True
clock = pygame.time.Clock()
prev_time = pygame.time.get_ticks()  # 上一帧的时间

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  # 自上一帧以来经过的时间(以秒为单位)
    dt *= SPEED_FACTOR  # 将dt乘以速度因子

    prev_time = current_time  # 更新上一帧的时间

    screen.fill((0, 0, 0))  # 清空屏幕

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

    pygame.display.flip()  # 更新屏幕

pygame.quit()

关于重力因素的工作原理,我不完全理解,但假设它只是一个加速度。额外的能量是从哪里进入系统的,我也不太清楚。

英文:

I'm attempting to model the bouncing of balls in a pattern in python with pygame. Something is causing the physics to be incorrect - the balls GAIN energy, i.e. they bounce fractionally higher over time. (I have included a 'speed factor' which can be increase to see the effect I describe.)

Here is my code:

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&#178;
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 &lt; 0 or self.x + RADIUS &gt; WIDTH:
self.vx *= -1
self.x = max(RADIUS, min(WIDTH - RADIUS, self.x))  # Clamp x within bounds
if self.y - RADIUS &lt; 0 or self.y + RADIUS &gt; 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()

I don't fully understand how the gravity factor works, but assume it's just an acceleration. Where is the extra energy coming in to the system?

答案1

得分: 2

当你将球“夹住”在边缘位置时,你稍微向上移动了它。理论上,像这样弹跳的完美球在相同的y坐标上向上和向下具有相同大小的速度,但你改变了相同速度的位置。在回升的过程中,球会领先一点点,可以稍微更高一点。

为了解决这个问题,你可以进行某种计算,以确定在这个小距离内由于重力而失去了多少速度,并相应地减小你的vy,但可能有更好的方法来做这件事。

编辑

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

英文:

When you 'clamp' the ball to the position on the edge, you're moving it up slightly. Theoretically, a perfect ball bouncing like this would have the same magnitude of velocity going up and down for the same y-coordinate, but you change the position for the same velocity. On the way back up, the ball gets a head-start, and can go ever so slightly higher.

To solve this, you could do some sort of calculation to determine how much velocity would be lost to gravity in that small distance and diminish your vy accordingly, but there's probably a better way to do it

EDIT

another solution would be to leave the y coordinate as-is and simply change the shape of the ball into an appropriately sized ellipse so that it falls within the boundary. Then change it back into a circle when it no longer intersects a boundary. you would need to have sufficiently short time steps, and a limit on velocity or else your ellipses might become very weird.

答案2

得分: 1

alex_danielssen的答案 提到了模拟中能量增加的原因

当你将球“夹住”在边缘位置时,你会轻微地将它向上移动。

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

假设当弹跳因为self.y - RADIUS = -1(这是&lt; 0)而触发时,我们有self.vy = -10。此时,self.yRADIUS - 1。在处理弹跳后,你将self.vy = 10,并且self.y = RADIUS。球被提高了1个单位,超过了其以前的位置,因此具有更多的势能,但其动能与较低时相同。这就是你的神奇能量增加。

要修复这个问题,你只需要从能量守恒中正确计算其速度。

if self.y - RADIUS &lt;= 0 or self.y + RADIUS &gt;= HEIGHT:
    # 能量 = 势能             + 动能           (每单位质量)
    energy = GRAVITY * self.y + 0.5 * self.vy**2
    # 更新位置
    self.y = max(RADIUS, min(HEIGHT - RADIUS, self.y))  # 在边界内夹住y
    # vy的方向(1 = 上,-1 = 下)
    vy_direction = self.vy / abs(self.vy)
    # 从能量和新位置重新计算速度,翻转符号
    self.vy = -vy_direction * (2 * (energy - GRAVITY * self.y))**0.5
英文:

alex_danielssen's answer touches on the reason your simulation gains energy

> When you 'clamp' the ball to the position on the edge, you're moving it up slightly.

Consider what happens when a bounce is triggered at the floor (this applies at any wall, but let's just think about the floor for now):

Suppose that when the bounce was triggered because self.y - RADIUS = -1 (which is &lt; 0), we had self.vy = -10. At this point in time, self.y was RADIUS - 1. After you processed the bounce, you set self.vy = 10, and self.y = RADIUS. The ball was raised 1 unit above its previous position so it has more potential energy, but it still has the same kinetic energy as it had when it was lower. Here is your magical gain of energy.

To fix this, you just need to correctly calculate its velocity from energy conservation.

if self.y - RADIUS &lt;= 0 or self.y + RADIUS &gt;= 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

答案3

得分: -1

Your problem我认为是您正在进行一个完全弹性碰撞。我的意思是,您的垂直速度只是在撞击边缘时乘以-1的因子。这样做的结果是球永远不会失去垂直速度。

尝试将vy代码更改为以下内容:

if self.y - RADIUS < 0 or self.y + RADIUS >= HEIGHT: # 您不希望它等于高度。它应该严格小于。
    self.vy *= -0.95 # 而不是1的因子。这将减小球的弹跳高度。
    self.y = max(RADIUS, min(HEIGHT - RADIUS, self.y))  # 将y限制在范围内
英文:

Your problem I think is the fact that you are doing a perfectly elastic collision. What I mean is that your vertical velocity is just multiplied by a factor of -1 on hitting the edges. What this does is that ball never looses vertical velocity.

Try changing the vy code to this:

if self.y - RADIUS &lt; 0 or self.y + RADIUS &gt;= HEIGHT: # You don&#39;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

huangapple
  • 本文由 发表于 2023年5月11日 05:12:21
  • 转载请务必保留本文链接:https://go.coder-hub.com/76222588.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定