鱼C论坛

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

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

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

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

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

x
代码如下:
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[0]) * scale
        self.rect.centery = (self.star.y + rel[1]) * 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 = [0, 0]
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[0] += event.rel[0]
                rel[1] += event.rel[1]
        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[0] + rel[0]) * scale, (point[1] + rel[1]) * 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又是什么?
为什么会出现如下现象?

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

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

Snipaste_2023-07-24_10-43-00.png
最佳答案
2023-7-24 11:22:01
改成这样:
                heavier = sprite1 if star1.mass > star2.mass else sprite2
                lighter = sprite2 if heavier is star2 else sprite1
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 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

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

使用道具 举报

发表于 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)预训练模型,它能够生成类似人类的文本。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

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

中计了
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

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


加了还是这样,还有就算重复碰撞也不会和自己撞啊
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-7-24 11:18:52 | 显示全部楼层
heavier = sprite1 if star1.mass > star2.mass else sprite2
                lighter = sprite1 if heavier is star2 else sprite2
应该是这里的问题
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

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

没问题吧
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 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
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

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

这是结果:

所以是哪一步出错了?
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-7-24 11:22:01 | 显示全部楼层    本楼为最佳答案   
改成这样:
                heavier = sprite1 if star1.mass > star2.mass else sprite2
                lighter = sprite2 if heavier is star2 else sprite1
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-7-24 11:22:32 | 显示全部楼层
你再试试应该可以了
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

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

居然搞反了你很厉害
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-7-24 11:25:53 | 显示全部楼层
谢谢你捏,你的代码写的好漂亮,装饰器我还没学,很多地方看不懂大约猜了一下竟然对了,一会去学一下装饰器
想知道小甲鱼最近在做啥?请访问 -> 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
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-7-24 11:28:04 | 显示全部楼层
@property 这个
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

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

三元表达式我还是看得懂的
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

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

哦,那个是将属性改造成类似函数的作用,甚至可以在里面执行其他语句,这就起到了监视属性操作的作用
还可以避免使用 __dict__ 偷窥
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

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

这样啊! 谢谢你
期待你的后续作品,我要去手搓教程啦
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-7-24 12:07:47 | 显示全部楼层
你好厉害有qq吗,私聊
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

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


不想用qq,去hack.chat?liu2023
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

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

本版积分规则

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

GMT+8, 2024-9-17 03:58

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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