鱼C论坛

 找回密码
 立即注册
查看: 1763|回复: 6

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

[复制链接]
发表于 2023-7-26 22:32:53 | 显示全部楼层 |阅读模式

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

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

x
本帖最后由 歌者文明清理员 于 2023-7-26 23:11 编辑

目前已经放弃 SCALE

怎么会,居然把 pygame 写成了 pycharm

目前问题是不能将天体和轨道合并在同一个点上

cjjJasonchen 的回答:https://fishc.com.cn/forum.php?m ... 284&pid=6297452
当用鼠标滚轮滑动的时候行星会变大或者变小,这时,他们的中心点会发生变化,
而此时,轨迹的生成位置还是在行星原本大小时的center,

所以看上去轨道就不和行星在一起了


我有点没懂,这么说不应该是轨道的曲线很诡异吗,再说我这个现象不是滚轮,是拖动时发生的
目前的想法:https://fishc.com.cn/forum.php?m ... 284&pid=6298215
那可以不可以让StarSprite和轨迹都先放到另外一个surface上,对另外一个surface处理后再放回screen?
这样也有一个问题,就是轨迹的粗细不固定


但这个肯定不可行,首先这个 Surface 会超出屏幕
  1. screen.blit(surf, rel)
复制代码

因为 surf 也得有大小,不能无限大,所以就会出现过一会儿就黑屏的情况

求救,不然 Star Motion Simulate 将止步于此……

code.zip (128.5 KB, 下载次数: 1)
最佳答案
2023-7-26 22:35:45
本帖最后由 hellomyprogram 于 2023-7-27 11:12 编辑

对于这种大型程序,你可以将代码拆分成不同板块来分别储存。

大功告成!以下是结果(代码部分):
  1. import itertools
  2. import os
  3. import pygame
  4. import pyini
  5. import tkinter as tk
  6. import tkinter.filedialog as fd
  7. from time import sleep
  8. from threading import Thread

  9. pygame.init()
  10. pygame.key.set_repeat(1000, 50)

  11. G = 6

  12. # why do you need to create another object?
  13. class StarObject:
  14.     def __init__(self, x, y, vx, vy, mass):
  15.         self.x = x
  16.         self.y = y
  17.         self.vx = vx  # v means speed
  18.         self.vy = vy
  19.         self.mass = mass

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

  23. class StarSprite(pygame.sprite.Sprite):
  24.     def __init__(self, name, star, radius, color):
  25.         # pygame.sprite.Sprite.__init__(self)
  26.         super().__init__()
  27.         self.name = name
  28.         self.star = star  # this is a StarObject
  29.         self.radius = radius
  30.         self.color = color
  31.         self.trail = []
  32.         self.image = pygame.Surface((radius * 2, radius * 2)).convert_alpha()
  33.         self.image.fill((0, 0, 0, 0))
  34.         pygame.draw.circle(self.image, color, (radius, radius), radius, 0)
  35.         self.rect = self.image.get_rect()
  36.         self.flush()

  37.     def flush(self):
  38.         self.rect.center = self.star.x, self.star.y
  39.         self.trail.append(self.rect.center)

  40.     def __repr__(self):
  41.         return self.name

  42.     __str__ = __repr__

  43. class Message(pygame.sprite.Sprite):
  44.     def __init__(self, text, pos):
  45.         pygame.sprite.Sprite.__init__(self)
  46.         self._text = text
  47.         self.flush(pos)

  48.     @property
  49.     def text(self):
  50.         return self._text

  51.     @text.setter
  52.     def text(self, text):
  53.         pos = self.rect.topright
  54.         self._text = text
  55.         self.flush(pos)

  56.     def flush(self, pos):
  57.         self.image = font.render(self._text, False, (255, 255, 255))
  58.         self.image = pygame.transform.scale_by(self.image, 1 / SCALE)
  59.         self.rect = self.image.get_rect()
  60.         self.rect.topright = pos

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

  67. def disappear_message(delay=5, interval=0.09):
  68.     sleep(delay)
  69.     while message.text:
  70.         try:
  71.             message.text = message.text[:-1]
  72.         except pygame.error:
  73.             return
  74.         sleep(interval)

  75. def move(t):
  76.     global sprites
  77.     sprites_to_delete = set()
  78.     for sprite1, sprite2 in itertools.combinations(sprites, 2):  # this is way faster
  79.         star1 = sprite1.star
  80.         star2 = sprite2.star
  81.         if star1 is star2:
  82.             continue
  83.         x1, y1, vx1, vy1, m1 = star1.info
  84.         x2, y2, _, _, m2 = star2.info
  85.         ax1, ay1 = 0, 0
  86.         dx = x2 - x1
  87.         dy = y2 - y1
  88.         r = get_distance(sprite1, sprite2)
  89.         if is_collide(sprite1, sprite2):
  90.             heavier = sprite1 if star1.mass > star2.mass else sprite2
  91.             lighter = sprite2 if heavier is sprite1 else sprite1
  92.             sprites_to_delete.add(lighter)
  93.             heavier.star.vx += lighter.star.vx
  94.             heavier.star.vy += lighter.star.vy
  95.             message.text = language["star"]["collide"] % (heavier, lighter)
  96.             Thread(target=disappear_message).start()
  97.         f = G * m1 * m2 / (r ** 2)
  98.         accel = f / m1
  99.         ax1 += accel * (dx / r)
  100.         ay1 += accel * (dy / r)
  101.         x, y = (
  102.             x1 + vx1 * t + 0.5 * ax1 * (t ** 2),
  103.             y1 + vy1 * t + 0.5 * ay1 * (t ** 2)
  104.         )
  105.         tempx, tempy = star1.x, star1.y
  106.         star1.x, star1.y = x, y
  107.         star1.vx = (x - tempx) / t
  108.         star1.vy = (y - tempy) / t
  109.         sprite1.flush()
  110.     sprites -= sprites_to_delete  # delete everything in sprites_to_delete

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

  114. def zoom(direction, each=0.01):
  115.     global scale
  116.     if direction > 0:
  117.         scale += each
  118.     elif direction < 0:
  119.         scale -= each
  120.     message.text = language["game"]["zoom"] % scale
  121.     Thread(target=disappear_message).start()

  122. def change_view(move_x, move_y):
  123.     rel[0] += move_x
  124.     rel[1] += move_y
  125.     message.text = language["game"]["rel"] % str(tuple(rel))

  126. with open("config/config.ini", "r", encoding="utf-8") as f:
  127.     config = pyini.ConfigParser(f.read())

  128. with open(f"config/language_{config['language']['default']}.ini", "r", encoding="utf-8") as f:
  129.     language = pyini.ConfigParser(f.read())

  130. SCALE = int(config["window"]["screen_zoom"]) / 100  # what's this? is it different from "scale"?

  131. clock = pygame.time.Clock()
  132. drag = False
  133. rel = [0, 0]  # do you mean "the world origin on the screen"
  134. scale = 1
  135. paused = False

  136. size = width, height = (1000 / SCALE, 1000 / SCALE)
  137. screen = pygame.display.set_mode(size)
  138. screen_original = pygame.Surface(size)

  139. movement = 10

  140. pygame.display.set_icon(pygame.image.load(config["window"]["icon"]))
  141. pygame.display.set_caption("Pygame Star Motion Simulate")

  142. with open(f"simulation/{config['simulation']['file']}.simulation", "r", encoding="utf-8") as f:
  143.     sprites = eval(f.read())  # you can use sets rather than lists

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

  146. running = True
  147. while running:
  148.     screen.fill((0, 0, 0))
  149.     clock.tick(30)
  150.     if not paused:
  151.         move(2)
  152.     for sprite, trail in map(lambda each_sprite: (each_sprite, each_sprite.trail), sprites):
  153.         if len(trail) < 2:
  154.             continue
  155.         trail = list(map(lambda point: ((point[0] + rel[0]) * scale, (point[1] + rel[1]) * scale), trail))
  156.         pygame.draw.lines(screen, sprite.color, False, trail, 2)
  157.     for sprite in sprites:
  158.         image = sprite.image
  159.         image = pygame.transform.scale(image, (sprite.radius * 2 * scale,) * 2)
  160.         screen.blit(image, ((sprite.rect.topleft[0] + rel[0]) * scale, (sprite.rect.topleft[1] + rel[1]) * scale))
  161.     try:
  162.         screen.blit(message.image, message.rect)
  163.     except pygame.error:
  164.         pass
  165.     pygame.display.flip()
  166.     for event in pygame.event.get():
  167.         if event.type == pygame.QUIT:
  168.             running = False
  169.         elif event.type == pygame.MOUSEBUTTONDOWN:
  170.             if event.button == 1:
  171.                 drag = True
  172.         elif event.type == pygame.MOUSEBUTTONUP:
  173.             if event.button == 1:
  174.                 drag = False
  175.             elif event.button == 4:
  176.                 zoom(1)
  177.             else:
  178.                 zoom(-1)
  179.         elif event.type == pygame.MOUSEMOTION:
  180.             if drag:
  181.                 change_view(*event.rel)
  182.         elif event.type == pygame.KEYDOWN:
  183.             if event.key == pygame.K_SPACE:
  184.                 paused = not paused
  185.             elif event.mod & pygame.KMOD_CTRL and event.key == pygame.K_s:
  186.                 root = tk.Tk()
  187.                 root.withdraw()
  188.                 filepath = fd.asksaveasfilename(
  189.                     initialdir=os.path.dirname(__file__),
  190.                     defaultextension=".png",
  191.                     filetypes=((language["game"]["picture"] % "PNG", "*.png"), )
  192.                 )
  193.                 if filepath:
  194.                     pygame.image.save(screen, filepath)
  195.                 root.destroy()
  196.             elif event.key in (pygame.K_LEFT, pygame.K_a, pygame.K_KP_4):
  197.                 change_view(movement, 0)
  198.             elif event.key in (pygame.K_RIGHT, pygame.K_d, pygame.K_KP_6):
  199.                 change_view(-movement, 0)
  200.             elif event.key in (pygame.K_UP, pygame.K_w, pygame.K_KP_8):
  201.                 change_view(0, movement)
  202.             elif event.key in (pygame.K_DOWN, pygame.K_s, pygame.K_KP_2):
  203.                 change_view(0, -movement)
  204.             elif event.key in (pygame.K_EQUALS, pygame.K_KP_PLUS):
  205.                 zoom(1)
  206.             elif event.key in (pygame.K_MINUS, pygame.K_KP_MINUS):
  207.                 zoom(-1)
  208. pygame.quit()
复制代码

存储部分要把.simulation的文件中的方括号改为大括号。以下是完整文件:
code (fixed).zip (128.92 KB, 下载次数: 2)
其实,问题出在这一行(第188行)上:
  1. screen.blit(image, sprite.rect)
复制代码

这样并没有改变星球的显示位置,只改变了大小,将这一行改为这样:
  1. screen.blit(image, ((sprite.rect.topleft[0] + rel[0]) * scale, (sprite.rect.topleft[1] + rel[1]) * scale))
复制代码



无关紧要的问题:
  • 你可以将StarObject与StarSprite整合在一起,分开来似乎没有什么用;
  • 你在进行缩小判断的时候只用了else语句,这样会匹配其他按键(例如鼠标中键和鼠标右键),你可以试试;
  • 把大写的SCALE改为SCREEN_SIZE_SCALE语义会明确一点;
  • 你可以将两个循环通过itertools.combination整合到一起(又不需要第三方库),一次性计算两个星球之间的关系,比用两个循环要快;
  • set(集合)自带差集、并集和交集算法,可以使用set加快运行速度,减少代码量;
  • 对于加速度、速度以及力这些矢量,你可以创建一个向量(Vector)对象来存储并更改他们,加强可读性;
  • 你可以定时清理trail(星球轨迹),否则内存占用会一直增加,直到爆栈。
  • 像你这样的文件,可以使用git作为版本管理工具(不想开源可以不用github),在每一个版本附上这个版本的说明,如果出了什么bug,可以回退版本查看问题出在哪里。
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2023-7-26 22:33:03 | 显示全部楼层
根据你提供的信息,你的问题是在pycharm中进行天体运动模拟时,无法将天体和轨道合并在同一个点上。

根据你提到的想法,将StarSprite和轨迹都放到另一个surface上,然后对该surface进行处理后再放回screen。但是这个方法是不可行的,因为这个surface的大小是有限的,可能会超出屏幕范围,导致黑屏的情况。

根据cjjJasonchen的回答,当用鼠标滚轮滑动时,行星的大小会改变,这时行星的中心点会发生变化,而轨道的生成位置仍然是在行星原本大小时的中心点上,所以看上去轨道和行星不在一起。

根据你的描述,这个现象是在拖动时发生的,而不是滚轮滑动。对于这个问题,我需要查看你的代码来进行更详细的分析和解答。请提供你的代码文件(code.zip)以便我可以帮助你解决问题。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-7-26 22:33:12 | 显示全部楼层
很抱歉,由于我无法打开或访问链接中的内容,我无法准确理解你提到的问题和现象。但是,我可以为你提供一些关于天体运动模拟问题的一般性建议。

对于将天体和轨道合并在同一个点上的问题,你的想法是将StarSprite和轨迹放到另一个surface上进行处理,然后再放回screen。这个想法是可行的,可以使用一个临时的surface来进行处理,但需要注意处理后的surface大小不能超出屏幕。

关于轨迹的粗细不固定的问题,你可以尝试使用适当的绘图函数或技巧来实现轨迹的可变粗细。例如,你可以使用带有透明度的画笔,在每个时间步骤中根据行星的位置绘制轨迹,并根据距离行星中心的距离来调整透明度或线条的粗细,以达到变化的效果。

关于行星大小变化导致轨道生成位置的问题,你可以在每次行星大小发生变化时,同时更新轨道的生成位置。根据新的行星大小,重新计算轨道生成位置,确保轨道与行星始终保持一致。

总之,天体运动模拟问题涉及到多个方面的计算和图形处理,需要综合考虑各个因素来进行解决。如果你能提供更具体的代码或问题描述,我将能够给出更详细和准确的帮助。希望这些提示对你有所帮助,如果还有其他问题,请随时提问。
如果问题已经解决,请设置最佳答案
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-7-26 22:33:13 | 显示全部楼层
问题描述

根据你的描述,你在使用PyCharm进行天体运动模拟时遇到了问题。目前无法将天体和轨道合并在同一个点上。你考虑过将StarSprite和轨迹都放在一个新的surface上,然后对该surface进行处理后再放回主屏幕。但是这样会导致surface超出屏幕大小,并最终导致黑屏现象。此外,你提到鼠标滚轮滑动时行星的大小变化会影响到轨道的生成位置。

解决方案

1. 合并天体和轨道显示: 为了将天体和轨道合并在同一个点上,你可以将天体和轨道的绘制操作都放在同一个循环中,并将它们都绘制在同一个surface上,然后将该surface绘制到屏幕上。这样可以保证天体和轨道在视觉上的一致性。

2. 轨道粗细处理: 如果你希望轨道的粗细有所不同,可以使用Pygame的pygame.draw.lines()函数绘制轨道,该函数可以指定线条的宽度。你可以根据行星的大小或其他条件来动态调整轨道的粗细。如果你不希望轨道的粗细固定,可以在每次绘制轨道时计算轨道的粗细值,并将其作为参数传递给绘制函数。

3. 行星大小变化对轨道位置的影响: 当行星的大小发生变化时,它的中心点也会随之发生变化。这可能导致轨道生成位置与行星当前位置不一致。为了解决这个问题,你需要确保轨道的生成位置始终与行星的中心点保持一致。你可以根据行星的当前位置和大小重新计算轨道的生成位置,以确保它们始终在一起。

希望以上解决方案能够对你的问题有所帮助。如果你还有其他问题,请随时提问。

球一个最佳答案谢谢啦!这对我非常重要!
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-7-26 22:35:24 | 显示全部楼层
isdkz 发表于 2023-7-26 22:33
根据你提供的信息,你的问题是在pycharm中进行天体运动模拟时,无法将天体和轨道合并在同一个点上。

根 ...

为何不下载 code.zip 去问 GPT?
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-7-26 22:35:45 | 显示全部楼层    本楼为最佳答案   
本帖最后由 hellomyprogram 于 2023-7-27 11:12 编辑

对于这种大型程序,你可以将代码拆分成不同板块来分别储存。

大功告成!以下是结果(代码部分):
  1. import itertools
  2. import os
  3. import pygame
  4. import pyini
  5. import tkinter as tk
  6. import tkinter.filedialog as fd
  7. from time import sleep
  8. from threading import Thread

  9. pygame.init()
  10. pygame.key.set_repeat(1000, 50)

  11. G = 6

  12. # why do you need to create another object?
  13. class StarObject:
  14.     def __init__(self, x, y, vx, vy, mass):
  15.         self.x = x
  16.         self.y = y
  17.         self.vx = vx  # v means speed
  18.         self.vy = vy
  19.         self.mass = mass

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

  23. class StarSprite(pygame.sprite.Sprite):
  24.     def __init__(self, name, star, radius, color):
  25.         # pygame.sprite.Sprite.__init__(self)
  26.         super().__init__()
  27.         self.name = name
  28.         self.star = star  # this is a StarObject
  29.         self.radius = radius
  30.         self.color = color
  31.         self.trail = []
  32.         self.image = pygame.Surface((radius * 2, radius * 2)).convert_alpha()
  33.         self.image.fill((0, 0, 0, 0))
  34.         pygame.draw.circle(self.image, color, (radius, radius), radius, 0)
  35.         self.rect = self.image.get_rect()
  36.         self.flush()

  37.     def flush(self):
  38.         self.rect.center = self.star.x, self.star.y
  39.         self.trail.append(self.rect.center)

  40.     def __repr__(self):
  41.         return self.name

  42.     __str__ = __repr__

  43. class Message(pygame.sprite.Sprite):
  44.     def __init__(self, text, pos):
  45.         pygame.sprite.Sprite.__init__(self)
  46.         self._text = text
  47.         self.flush(pos)

  48.     @property
  49.     def text(self):
  50.         return self._text

  51.     @text.setter
  52.     def text(self, text):
  53.         pos = self.rect.topright
  54.         self._text = text
  55.         self.flush(pos)

  56.     def flush(self, pos):
  57.         self.image = font.render(self._text, False, (255, 255, 255))
  58.         self.image = pygame.transform.scale_by(self.image, 1 / SCALE)
  59.         self.rect = self.image.get_rect()
  60.         self.rect.topright = pos

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

  67. def disappear_message(delay=5, interval=0.09):
  68.     sleep(delay)
  69.     while message.text:
  70.         try:
  71.             message.text = message.text[:-1]
  72.         except pygame.error:
  73.             return
  74.         sleep(interval)

  75. def move(t):
  76.     global sprites
  77.     sprites_to_delete = set()
  78.     for sprite1, sprite2 in itertools.combinations(sprites, 2):  # this is way faster
  79.         star1 = sprite1.star
  80.         star2 = sprite2.star
  81.         if star1 is star2:
  82.             continue
  83.         x1, y1, vx1, vy1, m1 = star1.info
  84.         x2, y2, _, _, m2 = star2.info
  85.         ax1, ay1 = 0, 0
  86.         dx = x2 - x1
  87.         dy = y2 - y1
  88.         r = get_distance(sprite1, sprite2)
  89.         if is_collide(sprite1, sprite2):
  90.             heavier = sprite1 if star1.mass > star2.mass else sprite2
  91.             lighter = sprite2 if heavier is sprite1 else sprite1
  92.             sprites_to_delete.add(lighter)
  93.             heavier.star.vx += lighter.star.vx
  94.             heavier.star.vy += lighter.star.vy
  95.             message.text = language["star"]["collide"] % (heavier, lighter)
  96.             Thread(target=disappear_message).start()
  97.         f = G * m1 * m2 / (r ** 2)
  98.         accel = f / m1
  99.         ax1 += accel * (dx / r)
  100.         ay1 += accel * (dy / r)
  101.         x, y = (
  102.             x1 + vx1 * t + 0.5 * ax1 * (t ** 2),
  103.             y1 + vy1 * t + 0.5 * ay1 * (t ** 2)
  104.         )
  105.         tempx, tempy = star1.x, star1.y
  106.         star1.x, star1.y = x, y
  107.         star1.vx = (x - tempx) / t
  108.         star1.vy = (y - tempy) / t
  109.         sprite1.flush()
  110.     sprites -= sprites_to_delete  # delete everything in sprites_to_delete

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

  114. def zoom(direction, each=0.01):
  115.     global scale
  116.     if direction > 0:
  117.         scale += each
  118.     elif direction < 0:
  119.         scale -= each
  120.     message.text = language["game"]["zoom"] % scale
  121.     Thread(target=disappear_message).start()

  122. def change_view(move_x, move_y):
  123.     rel[0] += move_x
  124.     rel[1] += move_y
  125.     message.text = language["game"]["rel"] % str(tuple(rel))

  126. with open("config/config.ini", "r", encoding="utf-8") as f:
  127.     config = pyini.ConfigParser(f.read())

  128. with open(f"config/language_{config['language']['default']}.ini", "r", encoding="utf-8") as f:
  129.     language = pyini.ConfigParser(f.read())

  130. SCALE = int(config["window"]["screen_zoom"]) / 100  # what's this? is it different from "scale"?

  131. clock = pygame.time.Clock()
  132. drag = False
  133. rel = [0, 0]  # do you mean "the world origin on the screen"
  134. scale = 1
  135. paused = False

  136. size = width, height = (1000 / SCALE, 1000 / SCALE)
  137. screen = pygame.display.set_mode(size)
  138. screen_original = pygame.Surface(size)

  139. movement = 10

  140. pygame.display.set_icon(pygame.image.load(config["window"]["icon"]))
  141. pygame.display.set_caption("Pygame Star Motion Simulate")

  142. with open(f"simulation/{config['simulation']['file']}.simulation", "r", encoding="utf-8") as f:
  143.     sprites = eval(f.read())  # you can use sets rather than lists

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

  146. running = True
  147. while running:
  148.     screen.fill((0, 0, 0))
  149.     clock.tick(30)
  150.     if not paused:
  151.         move(2)
  152.     for sprite, trail in map(lambda each_sprite: (each_sprite, each_sprite.trail), sprites):
  153.         if len(trail) < 2:
  154.             continue
  155.         trail = list(map(lambda point: ((point[0] + rel[0]) * scale, (point[1] + rel[1]) * scale), trail))
  156.         pygame.draw.lines(screen, sprite.color, False, trail, 2)
  157.     for sprite in sprites:
  158.         image = sprite.image
  159.         image = pygame.transform.scale(image, (sprite.radius * 2 * scale,) * 2)
  160.         screen.blit(image, ((sprite.rect.topleft[0] + rel[0]) * scale, (sprite.rect.topleft[1] + rel[1]) * scale))
  161.     try:
  162.         screen.blit(message.image, message.rect)
  163.     except pygame.error:
  164.         pass
  165.     pygame.display.flip()
  166.     for event in pygame.event.get():
  167.         if event.type == pygame.QUIT:
  168.             running = False
  169.         elif event.type == pygame.MOUSEBUTTONDOWN:
  170.             if event.button == 1:
  171.                 drag = True
  172.         elif event.type == pygame.MOUSEBUTTONUP:
  173.             if event.button == 1:
  174.                 drag = False
  175.             elif event.button == 4:
  176.                 zoom(1)
  177.             else:
  178.                 zoom(-1)
  179.         elif event.type == pygame.MOUSEMOTION:
  180.             if drag:
  181.                 change_view(*event.rel)
  182.         elif event.type == pygame.KEYDOWN:
  183.             if event.key == pygame.K_SPACE:
  184.                 paused = not paused
  185.             elif event.mod & pygame.KMOD_CTRL and event.key == pygame.K_s:
  186.                 root = tk.Tk()
  187.                 root.withdraw()
  188.                 filepath = fd.asksaveasfilename(
  189.                     initialdir=os.path.dirname(__file__),
  190.                     defaultextension=".png",
  191.                     filetypes=((language["game"]["picture"] % "PNG", "*.png"), )
  192.                 )
  193.                 if filepath:
  194.                     pygame.image.save(screen, filepath)
  195.                 root.destroy()
  196.             elif event.key in (pygame.K_LEFT, pygame.K_a, pygame.K_KP_4):
  197.                 change_view(movement, 0)
  198.             elif event.key in (pygame.K_RIGHT, pygame.K_d, pygame.K_KP_6):
  199.                 change_view(-movement, 0)
  200.             elif event.key in (pygame.K_UP, pygame.K_w, pygame.K_KP_8):
  201.                 change_view(0, movement)
  202.             elif event.key in (pygame.K_DOWN, pygame.K_s, pygame.K_KP_2):
  203.                 change_view(0, -movement)
  204.             elif event.key in (pygame.K_EQUALS, pygame.K_KP_PLUS):
  205.                 zoom(1)
  206.             elif event.key in (pygame.K_MINUS, pygame.K_KP_MINUS):
  207.                 zoom(-1)
  208. pygame.quit()
复制代码

存储部分要把.simulation的文件中的方括号改为大括号。以下是完整文件:
code (fixed).zip (128.92 KB, 下载次数: 2)
其实,问题出在这一行(第188行)上:
  1. screen.blit(image, sprite.rect)
复制代码

这样并没有改变星球的显示位置,只改变了大小,将这一行改为这样:
  1. screen.blit(image, ((sprite.rect.topleft[0] + rel[0]) * scale, (sprite.rect.topleft[1] + rel[1]) * scale))
复制代码



无关紧要的问题:
  • 你可以将StarObject与StarSprite整合在一起,分开来似乎没有什么用;
  • 你在进行缩小判断的时候只用了else语句,这样会匹配其他按键(例如鼠标中键和鼠标右键),你可以试试;
  • 把大写的SCALE改为SCREEN_SIZE_SCALE语义会明确一点;
  • 你可以将两个循环通过itertools.combination整合到一起(又不需要第三方库),一次性计算两个星球之间的关系,比用两个循环要快;
  • set(集合)自带差集、并集和交集算法,可以使用set加快运行速度,减少代码量;
  • 对于加速度、速度以及力这些矢量,你可以创建一个向量(Vector)对象来存储并更改他们,加强可读性;
  • 你可以定时清理trail(星球轨迹),否则内存占用会一直增加,直到爆栈。
  • 像你这样的文件,可以使用git作为版本管理工具(不想开源可以不用github),在每一个版本附上这个版本的说明,如果出了什么bug,可以回退版本查看问题出在哪里。

评分

参与人数 1荣誉 +5 鱼币 +5 贡献 +3 收起 理由
歌者文明清理员 + 5 + 5 + 3

查看全部评分

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

使用道具 举报

 楼主| 发表于 2023-7-26 22:38:16 | 显示全部楼层
hellomyprogram 发表于 2023-7-26 22:35
你可以试试用一个列表存储天体的位置,并使用pygame.draw.lines()方法将天体的位置信息画到屏幕上,你可能 ...


我的代码中为了让用户移动视角定义了 rel 变量,表示偏移量
然后 scale 是缩放比例(小数,类似 0.9 这种)
常量 SCALE 用来方便一些屏幕缩放不一样的用户,比如 125%,就让整个游戏的 x y width ehight都除以 SCALE

就是这些搞着搞着就搞糊涂了,还不写注释
小甲鱼最新课程 -> 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.

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