歌者文明清理员 发表于 2023-7-24 10:43:08

Pygame模拟天体运动问题

代码如下:

import pygame
pygame.init()
pygame.key.set_repeat(1000, 50)

G = 6

class StarObject:
    def __init__(self, x, y, vx, vy, mass):
      self.x = x
      self.y = y
      self.vx = vx
      self.vy = vy
      self.mass = mass

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

class StarSprite(pygame.sprite.Sprite):
    def __init__(self, name, star, radius, color):
      pygame.sprite.Sprite.__init__(self)
      self.name = name
      self.star = star
      self.radius = radius
      self.color = color
      self.trail = []
      self.image = pygame.Surface((radius * 2, radius * 2)).convert_alpha()
      self.image.fill((0, 0, 0, 0))
      pygame.draw.circle(self.image, color, (radius, radius), radius, 0)
      self.rect = self.image.get_rect()
      self.rect.center = self.star.x, self.star.y

    def flush(self):
      self.rect.centerx = (self.star.x + rel) * scale
      self.rect.centery = (self.star.y + rel) * scale
      self.trail.append((self.star.x, self.star.y))

    def __repr__(self):
      return self.name

    __str__ = __repr__

class Message(pygame.sprite.Sprite):
    def __init__(self, text, pos):
      pygame.sprite.Sprite.__init__(self)
      self._text = text
      self.image = font.render(text, False, (255, 255, 255))
      self.rect = self.image.get_rect()
      self.rect.topright = pos

    @property
    def text(self):
      return self._text

    @text.setter
    def text(self, text):
      pos = self.rect.topright
      self._text = text
      self.image = font.render(text, False, (255, 255, 255))
      self.rect = self.image.get_rect()
      self.rect.topright = pos

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

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

def move(t):
    sprites_to_delete = []
    for sprite1 in sprites:
      star1 = sprite1.star
      x1, y1, vx1, vy1, m1 = star1.info
      ax1, ay1 = 0, 0
      for sprite2 in sprites:
            star2 = sprite2.star
            if sprite1 in sprites_to_delete or sprite2 in sprites_to_delete:
                break
            if star1 is star2:
                continue
            x2, y2, vx2, vy2, m2 = star2.info
            dx = x2 - x1
            dy = y2 - y1
            r = get_distance(sprite1, sprite2)
            if is_collide(sprite1, sprite2):
                heavier = sprite1 if star1.mass > star2.mass else sprite2
                lighter = sprite1 if heavier is star2 else sprite2
                sprites_to_delete.append(lighter)
                heavier.star.vx += lighter.star.vx
                heavier.star.vy += lighter.star.vy
                message.text = f"{heavier} 和 {lighter} 相撞"
                continue
            f = G * m1 * m2 / (r ** 2)
            accel = f / m1
            ax1 += accel * (dx / r)
            ay1 += accel * (dy / r)
      x, y = (
            x1 + vx1 * t + 0.5 * ax1 * (t ** 2),
            y1 + vy1 * t + 0.5 * ay1 * (t ** 2)
      )
      tempx, tempy = star1.x, star1.y
      star1.x, star1.y = x, y
      star1.vx = (x - tempx) / t
      star1.vy = (y - tempy) / t
      sprite1.flush()
    for sprite in sprites_to_delete:
      sprites.remove(sprite)

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

sprites = [
    StarSprite("planet1", StarObject(100, 100, 2, 0, 1000), 10, "green"),
    StarSprite("planet2", StarObject(800, 100, 0, 2, 1000), 10, "blue"),
    StarSprite("planet3", StarObject(800, 800, -2, 0, 1000), 10, "cyan"),
    StarSprite("planet4", StarObject(100, 800, 0, -2, 1000), 10, "yellow")
]

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

clock = pygame.time.Clock()
drag = False
rel =
scale = 1
paused = False
running = True
while running:
    screen.fill((0, 0, 0))
    clock.tick(30)
    for event in pygame.event.get():
      if event.type == pygame.QUIT:
            running = False
      elif event.type == pygame.MOUSEBUTTONDOWN:
            drag = True
      elif event.type == pygame.MOUSEBUTTONUP:
            drag = False
      elif event.type == pygame.MOUSEMOTION:
            if drag:
                rel += event.rel
                rel += event.rel
      elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_SPACE:
                paused = not paused
            elif event.key in (pygame.K_EQUALS, pygame.K_KP_PLUS):
                scale += 0.01
            elif event.key in (pygame.K_MINUS, pygame.K_KP_MINUS):
                scale -= 0.01
    if not paused:
      move(2)
    for sprite, trail in map(lambda xxx: (xxx, xxx.trail), sprites):
      if len(trail) < 2:
            continue
      trail = list(map(lambda point: ((point + rel) * scale, (point + rel) * scale), trail))
      pygame.draw.lines(screen, sprite.color, False, trail, 2)
    for sprite in sprites:
      screen.blit(sprite.image, sprite.rect)
    screen.blit(message.image, message.rect)
    pygame.display.flip()
pygame.quit()

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



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

陶远航 发表于 2023-7-24 10:43:28

OpenAI 是一家人工智能研究实验室和公司,致力于开发先进的人工智能技术。他们开发了许多重要的人工智能模型和系统,其中之一就是 GPT(Generative Pre-trained Transformer)。

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

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

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

if sprite1 is sprite2:
    continue


通过这个判断条件,就可以避免同一个星球对象被重复处理,避免自己和自己相撞的情况发生。
如果问题已经解决,请设置最佳答案

isdkz 发表于 2023-7-24 10:43:58

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

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

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

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

def move(t):
    sprites_to_delete = []
    for sprite1 in sprites:
      if sprite1 in sprites_to_delete: # 添加此行
            continue # 添加此行
      # ...
      for sprite2 in sprites:
            if sprite1 in sprites_to_delete or sprite2 in sprites_to_delete: # 添加此行
                break # 添加此行
            # ...
            if is_collide(sprite1, sprite2):
                heavier = sprite1 if star1.mass > star2.mass else sprite2
                lighter = sprite1 if heavier is star2 else sprite2
                sprites_to_delete.append(lighter)
                heavier.star.vx += lighter.star.vx
                heavier.star.vy += lighter.star.vy
                message.text = f"{heavier} 和 {lighter} 相撞"
                break # 添加此行
            # ...


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

至于OpenAI和GPT,OpenAI是一个人工智能研究实验室,致力于创建和推动友好的AI,以确保人工智能的利益广泛地惠及所有人。GPT(Generative Pretrained Transformer)是OpenAI开发的一种自然语言处理(NLP)预训练模型,它能够生成类似人类的文本。

歌者文明清理员 发表于 2023-7-24 10:44:05

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

{:10_256:}中计了

歌者文明清理员 发表于 2023-7-24 10:47:18

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

加了还是这样,还有就算重复碰撞也不会和自己撞啊

cjjJasonchen 发表于 2023-7-24 11:18:52

heavier = sprite1 if star1.mass > star2.mass else sprite2
                lighter = sprite1 if heavier is star2 else sprite2
应该是这里的问题

歌者文明清理员 发表于 2023-7-24 11:19:47

cjjJasonchen 发表于 2023-7-24 11:18
应该是这里的问题

没问题吧

cjjJasonchen 发表于 2023-7-24 11:20:25

加了打印
print(sprite1,sprite2)
                print(heavier == lighter, sprite1 == sprite2

这是结果:
pygame 2.3.0 (SDL 2.24.2, Python 3.10.10)
Hello from the pygame community. https://www.pygame.org/contribute.html
planet1 planet2
True False

歌者文明清理员 发表于 2023-7-24 11:21:24

cjjJasonchen 发表于 2023-7-24 11:20
加了打印

这是结果:

{:10_257:}所以是哪一步出错了?

cjjJasonchen 发表于 2023-7-24 11:22:01

改成这样:
                heavier = sprite1 if star1.mass > star2.mass else sprite2
                lighter = sprite2 if heavier is star2 else sprite1

cjjJasonchen 发表于 2023-7-24 11:22:32

你再试试应该可以了

歌者文明清理员 发表于 2023-7-24 11:24:30

cjjJasonchen 发表于 2023-7-24 11:22
改成这样:

居然搞反了{:10_257:}你很厉害{:10_327:}

cjjJasonchen 发表于 2023-7-24 11:25:53

谢谢你捏,你的代码写的好漂亮,装饰器我还没学,很多地方看不懂大约猜了一下竟然对了,一会去学一下装饰器{:10_328:}

歌者文明清理员 发表于 2023-7-24 11:26:43

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

这不是装饰器吧,这个类似c的三目表达式
xxx if yyy else zzz
if xxx:
   yyy
else:
   zzz

cjjJasonchen 发表于 2023-7-24 11:28:04

@property 这个{:10_297:}

cjjJasonchen 发表于 2023-7-24 11:28:29

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


三元表达式我还是看得懂的{:10_297:}

歌者文明清理员 发表于 2023-7-24 11:30:25

cjjJasonchen 发表于 2023-7-24 11:28
@property 这个

哦,那个是将属性改造成类似函数的作用,甚至可以在里面执行其他语句,这就起到了监视属性操作的作用
还可以避免使用 __dict__ 偷窥

cjjJasonchen 发表于 2023-7-24 11:33:06

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

{:10_257:}这样啊! 谢谢你
期待你的后续作品,我要去手搓教程啦{:10_256:}

Ewan-Ahiouy 发表于 2023-7-24 12:07:47

你好厉害{:10_257:}有qq吗,私聊

歌者文明清理员 发表于 2023-7-24 12:08:13

Ewan-Ahiouy 发表于 2023-7-24 12:07
你好厉害有qq吗,私聊

不想用qq,去hack.chat?liu2023
页: [1] 2
查看完整版本: Pygame模拟天体运动问题