Pygame轨道模拟问题
Pygame orbit simulation issue
我最近一直在用这个等式制作轨道模拟器:
这是我的代码:
import pygame, math
from pygame.locals import *
from random import randint
pygame.init()
screen = pygame.display.set_mode([500,500])
clock = pygame.time.Clock()
class Planet():
def __init__(self, vel = [1, 1], mass = 100000, pos = [100, 100], pathLength = 100000):
self.v = vel
self.m = mass
self.size = mass/1000000
self.pos = pos
self.pL = pathLength
self.path = [[pos[0], pos[1]]]
def update(self):
self.pos[0] += self.v[0]
self.pos[1] += self.v[1]
self.path.append([self.pos[0], self.pos[1]])
if len(self.path) == self.pL:
self.path.pop(0)
class World():
def __init__(self, planetList, iterations, mass = 10000000, gravityConstant = (6 * 10 ** -9)):
self.plnt = planetList
self.iter = iterations
self.mass = mass
self.size = int(mass/1000000)
self.gC = gravityConstant
def draw(self):
pygame.draw.circle(screen, [0, 0, 0], [250, 250], self.size)
for p in self.plnt:
pygame.draw.rect(screen, [0, 0, 0], [p.pos[0], p.pos[1], p.size, p.size])
pygame.draw.lines(screen, [0, 0, 0], False, p.path)
def update(self):
for i in range(self.iter):
for p in self.plnt:
d = math.sqrt((p.pos[0] - 250) ** 2 + (p.pos[1] - 250) ** 2)
f = (self.gC * self.mass * p.m)/(d ** 2)
vect = [((250 - p.pos[0]) / d) * f, ((250 - p.pos[1]) / d) * f]
p.v[0] += vect[0]
p.v[1] += vect[1]
p.update()
self.draw()
a = Planet([4,0])
b = Planet([4, 0])
w = World([b], 100)
while 1:
screen.fill([255, 255, 255])
w.update()
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
pygame.display.update()
clock.tick(60)
如果我在模拟中只有 1 个行星,它会按预期工作,但它有问题
a = Planet([4,0])
b = Planet([4, 0])
w = World([a, b], 100)
行星从屏幕上飞出并永远继续下去,我看不出我哪里做错了。
您掉入了声明可变默认参数的 age-old Python 陷阱。 :)
为了让您的代码正常工作,切入正题,将我在下面所做的替换复制到您自己的代码中:
class Planet():
def __init__(self, vel = [1, 1], mass = 100000, pos = [100, 100], pathLength = 100000):
self.v = vel[:] # Added [:] to ensure the list is copied
self.m = mass
self.size = mass/1000000
self.pos = pos[:] # Added [:] here for the same reason
self.pL = pathLength
self.path = [[pos[0], pos[1]]]
说明
在 Python 中,列表是 可变的 - 您可以修改列表的同一实例。人们在使用 Python 时常犯的一个错误是在函数签名中将可变参数声明为默认值。
问题是 Python 会在处理函数定义时将默认值 一次 分配给参数,然后 重用 每次调用函数和调用默认参数时分配的值。
在您的 Planet
class 构造函数中,您声明了两个可变的默认参数:
vel = [1, 1]
pos = [100, 100]
您创建的每个 Planet
实例都将存储对这些列表的引用,但请注意,由于我上面所说的,每个星球都将共享 相同的 vel
列表和 相同 pos
列表。这意味着每个实例都会干扰其他实例的速度和位置数据。
您可以阅读有关此问题的更多信息 here。
处理这种情况的另一种首选方法是将默认值设置为 None
,然后在调用者未提供明确值的情况下分配 "real" 默认值它:
class Planet():
def __init__(self, vel = None, mass = 100000, pos = None, pathLength = 100000):
self.v = vel or [1, 1]
self.m = mass
self.size = mass/1000000
self.pos = pos or [100, 100]
self.pL = pathLength
self.path = [[self.pos[0], self.pos[1]]]
然后您需要记录该函数的这种行为,否则调用者将看不到它。
我最近一直在用这个等式制作轨道模拟器:
这是我的代码:
import pygame, math
from pygame.locals import *
from random import randint
pygame.init()
screen = pygame.display.set_mode([500,500])
clock = pygame.time.Clock()
class Planet():
def __init__(self, vel = [1, 1], mass = 100000, pos = [100, 100], pathLength = 100000):
self.v = vel
self.m = mass
self.size = mass/1000000
self.pos = pos
self.pL = pathLength
self.path = [[pos[0], pos[1]]]
def update(self):
self.pos[0] += self.v[0]
self.pos[1] += self.v[1]
self.path.append([self.pos[0], self.pos[1]])
if len(self.path) == self.pL:
self.path.pop(0)
class World():
def __init__(self, planetList, iterations, mass = 10000000, gravityConstant = (6 * 10 ** -9)):
self.plnt = planetList
self.iter = iterations
self.mass = mass
self.size = int(mass/1000000)
self.gC = gravityConstant
def draw(self):
pygame.draw.circle(screen, [0, 0, 0], [250, 250], self.size)
for p in self.plnt:
pygame.draw.rect(screen, [0, 0, 0], [p.pos[0], p.pos[1], p.size, p.size])
pygame.draw.lines(screen, [0, 0, 0], False, p.path)
def update(self):
for i in range(self.iter):
for p in self.plnt:
d = math.sqrt((p.pos[0] - 250) ** 2 + (p.pos[1] - 250) ** 2)
f = (self.gC * self.mass * p.m)/(d ** 2)
vect = [((250 - p.pos[0]) / d) * f, ((250 - p.pos[1]) / d) * f]
p.v[0] += vect[0]
p.v[1] += vect[1]
p.update()
self.draw()
a = Planet([4,0])
b = Planet([4, 0])
w = World([b], 100)
while 1:
screen.fill([255, 255, 255])
w.update()
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
pygame.display.update()
clock.tick(60)
如果我在模拟中只有 1 个行星,它会按预期工作,但它有问题
a = Planet([4,0])
b = Planet([4, 0])
w = World([a, b], 100)
行星从屏幕上飞出并永远继续下去,我看不出我哪里做错了。
您掉入了声明可变默认参数的 age-old Python 陷阱。 :)
为了让您的代码正常工作,切入正题,将我在下面所做的替换复制到您自己的代码中:
class Planet():
def __init__(self, vel = [1, 1], mass = 100000, pos = [100, 100], pathLength = 100000):
self.v = vel[:] # Added [:] to ensure the list is copied
self.m = mass
self.size = mass/1000000
self.pos = pos[:] # Added [:] here for the same reason
self.pL = pathLength
self.path = [[pos[0], pos[1]]]
说明
在 Python 中,列表是 可变的 - 您可以修改列表的同一实例。人们在使用 Python 时常犯的一个错误是在函数签名中将可变参数声明为默认值。
问题是 Python 会在处理函数定义时将默认值 一次 分配给参数,然后 重用 每次调用函数和调用默认参数时分配的值。
在您的 Planet
class 构造函数中,您声明了两个可变的默认参数:
vel = [1, 1]
pos = [100, 100]
您创建的每个 Planet
实例都将存储对这些列表的引用,但请注意,由于我上面所说的,每个星球都将共享 相同的 vel
列表和 相同 pos
列表。这意味着每个实例都会干扰其他实例的速度和位置数据。
您可以阅读有关此问题的更多信息 here。
处理这种情况的另一种首选方法是将默认值设置为 None
,然后在调用者未提供明确值的情况下分配 "real" 默认值它:
class Planet():
def __init__(self, vel = None, mass = 100000, pos = None, pathLength = 100000):
self.v = vel or [1, 1]
self.m = mass
self.size = mass/1000000
self.pos = pos or [100, 100]
self.pL = pathLength
self.path = [[self.pos[0], self.pos[1]]]
然后您需要记录该函数的这种行为,否则调用者将看不到它。