鱼C论坛

 找回密码
 立即注册
查看: 2124|回复: 20

[已解决]Pygame模拟天体运动问题

[复制链接]
发表于 2023-7-24 10:43:08 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
代码如下:

  1. import pygame
  2. pygame.init()
  3. pygame.key.set_repeat(1000, 50)

  4. G = 6

  5. class StarObject:
  6.     def __init__(self, x, y, vx, vy, mass):
  7.         self.x = x
  8.         self.y = y
  9.         self.vx = vx
  10.         self.vy = vy
  11.         self.mass = mass

  12.     @property
  13.     def info(self):
  14.         return self.x, self.y, self.vx, self.vy, self.mass

  15. class StarSprite(pygame.sprite.Sprite):
  16.     def __init__(self, name, star, radius, color):
  17.         pygame.sprite.Sprite.__init__(self)
  18.         self.name = name
  19.         self.star = star
  20.         self.radius = radius
  21.         self.color = color
  22.         self.trail = []
  23.         self.image = pygame.Surface((radius * 2, radius * 2)).convert_alpha()
  24.         self.image.fill((0, 0, 0, 0))
  25.         pygame.draw.circle(self.image, color, (radius, radius), radius, 0)
  26.         self.rect = self.image.get_rect()
  27.         self.rect.center = self.star.x, self.star.y

  28.     def flush(self):
  29.         self.rect.centerx = (self.star.x + rel[0]) * scale
  30.         self.rect.centery = (self.star.y + rel[1]) * scale
  31.         self.trail.append((self.star.x, self.star.y))

  32.     def __repr__(self):
  33.         return self.name

  34.     __str__ = __repr__

  35. class Message(pygame.sprite.Sprite):
  36.     def __init__(self, text, pos):
  37.         pygame.sprite.Sprite.__init__(self)
  38.         self._text = text
  39.         self.image = font.render(text, False, (255, 255, 255))
  40.         self.rect = self.image.get_rect()
  41.         self.rect.topright = pos

  42.     @property
  43.     def text(self):
  44.         return self._text

  45.     @text.setter
  46.     def text(self, text):
  47.         pos = self.rect.topright
  48.         self._text = text
  49.         self.image = font.render(text, False, (255, 255, 255))
  50.         self.rect = self.image.get_rect()
  51.         self.rect.topright = pos

  52. def get_distance(sprite1, sprite2):
  53.     x1, y1 = sprite1.star.x, sprite1.star.y
  54.     x2, y2 = sprite2.star.x, sprite2.star.y
  55.     dx = x2 - x1
  56.     dy = y2 - y1
  57.     return (dx ** 2 + dy ** 2) ** 0.5

  58. def is_collide(sprite1, sprite2):
  59.     r1, r2 = sprite1.radius, sprite2.radius
  60.     return r1 + r2 > get_distance(sprite1, sprite2)

  61. def move(t):
  62.     sprites_to_delete = []
  63.     for sprite1 in sprites:
  64.         star1 = sprite1.star
  65.         x1, y1, vx1, vy1, m1 = star1.info
  66.         ax1, ay1 = 0, 0
  67.         for sprite2 in sprites:
  68.             star2 = sprite2.star
  69.             if sprite1 in sprites_to_delete or sprite2 in sprites_to_delete:
  70.                 break
  71.             if star1 is star2:
  72.                 continue
  73.             x2, y2, vx2, vy2, m2 = star2.info
  74.             dx = x2 - x1
  75.             dy = y2 - y1
  76.             r = get_distance(sprite1, sprite2)
  77.             if is_collide(sprite1, sprite2):
  78.                 heavier = sprite1 if star1.mass > star2.mass else sprite2
  79.                 lighter = sprite1 if heavier is star2 else sprite2
  80.                 sprites_to_delete.append(lighter)
  81.                 heavier.star.vx += lighter.star.vx
  82.                 heavier.star.vy += lighter.star.vy
  83.                 message.text = f"{heavier} 和 {lighter} 相撞"
  84.                 continue
  85.             f = G * m1 * m2 / (r ** 2)
  86.             accel = f / m1
  87.             ax1 += accel * (dx / r)
  88.             ay1 += accel * (dy / r)
  89.         x, y = (
  90.             x1 + vx1 * t + 0.5 * ax1 * (t ** 2),
  91.             y1 + vy1 * t + 0.5 * ay1 * (t ** 2)
  92.         )
  93.         tempx, tempy = star1.x, star1.y
  94.         star1.x, star1.y = x, y
  95.         star1.vx = (x - tempx) / t
  96.         star1.vy = (y - tempy) / t
  97.         sprite1.flush()
  98.     for sprite in sprites_to_delete:
  99.         sprites.remove(sprite)

  100. size = width, height = (1000, 1000)
  101. screen = pygame.display.set_mode((1000, 1000))

  102. sprites = [
  103.     StarSprite("planet1", StarObject(100, 100, 2, 0, 1000), 10, "green"),
  104.     StarSprite("planet2", StarObject(800, 100, 0, 2, 1000), 10, "blue"),
  105.     StarSprite("planet3", StarObject(800, 800, -2, 0, 1000), 10, "cyan"),
  106.     StarSprite("planet4", StarObject(100, 800, 0, -2, 1000), 10, "yellow")
  107. ]

  108. font = pygame.font.SysFont("Microsoft YaHei UI", 30)
  109. message = Message("", (width - 10, 10))

  110. clock = pygame.time.Clock()
  111. drag = False
  112. rel = [0, 0]
  113. scale = 1
  114. paused = False
  115. running = True
  116. while running:
  117.     screen.fill((0, 0, 0))
  118.     clock.tick(30)
  119.     for event in pygame.event.get():
  120.         if event.type == pygame.QUIT:
  121.             running = False
  122.         elif event.type == pygame.MOUSEBUTTONDOWN:
  123.             drag = True
  124.         elif event.type == pygame.MOUSEBUTTONUP:
  125.             drag = False
  126.         elif event.type == pygame.MOUSEMOTION:
  127.             if drag:
  128.                 rel[0] += event.rel[0]
  129.                 rel[1] += event.rel[1]
  130.         elif event.type == pygame.KEYDOWN:
  131.             if event.key == pygame.K_SPACE:
  132.                 paused = not paused
  133.             elif event.key in (pygame.K_EQUALS, pygame.K_KP_PLUS):
  134.                 scale += 0.01
  135.             elif event.key in (pygame.K_MINUS, pygame.K_KP_MINUS):
  136.                 scale -= 0.01
  137.     if not paused:
  138.         move(2)
  139.     for sprite, trail in map(lambda xxx: (xxx, xxx.trail), sprites):
  140.         if len(trail) < 2:
  141.             continue
  142.         trail = list(map(lambda point: ((point[0] + rel[0]) * scale, (point[1] + rel[1]) * scale), trail))
  143.         pygame.draw.lines(screen, sprite.color, False, trail, 2)
  144.     for sprite in sprites:
  145.         screen.blit(sprite.image, sprite.rect)
  146.     screen.blit(message.image, message.rect)
  147.     pygame.display.flip()
  148. pygame.quit()
复制代码

OpenAI是什么?GPT又是什么?
为什么会出现如下现象?

Snipaste_2023-07-24_10-42-23.png

???自己和自己相撞?我的代码里不是检查过这种情况了吗

Snipaste_2023-07-24_10-43-00.png
最佳答案
2023-7-24 11:22:01
改成这样:
  1.                 heavier = sprite1 if star1.mass > star2.mass else sprite2
  2.                 lighter = sprite2 if heavier is star2 else sprite1
复制代码
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2023-7-24 10:43:28 | 显示全部楼层
OpenAI 是一家人工智能研究实验室和公司,致力于开发先进的人工智能技术。他们开发了许多重要的人工智能模型和系统,其中之一就是 GPT(Generative Pre-trained Transformer)。

GPT 是一种基于 Transformer 模型的自然语言处理模型。它通过大规模的无监督学习从海量的文本数据中抽取知识,然后可以用于生成文本、回答问题、进行对话等任务。GPT 模型在自然语言理解和生成方面表现出色,被广泛应用于各种领域。

关于你提到的现象,自己和自己相撞,可能是因为在代码中的循环遍历过程中,同一个星球对象被重复当作两个不同的 sprite 对象处理了。在 move(t) 函数中的第一个循环遍历中,如果某个 sprite1 和 sprite2 是同一个星球的话,会直接跳过当前迭代,但是在接下来的循环遍历中,两个星球对象又被重新处理了一次,导致自己和自己发生了碰撞。

为了解决这个问题,你可以在内层循环遍历前添加一个判断条件,判断两个 sprite 是否指向同一个星球对象,如果是的话就跳过当前迭代。例如,在内层循环的开始添加以下代码:

  1. if sprite1 is sprite2:
  2.     continue
复制代码


通过这个判断条件,就可以避免同一个星球对象被重复处理,避免自己和自己相撞的情况发生。
如果问题已经解决,请设置最佳答案
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-7-24 10:43:58 | 显示全部楼层
这个问题可能是由于你的代码中对碰撞检测的处理方式导致的。在你的代码中,你在检查每个星体的每一个相互作用时,都会检查它们是否发生碰撞。然而,当两个星体发生碰撞时,你将更轻的星体添加到了 sprites_to_delete 列表中,但是在当前的循环中,你并没有立即停止处理更重星体的其余相互作用。

这可能会导致一个问题:如果在同一个时间步中,一个较重的星体与两个较轻的星体相撞,那么当处理第二个碰撞事件时,第一个较轻的星体可能已经在 sprites_to_delete 列表中,但是第二个碰撞事件仍然会被处理,可能导致像"planet1和planet1相撞"这样的输出。

你可以通过添加一个检查来解决此问题,以确保一个星体在同一个时间步中只能发生一次碰撞。另外,你可能还需要调整你的代码,以确保在发生碰撞时立即停止处理当前星体的其他相互作用。

下面是代码片段的修改建议:

  1. def move(t):
  2.     sprites_to_delete = []
  3.     for sprite1 in sprites:
  4.         if sprite1 in sprites_to_delete: # 添加此行
  5.             continue # 添加此行
  6.         # ...
  7.         for sprite2 in sprites:
  8.             if sprite1 in sprites_to_delete or sprite2 in sprites_to_delete: # 添加此行
  9.                 break # 添加此行
  10.             # ...
  11.             if is_collide(sprite1, sprite2):
  12.                 heavier = sprite1 if star1.mass > star2.mass else sprite2
  13.                 lighter = sprite1 if heavier is star2 else sprite2
  14.                 sprites_to_delete.append(lighter)
  15.                 heavier.star.vx += lighter.star.vx
  16.                 heavier.star.vy += lighter.star.vy
  17.                 message.text = f"{heavier} 和 {lighter} 相撞"
  18.                 break # 添加此行
  19.             # ...
复制代码


这些修改可以确保在处理一个星体的相互作用时,如果检测到碰撞,我们将立即停止处理其他相互作用,并且如果一个星体已经在待删除列表中,我们将不再处理它的相互作用。

至于OpenAI和GPT,OpenAI是一个人工智能研究实验室,致力于创建和推动友好的AI,以确保人工智能的利益广泛地惠及所有人。GPT(Generative Pretrained Transformer)是OpenAI开发的一种自然语言处理(NLP)预训练模型,它能够生成类似人类的文本。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-7-24 10:44:05 | 显示全部楼层
Mike_python小 发表于 2023-7-24 10:43
OpenAI 是一个人工智能研究实验室,致力于推动人工智能的发展和应用。他们开发了许多先进的技术和模型,其 ...

中计了
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-7-24 10:47:18 | 显示全部楼层
isdkz 发表于 2023-7-24 10:43
这个问题可能是由于你的代码中对碰撞检测的处理方式导致的。在你的代码中,你在检查每个星体的每一个相互作 ...


加了还是这样,还有就算重复碰撞也不会和自己撞啊
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-7-24 11:18:52 | 显示全部楼层
  1. heavier = sprite1 if star1.mass > star2.mass else sprite2
  2.                 lighter = sprite1 if heavier is star2 else sprite2
复制代码

应该是这里的问题
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-7-24 11:19:47 | 显示全部楼层

没问题吧
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-7-24 11:20:25 | 显示全部楼层
加了打印
  1. print(sprite1,sprite2)
  2.                 print(heavier == lighter, sprite1 == sprite2
复制代码

这是结果:
  1. pygame 2.3.0 (SDL 2.24.2, Python 3.10.10)
  2. Hello from the pygame community. https://www.pygame.org/contribute.html
  3. planet1 planet2
  4. True False
复制代码
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-7-24 11:21:24 | 显示全部楼层
cjjJasonchen 发表于 2023-7-24 11:20
加了打印

这是结果:

所以是哪一步出错了?
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-7-24 11:22:01 | 显示全部楼层    本楼为最佳答案   
改成这样:
  1.                 heavier = sprite1 if star1.mass > star2.mass else sprite2
  2.                 lighter = sprite2 if heavier is star2 else sprite1
复制代码
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-7-24 11:22:32 | 显示全部楼层
你再试试应该可以了
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-7-24 11:24:30 | 显示全部楼层

居然搞反了你很厉害
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-7-24 11:25:53 | 显示全部楼层
谢谢你捏,你的代码写的好漂亮,装饰器我还没学,很多地方看不懂大约猜了一下竟然对了,一会去学一下装饰器
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-7-24 11:26:43 | 显示全部楼层
cjjJasonchen 发表于 2023-7-24 11:25
谢谢你捏,你的代码写的好漂亮,装饰器我还没学,很多地方看不懂大约猜了一下竟然对了,一会去学一下装饰器 ...

这不是装饰器吧,这个类似c的三目表达式
xxx if yyy else zzz
if xxx:
   yyy
else:
   zzz
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-7-24 11:28:04 | 显示全部楼层
@property 这个
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-7-24 11:28:29 | 显示全部楼层
歌者文明清理员 发表于 2023-7-24 11:26
这不是装饰器吧,这个类似c的三目表达式
xxx if yyy else zzz
if xxx:

三元表达式我还是看得懂的
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-7-24 11:30:25 | 显示全部楼层

哦,那个是将属性改造成类似函数的作用,甚至可以在里面执行其他语句,这就起到了监视属性操作的作用
还可以避免使用 __dict__ 偷窥
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-7-24 11:33:06 | 显示全部楼层
歌者文明清理员 发表于 2023-7-24 11:30
哦,那个是将属性改造成类似函数的作用,甚至可以在里面执行其他语句,这就起到了监视属性操作的作用
还 ...

这样啊! 谢谢你
期待你的后续作品,我要去手搓教程啦
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-7-24 12:07:47 | 显示全部楼层
你好厉害有qq吗,私聊
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-7-24 12:08:13 | 显示全部楼层
Ewan-Ahiouy 发表于 2023-7-24 12:07
你好厉害有qq吗,私聊


不想用qq,去hack.chat?liu2023
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-4-23 07:34

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表