我有一个浮力模拟的工作代码,但它运行不正常。我有三个场景和预期行为如下:
- 对象初始位置已被淹没:(a(预计将向上移动;(b( 然后下降但不超过其先前的浸没深度;(c( 重复a-b,直到物体停止或漂浮在水面
- 对象的初始位置在表面级别:(a(保持在该表面级别或具有浮动效果
- 物体在水面以上的初始位置:(a(物体落入水中,直到达到一定深度;(b( 预计将上升;(c( 然后下降但不超过其先前的浸没深度;(d( 重复b-c,直到物体停止或漂浮在水面
要实现上述预期(特别是场景3(,您可以参考此视频(https://www.youtube.com/watch?v=Z_vfP_S5wis)并跳到00:06:00时间帧。
我还有如下代码:
import pygame
import sys
def get_overlapping_area(rect1, rect2):
overlap_width = min(rect1.right, rect2.right) - max(rect1.left, rect2.left)
overlap_height = min(rect1.bottom, rect2.bottom) - max(rect1.top, rect2.top)
return overlap_width * overlap_height
class Player(pygame.sprite.Sprite):
def __init__(self, pos):
super().__init__()
self.image = pygame.Surface((16, 32))
self.image.fill((0, 0, 0))
self.rect = self.image.get_rect(center=pos)
self.y_vel = 0
def apply_gravity(self):
self.y_vel += GRAVITY
def check_buoyancy_collisions(self):
buoyant_force = 0
for sprite in buoyant_group:
if sprite.rect.colliderect(self.rect):
submerged_area = get_overlapping_area(sprite.rect, self.rect)
buoyant_force -= sprite.buoyancy * GRAVITY * submerged_area
self.y_vel += buoyant_force
def update(self):
self.apply_gravity()
self.check_buoyancy_collisions()
self.rect.top += self.y_vel
def draw(self, screen: pygame.display):
screen.blit(self.image, self.rect)
class Fluid(pygame.sprite.Sprite):
def __init__(self, pos, tile_size):
super().__init__()
self.image = pygame.Surface((tile_size, tile_size))
self.image.fill((0, 255, 255))
self.rect = self.image.get_rect(topleft=pos)
self.buoyancy = 0.00225
pygame.init()
WIDTH, HEIGHT = 500, 700
screen = pygame.display.set_mode((WIDTH, HEIGHT))
TILE_SIZE = 32
GRAVITY = 0.05
buoyant_group = pygame.sprite.Group()
player_y = HEIGHT * 3 // 4 # player drop point few pixels below fluid surface
# player_y = HEIGHT // 4 # player drop point at fluid surface
# player_y = HEIGHT // 8 # player drop point few pixels above fluid surface
# Instantiate Player and Fluid objects
player = Player((WIDTH // 2, player_y))
for r in range(HEIGHT // 4, HEIGHT * 2, TILE_SIZE):
for c in range(0, WIDTH, TILE_SIZE):
buoyant_group.add(Fluid(pos=(c, r), tile_size=TILE_SIZE))
# Game Loop
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
player.update()
screen.fill((100, 100, 100))
buoyant_group.draw(surface=screen)
player.draw(screen=screen)
pygame.display.update()
代码的几种解释:
get_overlapping_area函数:浮力计算为流体重量密度x位移流体体积,由于我处理的是2D,因此计算的是物体和流体之间的重叠面积,而不是位移流体体积。
玩家等级:受重力和浮力影响的16x32精灵。它有一个check_buoyancy_closses函数,负责计算对象上的向上力。
流体类别:负责浮力的32x32精灵。其self.buoyancy属性的计算足以克服玩家对象因重力而产生的重量,从而产生作用在玩家对象上的向上合力。
player_y可变:我提供了3个设置来模拟上面提到的3个场景。您可以相应地对它们进行注释/取消注释。
附言:我已经讨论这个问题一个星期了。我找到的大多数资源都是关于这个理论的。剩下的少数资源使用为它们做物理处理的游戏引擎
最终找到了解决方案,尽管没有达到应有的完美。我不是物理学专家,但根据我的研究,除了浮力之外,还有三种阻力作用在移动/浸没体上,即(1(表面摩擦阻力、(2(形式阻力和(3(干扰阻力。
据我所知,皮肤摩擦阻力作用在身体表面,与运动方向平行;形状阻力作用在身体表面,垂直于运动方向;以及由流体流的组合产生的干涉阻力。
一般来说,这些阻力的作用与身体的运动方向相反。因此,为了简单起见,我们可以将它们概括为特定流体的阻力属性,如
class Fluid(pygame.sprite.Sprite):
def __init__(self, pos, tile_size):
super().__init__()
self.image = pygame.Surface((tile_size, tile_size))
self.image.fill((0, 255, 255))
self.rect = self.image.get_rect(topleft=pos)
self.buoyancy = 0.00215
self.drag = 0.0000080
移动/浸没物体的总阻力与物体的浸没面积成比例(与流体接触的面积(。最后,产生的阻力与身体运动的方向相反。
class Player(pygame.sprite.Sprite):
def check_buoyancy_collisions(self):
buoyant_force = 0
drag = 0
for sprite in buoyant_group:
if sprite.rect.colliderect(self.rect):
submerged_area = get_overlapping_area(sprite.rect, self.rect)
buoyant_force -= sprite.buoyancy * GRAVITY * submerged_area
drag += sprite.drag * submerged_area
self.y_vel += buoyant_force
if self.y_vel > 0:
self.y_vel -= drag
else:
self.y_vel += drag
这只适用于y方向的移动。我认为阻力也应该施加在身体在x方向上的运动上。
此外,对于复杂的模拟,可能必须分别应用这三种类型的拖动。