pygame天体运动模拟问题
本帖最后由 歌者文明清理员 于 2023-7-26 23:11 编辑目前已经放弃 SCALE
怎么会,居然把 pygame 写成了 pycharm
目前问题是不能将天体和轨道合并在同一个点上
cjjJasonchen 的回答:https://fishc.com.cn/forum.php?mod=redirect&goto=findpost&ptid=231284&pid=6297452
当用鼠标滚轮滑动的时候行星会变大或者变小,这时,他们的中心点会发生变化,
而此时,轨迹的生成位置还是在行星原本大小时的center,
所以看上去轨道就不和行星在一起了
我有点没懂,这么说不应该是轨道的曲线很诡异吗,再说我这个现象不是滚轮,是拖动时发生的
目前的想法:https://fishc.com.cn/forum.php?mod=redirect&goto=findpost&ptid=231284&pid=6298215
那可以不可以让StarSprite和轨迹都先放到另外一个surface上,对另外一个surface处理后再放回screen?
这样也有一个问题,就是轨迹的粗细不固定
但这个肯定不可行,首先这个 Surface 会超出屏幕
screen.blit(surf, rel)
因为 surf 也得有大小,不能无限大,所以就会出现过一会儿就黑屏的情况
求救,不然 Star Motion Simulate 将止步于此……
根据你提供的信息,你的问题是在pycharm中进行天体运动模拟时,无法将天体和轨道合并在同一个点上。
根据你提到的想法,将StarSprite和轨迹都放到另一个surface上,然后对该surface进行处理后再放回screen。但是这个方法是不可行的,因为这个surface的大小是有限的,可能会超出屏幕范围,导致黑屏的情况。
根据cjjJasonchen的回答,当用鼠标滚轮滑动时,行星的大小会改变,这时行星的中心点会发生变化,而轨道的生成位置仍然是在行星原本大小时的中心点上,所以看上去轨道和行星不在一起。
根据你的描述,这个现象是在拖动时发生的,而不是滚轮滑动。对于这个问题,我需要查看你的代码来进行更详细的分析和解答。请提供你的代码文件(code.zip)以便我可以帮助你解决问题。 很抱歉,由于我无法打开或访问链接中的内容,我无法准确理解你提到的问题和现象。但是,我可以为你提供一些关于天体运动模拟问题的一般性建议。
对于将天体和轨道合并在同一个点上的问题,你的想法是将StarSprite和轨迹放到另一个surface上进行处理,然后再放回screen。这个想法是可行的,可以使用一个临时的surface来进行处理,但需要注意处理后的surface大小不能超出屏幕。
关于轨迹的粗细不固定的问题,你可以尝试使用适当的绘图函数或技巧来实现轨迹的可变粗细。例如,你可以使用带有透明度的画笔,在每个时间步骤中根据行星的位置绘制轨迹,并根据距离行星中心的距离来调整透明度或线条的粗细,以达到变化的效果。
关于行星大小变化导致轨道生成位置的问题,你可以在每次行星大小发生变化时,同时更新轨道的生成位置。根据新的行星大小,重新计算轨道生成位置,确保轨道与行星始终保持一致。
总之,天体运动模拟问题涉及到多个方面的计算和图形处理,需要综合考虑各个因素来进行解决。如果你能提供更具体的代码或问题描述,我将能够给出更详细和准确的帮助。希望这些提示对你有所帮助,如果还有其他问题,请随时提问。
如果问题已经解决,请设置最佳答案 问题描述
根据你的描述,你在使用PyCharm进行天体运动模拟时遇到了问题。目前无法将天体和轨道合并在同一个点上。你考虑过将StarSprite和轨迹都放在一个新的surface上,然后对该surface进行处理后再放回主屏幕。但是这样会导致surface超出屏幕大小,并最终导致黑屏现象。此外,你提到鼠标滚轮滑动时行星的大小变化会影响到轨道的生成位置。
解决方案
1. 合并天体和轨道显示: 为了将天体和轨道合并在同一个点上,你可以将天体和轨道的绘制操作都放在同一个循环中,并将它们都绘制在同一个surface上,然后将该surface绘制到屏幕上。这样可以保证天体和轨道在视觉上的一致性。
2. 轨道粗细处理: 如果你希望轨道的粗细有所不同,可以使用Pygame的pygame.draw.lines()函数绘制轨道,该函数可以指定线条的宽度。你可以根据行星的大小或其他条件来动态调整轨道的粗细。如果你不希望轨道的粗细固定,可以在每次绘制轨道时计算轨道的粗细值,并将其作为参数传递给绘制函数。
3. 行星大小变化对轨道位置的影响: 当行星的大小发生变化时,它的中心点也会随之发生变化。这可能导致轨道生成位置与行星当前位置不一致。为了解决这个问题,你需要确保轨道的生成位置始终与行星的中心点保持一致。你可以根据行星的当前位置和大小重新计算轨道的生成位置,以确保它们始终在一起。
希望以上解决方案能够对你的问题有所帮助。如果你还有其他问题,请随时提问。
球一个最佳答案谢谢啦!这对我非常重要!{:10_254:}{:10_254:}{:10_254:}
isdkz 发表于 2023-7-26 22:33
根据你提供的信息,你的问题是在pycharm中进行天体运动模拟时,无法将天体和轨道合并在同一个点上。
根 ...
{:10_272:}为何不下载 code.zip 去问 GPT? 本帖最后由 hellomyprogram 于 2023-7-27 11:12 编辑
对于这种大型程序,你可以将代码拆分成不同板块来分别储存。
大功告成!以下是结果(代码部分):
import itertools
import os
import pygame
import pyini
import tkinter as tk
import tkinter.filedialog as fd
from time import sleep
from threading import Thread
pygame.init()
pygame.key.set_repeat(1000, 50)
G = 6
# why do you need to create another object?
class StarObject:
def __init__(self, x, y, vx, vy, mass):
self.x = x
self.y = y
self.vx = vx# v means speed
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)
super().__init__()
self.name = name
self.star = star# this is a StarObject
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.flush()
def flush(self):
self.rect.center = self.star.x, self.star.y
self.trail.append(self.rect.center)
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.flush(pos)
@property
def text(self):
return self._text
@text.setter
def text(self, text):
pos = self.rect.topright
self._text = text
self.flush(pos)
def flush(self, pos):
self.image = font.render(self._text, False, (255, 255, 255))
self.image = pygame.transform.scale_by(self.image, 1 / SCALE)
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 disappear_message(delay=5, interval=0.09):
sleep(delay)
while message.text:
try:
message.text = message.text[:-1]
except pygame.error:
return
sleep(interval)
def move(t):
global sprites
sprites_to_delete = set()
for sprite1, sprite2 in itertools.combinations(sprites, 2):# this is way faster
star1 = sprite1.star
star2 = sprite2.star
if star1 is star2:
continue
x1, y1, vx1, vy1, m1 = star1.info
x2, y2, _, _, m2 = star2.info
ax1, ay1 = 0, 0
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 = sprite2 if heavier is sprite1 else sprite1
sprites_to_delete.add(lighter)
heavier.star.vx += lighter.star.vx
heavier.star.vy += lighter.star.vy
message.text = language["star"]["collide"] % (heavier, lighter)
Thread(target=disappear_message).start()
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()
sprites -= sprites_to_delete# delete everything in sprites_to_delete
def is_collide(sprite1, sprite2):
r1, r2 = sprite1.radius, sprite2.radius
return r1 + r2 > get_distance(sprite1, sprite2)
def zoom(direction, each=0.01):
global scale
if direction > 0:
scale += each
elif direction < 0:
scale -= each
message.text = language["game"]["zoom"] % scale
Thread(target=disappear_message).start()
def change_view(move_x, move_y):
rel += move_x
rel += move_y
message.text = language["game"]["rel"] % str(tuple(rel))
with open("config/config.ini", "r", encoding="utf-8") as f:
config = pyini.ConfigParser(f.read())
with open(f"config/language_{config['language']['default']}.ini", "r", encoding="utf-8") as f:
language = pyini.ConfigParser(f.read())
SCALE = int(config["window"]["screen_zoom"]) / 100# what's this? is it different from "scale"?
clock = pygame.time.Clock()
drag = False
rel = # do you mean "the world origin on the screen"
scale = 1
paused = False
size = width, height = (1000 / SCALE, 1000 / SCALE)
screen = pygame.display.set_mode(size)
screen_original = pygame.Surface(size)
movement = 10
pygame.display.set_icon(pygame.image.load(config["window"]["icon"]))
pygame.display.set_caption("Pygame Star Motion Simulate")
with open(f"simulation/{config['simulation']['file']}.simulation", "r", encoding="utf-8") as f:
sprites = eval(f.read())# you can use sets rather than lists
font = pygame.font.SysFont("Microsoft YaHei UI", 20)
message = Message("", (width - 10, 10))
running = True
while running:
screen.fill((0, 0, 0))
clock.tick(30)
if not paused:
move(2)
for sprite, trail in map(lambda each_sprite: (each_sprite, each_sprite.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:
image = sprite.image
image = pygame.transform.scale(image, (sprite.radius * 2 * scale,) * 2)
screen.blit(image, ((sprite.rect.topleft + rel) * scale, (sprite.rect.topleft + rel) * scale))
try:
screen.blit(message.image, message.rect)
except pygame.error:
pass
pygame.display.flip()
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1:
drag = True
elif event.type == pygame.MOUSEBUTTONUP:
if event.button == 1:
drag = False
elif event.button == 4:
zoom(1)
else:
zoom(-1)
elif event.type == pygame.MOUSEMOTION:
if drag:
change_view(*event.rel)
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
paused = not paused
elif event.mod & pygame.KMOD_CTRL and event.key == pygame.K_s:
root = tk.Tk()
root.withdraw()
filepath = fd.asksaveasfilename(
initialdir=os.path.dirname(__file__),
defaultextension=".png",
filetypes=((language["game"]["picture"] % "PNG", "*.png"), )
)
if filepath:
pygame.image.save(screen, filepath)
root.destroy()
elif event.key in (pygame.K_LEFT, pygame.K_a, pygame.K_KP_4):
change_view(movement, 0)
elif event.key in (pygame.K_RIGHT, pygame.K_d, pygame.K_KP_6):
change_view(-movement, 0)
elif event.key in (pygame.K_UP, pygame.K_w, pygame.K_KP_8):
change_view(0, movement)
elif event.key in (pygame.K_DOWN, pygame.K_s, pygame.K_KP_2):
change_view(0, -movement)
elif event.key in (pygame.K_EQUALS, pygame.K_KP_PLUS):
zoom(1)
elif event.key in (pygame.K_MINUS, pygame.K_KP_MINUS):
zoom(-1)
pygame.quit()
存储部分要把.simulation的文件中的方括号改为大括号。以下是完整文件:
其实,问题出在这一行(第188行)上:
screen.blit(image, sprite.rect)
这样并没有改变星球的显示位置,只改变了大小,将这一行改为这样:
screen.blit(image, ((sprite.rect.topleft + rel) * scale, (sprite.rect.topleft + rel) * scale))
无关紧要的问题:
[*]你可以将StarObject与StarSprite整合在一起,分开来似乎没有什么用;
[*]你在进行缩小判断的时候只用了else语句,这样会匹配其他按键(例如鼠标中键和鼠标右键),你可以试试;
[*]把大写的SCALE改为SCREEN_SIZE_SCALE语义会明确一点;
[*]你可以将两个循环通过itertools.combination整合到一起(又不需要第三方库),一次性计算两个星球之间的关系,比用两个循环要快;
[*]set(集合)自带差集、并集和交集算法,可以使用set加快运行速度,减少代码量;
[*]对于加速度、速度以及力这些矢量,你可以创建一个向量(Vector)对象来存储并更改他们,加强可读性;
[*]你可以定时清理trail(星球轨迹),否则内存占用会一直增加,直到爆栈。
[*]像你这样的文件,可以使用git作为版本管理工具(不想开源可以不用github),在每一个版本附上这个版本的说明,如果出了什么bug,可以回退版本查看问题出在哪里。
hellomyprogram 发表于 2023-7-26 22:35
你可以试试用一个列表存储天体的位置,并使用pygame.draw.lines()方法将天体的位置信息画到屏幕上,你可能 ...
我的代码中为了让用户移动视角定义了 rel 变量,表示偏移量
然后 scale 是缩放比例(小数,类似 0.9 这种)
常量 SCALE 用来方便一些屏幕缩放不一样的用户,比如 125%,就让整个游戏的 x y width ehight都除以 SCALE
就是这些搞着搞着就搞糊涂了,还不写注释
页:
[1]