#python #pygame #sprite
Вопрос:
Я пытаюсь написать простую программу для моделирования системы N-Body с использованием python (и pygame для графического интерфейса только потому, что у меня больше опыта, чем у tk). Проблема, с которой я сталкиваюсь, заключается в том, что я создал класс тела, который является подклассом из спрайта. У меня есть адаптируемый размер списка n, который добавляет в список столько тел. Для начала я просто решил, что обновления будут выполняться в цикле double for с последним циклом for, который передает обновления после их вычисления (я знаю, что это неэффективно, не в этом дело). Моя проблема заключается в том, что, когда я передаю обновление элементу этого списка, обновление применяется ко всем элементам в этом списке. Что здесь происходит? Почему эти совершенно отдельные элементы связаны друг с другом? Необходимый код ниже
Класс кузова
class Body(Sprite):
vel = Vector2(0,0)
acc = Vector2(0,0)
def __init__(self, x, y, size, mass, init_val = True, color=(255,255,0)):
super().__init__()
self.rect = Rect(x-size[0]/2,y-size[1]/2,size[0],size[1])
self.mass = mass
self.pos = Vector2(x,y)
#self.vel = Vector2(random(),random())
if init_val:
self.acc = Vector2(random(),random())
self.image = Surface(size)
self.image.fill((255,255,255))
self.image.set_colorkey((255,255,255))
draw_circle(self.image,color,(size[0]/2,size[1]/2),size[0]/2)
def update(self, force):
self.acc = force
self.vel = self.acc
self.rect.move_ip(self.vel)
Цикл Sim-карты
class nbody:
def __init__(self, n = 3, screensize=(1366,768)):
self.win = pg.display.set_mode(screensize)
self.clock = pg.time.Clock()
self.bodies = Group()
sareax = [int(screensize[0]/4), int(screensize[0]*3/4)]
sareay = [int(screensize[1]/4), int(screensize[1]*3/4)]
c = [(255,0,0), (0,255,0), (0,0,255)]
self.bodies = []
for i in range(n):
mass = 5 random()*5
size = [mass*2]*2
self.bodies.append(Body(randint(sareax[0], sareax[1]),randint(sareay[0],sareay[1]),size,mass, init_val=False, color=c[i]))
def run_sim(self, time=None, fps = 0.5):
run = True
while run:
self.clock.tick(fps)
self.win.fill((0,0,0))
for e in pg.event.get():
if e.type == pg.QUIT:
pg.quit()
quit()
elif e.type == pg.KEYDOWN:
if e.key == pg.K_ESCAPE:
run = False
elif e.type == pg.KEYUP:
NotImplemented
updates = []
for b1,i in zip(self.bodies,range(len(self.bodies))):
df = Vector2(0,0)
for b2,j in zip(self.bodies,range(len(self.bodies))):
if i != j:
temp = newton(GRAV_CONSTANT, b1, b2)
mag = Vector2(b2.rect.centerx-b1.rect.centerx, b2.rect.centery-b1.rect.centery)
mag.scale_to_length(temp)
df = temp*mag
#print("Body {} has force {} to body {}".format(i,temp*mag,j))
if pg.sprite.collide_mask(b1,b2):
b1.kill()
b2.kill()
updates.append(df)
print(updates)
for bnum,up in zip(range(len(updates)),updates):
print("Before:", self.bodies[bnum].rect.center, self.bodies[bnum].vel, self.bodies[bnum].acc)
self.bodies[bnum].update(up)
print("Change:", up)
print("After:", self.bodies[bnum].rect.center, self.bodies[bnum].vel, self.bodies[bnum].acc)
pg.draw.line(self.win, (255,255,0), self.bodies[bnum].rect.center, (20*up.normalize()) self.bodies[bnum].rect.center, width=2)
self.win.blit(self.bodies[bnum].image,self.bodies[bnum].rect)
pg.display.flip()
return
Комментарии:
1.
vel
иacc
должны быть атрибутами экземпляра вместо атрибутов класса.
Ответ №1:
См. Раздел Переменные класса и экземпляра:
Вообще говоря, переменные экземпляра предназначены для данных, уникальных для каждого экземпляра, а переменные класса-для атрибутов и методов, общих для всех экземпляров класса.
Поскольку val
и acc
являются атрибутами класса, они являются общими для всех Body
объектов, и, как вы упомянули в своем вопросе: «эти совершенно отдельные элементы связаны вместе».
vel
и acc
должны быть атрибутом экземпляра вместо атрибутов класса:
class Body(Sprite):
def __init__(self, x, y, size, mass, init_val = True, color=(255,255,0)):
super().__init__()
self.rect = Rect(x-size[0]/2,y-size[1]/2,size[0],size[1])
self.mass = mass
self.pos = Vector2(x,y)
self.vel = Vector2(0,0)
self.acc = Vector2(0,0)
if init_val:
self.acc = Vector2(random(),random())
self.image = Surface(size)
self.image.fill((255,255,255))
self.image.set_colorkey((255,255,255))
draw_circle(self.image,color,(size[0]/2,size[1]/2),size[0]/2)
Комментарии:
1. Ах, я не знал, что атрибуты класса-это вещь. Решил, что это то же самое, что и c , где вы объявляете переменные там. Спасибо.